001 /*--------------------------------------------------------------------------+
002 $Id: SimulinkUtils.java 26277 2010-02-18 10:46:58Z juergens $
003 | |
004 | Copyright 2005-2010 Technische Universitaet Muenchen |
005 | |
006 | Licensed under the Apache License, Version 2.0 (the "License"); |
007 | you may not use this file except in compliance with the License. |
008 | You may obtain a copy of the License at |
009 | |
010 | http://www.apache.org/licenses/LICENSE-2.0 |
011 | |
012 | Unless required by applicable law or agreed to in writing, software |
013 | distributed under the License is distributed on an "AS IS" BASIS, |
014 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
015 | See the License for the specific language governing permissions and |
016 | limitations under the License. |
017 +--------------------------------------------------------------------------*/
018 package edu.tum.cs.simulink.util;
019
020 import java.util.ArrayList;
021 import java.util.Collection;
022 import java.util.HashMap;
023 import java.util.Iterator;
024 import java.util.List;
025 import java.util.Map;
026 import java.util.Set;
027 import java.util.regex.Matcher;
028 import java.util.regex.Pattern;
029
030 import edu.tum.cs.commons.assertion.CCSMAssert;
031 import edu.tum.cs.commons.assertion.CCSMPre;
032 import edu.tum.cs.commons.assertion.PreconditionException;
033 import edu.tum.cs.commons.collections.IdentityHashSet;
034 import edu.tum.cs.commons.error.NeverThrownRuntimeException;
035 import edu.tum.cs.commons.string.StringUtils;
036 import edu.tum.cs.commons.visitor.IVisitor;
037 import edu.tum.cs.simulink.model.ParameterizedElement;
038 import edu.tum.cs.simulink.model.SimulinkBlock;
039 import edu.tum.cs.simulink.model.SimulinkConstants;
040 import edu.tum.cs.simulink.model.SimulinkInPort;
041 import edu.tum.cs.simulink.model.SimulinkModel;
042 import edu.tum.cs.simulink.model.SimulinkOutPort;
043 import edu.tum.cs.simulink.model.stateflow.IStateflowElement;
044 import edu.tum.cs.simulink.model.stateflow.IStateflowNodeContainer;
045 import edu.tum.cs.simulink.model.stateflow.StateflowBlock;
046 import edu.tum.cs.simulink.model.stateflow.StateflowChart;
047 import edu.tum.cs.simulink.model.stateflow.StateflowMachine;
048 import edu.tum.cs.simulink.model.stateflow.StateflowNodeBase;
049 import edu.tum.cs.simulink.model.stateflow.StateflowState;
050 import edu.tum.cs.simulink.model.stateflow.StateflowTarget;
051
052 /**
053 * Collection of utility methods for Simulink models.
054 *
055 * @author deissenb
056 * @author $Author: juergens $
057 * @version $Rev: 26277 $
058 * @levd.rating GREEN Hash: 0D8577FED291C7F4F560DB259060F6F4
059 */
060 public class SimulinkUtils {
061
062 /** Visitor that stores all blocks in a id->block map. */
063 private static class MapVisitor implements
064 IVisitor<SimulinkBlock, NeverThrownRuntimeException> {
065 /** Maps from block id to block. */
066 private final HashMap<String, SimulinkBlock> map = new HashMap<String, SimulinkBlock>();
067
068 /** Visit block */
069 public void visit(SimulinkBlock block) {
070 map.put(block.getId(), block);
071 }
072 }
073
074 /** Copy parameters from one parameterized element to another. */
075 public static void copyParameters(ParameterizedElement source,
076 ParameterizedElement target) {
077 for (String name : source.getParameterNames()) {
078 target.setParameter(name, source.getParameter(name));
079 }
080 }
081
082 /** Create map that maps from id to block. */
083 public static Map<String, SimulinkBlock> createIdToNodeMap(
084 SimulinkBlock block) {
085 MapVisitor visitor = new MapVisitor();
086 visitDepthFirst(block, visitor);
087 return visitor.map;
088 }
089
090 /** Replaces forward slashes by double forward slashes. */
091 public static String escape(String string) {
092 return string.replace("/", "//");
093 }
094
095 /**
096 * Get Simulink array parameter as array. This raises a
097 * {@link NumberFormatException} if the elements of the array are not
098 * integers.
099 */
100 public static int[] getIntParameterArray(String parameter) {
101 String[] parts = getStringParameterArray(parameter);
102 int[] result = new int[parts.length];
103 for (int i = 0; i < result.length; i++) {
104 result[i] = Integer.parseInt(parts[i]);
105 }
106 return result;
107 }
108
109 /** Get Simulink array parameter as array. */
110 public static String[] getStringParameterArray(String parameter) {
111 // remove brackets
112 String content = parameter.substring(1, parameter.length() - 1);
113 if (StringUtils.isEmpty(content)) {
114 return new String[0];
115 }
116 return content.split("[,;] *");
117 }
118
119 /** Checks if a block is a target link block. */
120 public static boolean isTargetlinkBlock(SimulinkBlock node) {
121 return node.getType().equals(SimulinkConstants.TYPE_Reference)
122 && node.getParameter(SimulinkConstants.PARAM_SourceType)
123 .startsWith("TL_");
124 }
125
126 /** Split full qualified identifier. */
127 public static List<String> splitSimulinkId(String string) {
128 ArrayList<String> result = new ArrayList<String>();
129
130 // Simulink names cannot start or end with a slash
131 Pattern pattern = Pattern.compile("[^/]/[^/]");
132 Matcher matcher = pattern.matcher(string);
133
134 int begin = 0;
135 while (matcher.find(begin)) {
136 result.add(removeEscapes(string.substring(begin,
137 matcher.start() + 1)));
138 // pattern is one character longer than the slash
139 begin = matcher.end() - 1;
140 }
141 result.add(removeEscapes(string.substring(begin)));
142
143 return result;
144 }
145
146 /**
147 * Create Simulink id from a iteration of names. This takes care of proper
148 * escaping.
149 *
150 * @throws PreconditionException
151 * if one of names starts or ends with a slash
152 */
153 public static String createSimulinkId(Iterable<String> names) {
154 StringBuilder result = new StringBuilder();
155 Iterator<String> it = names.iterator();
156 while (it.hasNext()) {
157 String name = it.next();
158 CCSMPre.isFalse(name.startsWith("/") || name.endsWith("/"),
159 "Simulink names cannot start or end with a slash.");
160 result.append(escape(name));
161 if (it.hasNext()) {
162 result.append("/");
163 }
164 }
165 return result.toString();
166 }
167
168 /**
169 * Visit blocks in a depth first manner.
170 *
171 * @param <X>
172 * Type of exception thrown by the visitor.
173 * @param block
174 * block to start with
175 * @param visitor
176 * the visitor
177 * @throws X
178 * exception thrown by the visitor.
179 */
180 public static <X extends Exception> void visitDepthFirst(
181 SimulinkBlock block, IVisitor<SimulinkBlock, X> visitor) throws X {
182 visitor.visit(block);
183 if (!block.hasSubBlocks()) {
184 return;
185 }
186 for (SimulinkBlock child : block.getSubBlocks()) {
187 visitDepthFirst(child, visitor);
188 }
189 }
190
191 /** Replace double forward slashes by single forward slashes */
192 private static String removeEscapes(String name) {
193 return name.replace("//", "/");
194 }
195
196 /** Returns all recursively reachable subblocks of the given block. */
197 public static List<SimulinkBlock> listBlocksDepthFirst(SimulinkBlock block) {
198 final List<SimulinkBlock> result = new ArrayList<SimulinkBlock>();
199 SimulinkUtils.visitDepthFirst(block,
200 new IVisitor<SimulinkBlock, NeverThrownRuntimeException>() {
201 public void visit(SimulinkBlock block) {
202 result.add(block);
203 }
204 });
205 return result;
206 }
207
208 /**
209 * Calculate the set of all parent blocks up to the model for the given
210 * blocks.
211 */
212 public static Set<SimulinkBlock> calculateParentSet(
213 Collection<SimulinkBlock> blocks) {
214
215 Set<SimulinkBlock> parents = new IdentityHashSet<SimulinkBlock>();
216 if (blocks.isEmpty()) {
217 return parents;
218 }
219
220 for (SimulinkBlock block : blocks) {
221 SimulinkModel model = block.getModel();
222 while (block != model) {
223 parents.add(block);
224 block = block.getParent();
225 }
226 }
227
228 return parents;
229 }
230
231 /** Recursively count sub blocks. */
232 public static int countSubBlocks(SimulinkBlock block) {
233 BlockCounter counter = new BlockCounter();
234 SimulinkUtils.visitDepthFirst(block, counter);
235 // minus the root block
236 return counter.blockCount - 1;
237 }
238
239 /** Recursively count lines. */
240 public static int countLines(SimulinkBlock block) {
241 BlockCounter counter = new BlockCounter();
242 for (SimulinkBlock child : block.getSubBlocks()) {
243 SimulinkUtils.visitDepthFirst(child, counter);
244 }
245 return counter.lineCount;
246 }
247
248 /** Recursively count Stateflow states. */
249 public static int countStates(IStateflowNodeContainer<?> node) {
250 int count = 0;
251 if (node instanceof StateflowState) {
252 count = 1;
253 } else {
254 count = 0;
255 }
256
257 for (StateflowNodeBase element : node.getNodes()) {
258 if (element instanceof IStateflowNodeContainer<?>) {
259 count += countStates((IStateflowNodeContainer<?>) element);
260 }
261 }
262 return count;
263 }
264
265 /** Count states of all charts of the machine. */
266 public static int countStates(StateflowMachine stateflowMachine) {
267 int stateCount = 0;
268 for (StateflowChart chart : stateflowMachine.getCharts()) {
269 stateCount += countStates(chart);
270 }
271 return stateCount;
272 }
273
274 /**
275 * Get the Stateflow chart a Stateflow element belongs to.
276 *
277 * @return the Stateflow chart or <code>null</code> if the element is
278 * unconnected or not associated with a chart, e.g.
279 * {@link StateflowTarget}.
280 */
281 public static StateflowChart getChart(IStateflowElement<?> element) {
282 if (element instanceof StateflowChart) {
283 return (StateflowChart) element;
284 }
285 IStateflowElement<?> parent = element.getParent();
286 if (parent == null) {
287 return null;
288 }
289 return getChart(parent);
290 }
291
292 /**
293 * Get the Stateflow block a Stateflow element belongs to.
294 *
295 * @return the Stateflow block or <code>null</code> if the element is
296 * unconnected or not associated with a chart, e.g.
297 * {@link StateflowTarget}.
298 */
299 public static StateflowBlock getBlock(IStateflowElement<?> element) {
300 StateflowChart chart = getChart(element);
301 if (chart == null) {
302 return null;
303 }
304 return chart.getStateflowBlock();
305 }
306
307 /**
308 * Get name of a Stateflow state as defined in the Stateflow manual. As
309 * Stateflow awkwardly stores the names as part of the label, this is put in
310 * a utility methods and not directly at class {@link StateflowState}.
311 */
312 public static String getStateName(StateflowState state) {
313 String label = state.getLabel();
314 if (StringUtils.isEmpty(label)) {
315 return null;
316 }
317 String name = label.split("\\\\n")[0];
318
319 // State names MAY end with a slash
320 if (name.length() > 1 && name.endsWith("/")) {
321 name = name.substring(0, name.length() - 1);
322 }
323 return name;
324 }
325
326 /**
327 * Get full qualified state name. This is deliberately not part of class
328 * {@link StateflowState} as names of Stateflow derives names from the state
329 * labels.
330 */
331 public static String getFQStateName(StateflowState state) {
332 String name = getStateName(state);
333 IStateflowNodeContainer<?> parent = state.getParent();
334 if (parent == null) {
335 return name;
336 }
337 if (parent instanceof StateflowChart) {
338 StateflowChart chart = (StateflowChart) parent;
339 return chart.getStateflowBlock().getId() + "/" + name;
340 }
341
342 // Can be only a state
343 return getFQStateName((StateflowState) parent) + "." + name;
344 }
345
346 /**
347 * Obtain out port block that is below the a Stateflow block and describes
348 * the output of a Stateflow chart.
349 *
350 * What Simulink displays like an atomic Stateflow chart is internally
351 * represented as a Simulink sub system that itself contains multiple
352 * blocks. The sub system itself has a normal inport/outport which has only
353 * a number (as (almost) all ports of Simulink sub systems do). However the
354 * sub system contains a block of <b>type</b> Inport/Outport (quite
355 * confusing...) and this is the one the carries the name of the Stateflow
356 * output. Note that this is related to a past CR described at
357 * <https://bugzilla.informatik.tu-muenchen.de/show_bug.cgi?id=1502>.
358 *
359 * The code is the following:
360 * <ul>
361 * <li>iterate over all child blocks of the sub system that represents the
362 * Stateflow chart
363 * <li>pick the one that is of type Inport/Outport and that has the same
364 * port index as the inport/outport of the sub system (the index defines the
365 * mapping between the Inport/Ouport block and the actual inport/outport)
366 * </ul>
367 */
368 public static SimulinkBlock getStateflowOutport(SimulinkOutPort outPort) {
369 CCSMPre.isInstanceOf(outPort.getBlock(), StateflowBlock.class);
370 SimulinkBlock result = null;
371 for (SimulinkBlock block : outPort.getBlock().getSubBlocks()) {
372 if (SimulinkConstants.TYPE_Outport.equals(block.getType())
373 && block.getParameter(SimulinkConstants.PARAM_Port).equals(
374 outPort.getIndex())) {
375 CCSMAssert.isTrue(result == null,
376 "We assummed that there is only one matching port.");
377 result = block;
378 }
379 }
380 return result;
381 }
382
383 /**
384 * Obtain in port. See {@link #getStateflowOutport(SimulinkOutPort)} for
385 * details.
386 */
387 public static SimulinkBlock getStateflowInport(SimulinkInPort inPort) {
388 CCSMPre.isInstanceOf(inPort.getBlock(), StateflowBlock.class);
389 SimulinkBlock result = null;
390 for (SimulinkBlock block : inPort.getBlock().getSubBlocks()) {
391 if (SimulinkConstants.TYPE_Inport.equals(block.getType())
392 && block.getParameter(SimulinkConstants.PARAM_Port).equals(
393 inPort.getIndex())) {
394 CCSMAssert.isTrue(result == null,
395 "We assummed that there is only one matching port.");
396 result = block;
397 }
398 }
399 return result;
400 }
401
402 /** Visitor for counting sub blocks. */
403 private static class BlockCounter implements
404 IVisitor<SimulinkBlock, NeverThrownRuntimeException> {
405 /** Counter for blocks. */
406 private int blockCount = 0;
407
408 /** Counter for lines. */
409 private int lineCount = 0;
410
411 /** Count block. */
412 public void visit(SimulinkBlock element) {
413 blockCount++;
414 lineCount += element.getOutLines().size();
415 }
416
417 }
418 }