001 /*--------------------------------------------------------------------------+
002 $Id: SimulinkBlock.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.model;
019
020 import static edu.tum.cs.simulink.model.SimulinkConstants.PARAM_BlockType;
021 import static edu.tum.cs.simulink.model.SimulinkConstants.PARAM_SourceType;
022 import static edu.tum.cs.simulink.model.SimulinkConstants.TYPE_Reference;
023
024 import java.util.ArrayList;
025 import java.util.HashMap;
026 import java.util.List;
027 import java.util.Set;
028
029 import edu.tum.cs.commons.assertion.CCSMAssert;
030 import edu.tum.cs.commons.assertion.CCSMPre;
031 import edu.tum.cs.commons.assertion.PreconditionException;
032 import edu.tum.cs.commons.clone.DeepCloneException;
033 import edu.tum.cs.commons.collections.CollectionUtils;
034 import edu.tum.cs.commons.collections.IdentityHashSet;
035 import edu.tum.cs.commons.collections.UnmodifiableCollection;
036 import edu.tum.cs.commons.collections.UnmodifiableSet;
037 import edu.tum.cs.simulink.util.SimulinkUtils;
038
039 /**
040 * A Simulink block has a type and maintains a parameter map, a list of sub
041 * blocks, a list of annotations and in/out-ports.
042 *
043 * @author hummelb
044 * @author $Author: juergens $
045 * @version $Rev: 26277 $
046 * @levd.rating GREEN Hash: 5DDE14945CA1B08AC406000C88246E18
047 */
048 public class SimulinkBlock extends SimulinkElementBase {
049
050 /** The subBlocks of this block indexed by name. */
051 private final HashMap<String, SimulinkBlock> subBlocks = new HashMap<String, SimulinkBlock>();
052
053 /** Inports of this block indexed by port index. */
054 private final HashMap<String, SimulinkInPort> inPorts = new HashMap<String, SimulinkInPort>();
055
056 /** Outports of this block indexed by port index. */
057 private final HashMap<String, SimulinkOutPort> outPorts = new HashMap<String, SimulinkOutPort>();
058
059 /** Annotations of this block. */
060 private final IdentityHashSet<SimulinkAnnotation> annotations = new IdentityHashSet<SimulinkAnnotation>();
061
062 /** Create new Simulink block. */
063 public SimulinkBlock() {
064 super();
065 }
066
067 /**
068 * Copy constructor. This is used from the {@link SimulinkModel} during
069 * cloning.
070 */
071 protected SimulinkBlock(SimulinkBlock origBlock) throws DeepCloneException {
072 super(origBlock);
073
074 for (SimulinkInPort inPort : origBlock.inPorts.values()) {
075 new SimulinkInPort(this, inPort.getIndex());
076 }
077
078 for (SimulinkOutPort outPort : origBlock.outPorts.values()) {
079 new SimulinkOutPort(this, outPort.getIndex());
080 }
081
082 for (SimulinkAnnotation annotation : origBlock.annotations) {
083 addAnnotation(annotation.deepClone());
084 }
085
086 // Recursively deep clone sub blocks
087 for (SimulinkBlock subBlock : origBlock.subBlocks.values()) {
088 addSubBlock(subBlock.deepClone());
089 }
090
091 cloneLines(origBlock);
092 }
093
094 /** Add an annotation. */
095 public void addAnnotation(SimulinkAnnotation annotation) {
096 annotations.add(annotation);
097 annotation.setParent(this);
098 }
099
100 /** Adds a sub block. */
101 public void addSubBlock(SimulinkBlock subBlock) {
102 CCSMPre.isTrue(subBlock.getParent() == null,
103 "May not add block which already has a parent!");
104 subBlock.setParent(this);
105
106 CCSMPre.isFalse(subBlocks.containsKey(subBlock.getName()),
107 "Block already has a sub block called: " + subBlock.getName());
108 subBlocks.put(subBlock.getName(), subBlock);
109 }
110
111 /** Get annotations. */
112 public UnmodifiableSet<SimulinkAnnotation> getAnnotations() {
113 return CollectionUtils.asUnmodifiable(annotations);
114 }
115
116 /**
117 * Get all incoming lines of this block.
118 */
119 public List<SimulinkLine> getInLines() {
120 ArrayList<SimulinkLine> inLines = new ArrayList<SimulinkLine>();
121
122 for (SimulinkInPort inPort : inPorts.values()) {
123 if (inPort.getLine() != null) {
124 inLines.add(inPort.getLine());
125 }
126 }
127 return inLines;
128 }
129
130 /**
131 * Get inport by index or <code>null</code> if no inport with this index was
132 * found.
133 */
134 public SimulinkInPort getInPort(String portIndex) {
135 return inPorts.get(portIndex);
136 }
137
138 /** Returns the inports this block. */
139 public UnmodifiableCollection<SimulinkInPort> getInPorts() {
140 return CollectionUtils.asUnmodifiable(inPorts.values());
141 }
142
143 /**
144 * Get all outgoing lines of this block.
145 */
146 public List<SimulinkLine> getOutLines() {
147 ArrayList<SimulinkLine> outLines = new ArrayList<SimulinkLine>();
148
149 for (SimulinkOutPort outPort : outPorts.values()) {
150 outLines.addAll(outPort.getLines());
151 }
152 return outLines;
153 }
154
155 /**
156 * Get outport by index or <code>null</code> if no outport with this index
157 * was found.
158 */
159 public SimulinkOutPort getOutPort(String portIndex) {
160 return outPorts.get(portIndex);
161 }
162
163 /** Returns the outport of this block. */
164 public UnmodifiableCollection<SimulinkOutPort> getOutPorts() {
165 return CollectionUtils.asUnmodifiable(outPorts.values());
166 }
167
168 /**
169 * If this block is of type 'Reference' this returns
170 * <code>Reference.<source type of the reference></code>. Otherwise
171 * this just returns the type of the block.
172 */
173 public String getResolvedType() {
174 String type = getType();
175 if (TYPE_Reference.equals(type)) {
176 String sourceBlock = getParameter(PARAM_SourceType);
177 if (sourceBlock == null) {
178 return type;
179 }
180 return TYPE_Reference + "." + sourceBlock;
181 }
182 return type;
183 }
184
185 /**
186 * Get named sub block or <code>null</code> if no sub block with the given
187 * name is present.
188 */
189 public SimulinkBlock getSubBlock(String name) {
190 return subBlocks.get(name);
191 }
192
193 /** Returns the sub blocks of this block. */
194 public UnmodifiableCollection<SimulinkBlock> getSubBlocks() {
195 return CollectionUtils.asUnmodifiable(subBlocks.values());
196 }
197
198 /** Returns the type. */
199 public String getType() {
200 // We have to access the super class here as we do not want any defaults
201 // (infinite recursion!)
202 return getDeclaredParameter(PARAM_BlockType);
203 }
204
205 /** Returns whether this block has subBlocks. */
206 public boolean hasSubBlocks() {
207 return !subBlocks.isEmpty();
208 }
209
210 /** Unlinks this object from the simulink tree. */
211 @Override
212 public void remove() {
213
214 for (SimulinkBlock subBlock : new ArrayList<SimulinkBlock>(subBlocks
215 .values())) {
216 subBlock.remove();
217 }
218
219 for (SimulinkOutPort outPort : new ArrayList<SimulinkOutPort>(
220 getOutPorts())) {
221 outPort.remove();
222 }
223
224 for (SimulinkInPort inPort : new ArrayList<SimulinkInPort>(getInPorts())) {
225 inPort.remove();
226 }
227
228 for (SimulinkAnnotation annotation : new ArrayList<SimulinkAnnotation>(
229 annotations)) {
230 annotation.remove();
231 }
232
233 super.remove();
234 }
235
236 /** Get string representation of this block. */
237 @Override
238 public String toString() {
239 return getId() + " [" + getType() + ", " + inPorts.size() + ":"
240 + outPorts.size() + "]";
241 }
242
243 /**
244 * Creates a deep clone of this block. Please note that is possible to clone
245 * a single block but the resulting block will behave not properly as it
246 * does not belong to {@link SimulinkModel}. Therefore it is strongly
247 * recommended to deep clone only whole models.
248 */
249 public SimulinkBlock deepClone() throws DeepCloneException {
250 return new SimulinkBlock(this);
251 }
252
253 /**
254 * Add a inport to this block.
255 *
256 * @throws PreconditionException
257 * if the port does not belong to this block or a port with the
258 * same index was defined before.
259 */
260 /* package */void addInPort(SimulinkInPort inPort)
261 throws IllegalArgumentException {
262 CCSMPre.isTrue(inPort.getBlock() == this,
263 "Port does not belong to block.");
264 CCSMPre.isFalse(inPorts.containsKey(inPort.getIndex()),
265 "Port with index " + inPort.getIndex() + " already defined.");
266
267 inPorts.put(inPort.getIndex(), inPort);
268 }
269
270 /**
271 * Add a outport to this block.
272 *
273 * @throws PreconditionException
274 * if the port does not belong to this block or a port with the
275 * same index was defined before.
276 */
277 /* package */void addOutPort(SimulinkOutPort outPort)
278 throws IllegalArgumentException {
279 CCSMPre.isTrue(outPort.getBlock() == this,
280 "Port does not belong to block.");
281 CCSMPre.isFalse(outPorts.containsKey(outPort.getIndex()),
282 "Port with index " + outPort.getIndex() + " already defined.");
283
284 outPorts.put(outPort.getIndex(), outPort);
285 }
286
287 /**
288 * Clone all lines contained in the given block or one of its descendant
289 * blocks. This is usually called by {@link #deepClone()} after copying the
290 * block using the copy constructor. The lines cloned is the maximal set of
291 * lines which could be cloned without connecting to some parent.
292 *
293 * @param origBlock
294 * the original block.
295 */
296 private void cloneLines(SimulinkBlock origBlock) {
297
298 List<SimulinkLine> lines = new ArrayList<SimulinkLine>();
299 origBlock.collectLines(lines);
300
301 for (SimulinkLine line : lines) {
302 SimulinkBlock srcSub = origBlock.getAncestralChild(line
303 .getSrcPort().getBlock());
304 SimulinkBlock dstSub = origBlock.getAncestralChild(line
305 .getDstPort().getBlock());
306
307 if (srcSub == null || dstSub == null) {
308 // not a line between children
309 continue;
310 }
311
312 if (srcSub != origBlock && srcSub == dstSub) {
313 // has already been cloned when cloning srcSub
314 continue;
315 }
316
317 cloneLine(line, origBlock);
318 }
319 }
320
321 /**
322 * Get block default parameter.
323 */
324 @Override
325 /* package */String getDefaultParameter(String name) {
326 return getModel().getTypeBlockDefaultParameter(getType(), name);
327 }
328
329 /**
330 * Get block default parameter names.
331 */
332 @Override
333 /* package */Set<String> getDefaultParameterNames() {
334 return getModel().getBlockDefaultParameterNames(getType());
335 }
336
337 /** Removes the given element. */
338 /* package */void removeElement(SimulinkElementBase element) {
339 if (element instanceof SimulinkAnnotation) {
340 annotations.remove(element);
341 } else if (element instanceof SimulinkBlock) {
342 subBlocks.remove(element.getName());
343 } else {
344 CCSMAssert.fail(element.getClass().getName()
345 + " is a unknown sub class of "
346 + SimulinkElementBase.class.getName());
347 }
348 }
349
350 /** Remove in port. */
351 /* package */void removeInPort(SimulinkInPort inPort) {
352 CCSMPre.isTrue(inPorts.containsValue(inPort),
353 "Port does not belong to this block!");
354 inPorts.remove(inPort.getIndex());
355 }
356
357 /** Remove out port. */
358 /* package */void removeOutPort(SimulinkOutPort outPort) {
359 CCSMPre.isTrue(outPorts.containsValue(outPort),
360 "Port does not belong to this block!");
361 outPorts.remove(outPort.getIndex());
362 }
363
364 /**
365 * Clone a single line.
366 *
367 * @param origLine
368 * the line to clone.
369 */
370 private void cloneLine(SimulinkLine origLine, SimulinkBlock origBlock) {
371 SimulinkOutPort origSrcPort = origLine.getSrcPort();
372 SimulinkInPort origDstPort = origLine.getDstPort();
373
374 SimulinkBlock cloneSrcBlock = resolveRelativeBlock(origSrcPort
375 .getBlock(), origBlock);
376
377 CCSMAssert.isFalse(cloneSrcBlock == null, "Cloning Problem: Src block "
378 + origSrcPort.getBlock().getName() + " not found.");
379
380 SimulinkBlock cloneDstBlock = resolveRelativeBlock(origDstPort
381 .getBlock(), origBlock);
382 CCSMAssert.isFalse(cloneDstBlock == null, "Cloning Problem: Dst block "
383 + origDstPort.getBlock().getName() + " not found.");
384
385 @SuppressWarnings("null")
386 SimulinkOutPort cloneSrcPort = cloneSrcBlock.getOutPort(origSrcPort
387 .getIndex());
388 CCSMAssert.isFalse(cloneSrcPort == null,
389 "Cloning Problem: Src port with index "
390 + origSrcPort.getIndex() + " not found.");
391
392 @SuppressWarnings("null")
393 SimulinkInPort cloneDstPort = cloneDstBlock.getInPort(origDstPort
394 .getIndex());
395 CCSMAssert.isFalse(cloneDstPort == null,
396 "Cloning Problem: Dst port with index "
397 + origDstPort.getIndex() + " not found.");
398
399 // clone line
400 SimulinkLine line = new SimulinkLine(cloneSrcPort, cloneDstPort);
401 SimulinkUtils.copyParameters(origLine, line);
402 }
403
404 /**
405 * Fills the given list with all lines contained in this block or one of its
406 * descendant blocks.
407 */
408 private void collectLines(List<SimulinkLine> lines) {
409 lines.addAll(getOutLines());
410 for (SimulinkBlock sub : getSubBlocks()) {
411 sub.collectLines(lines);
412 }
413 }
414
415 /**
416 * Returns the sub block, which has the given block as a descendant (i.e.
417 * direct or indirect sub block). If the block is a sub block of
418 * <code>this</code>, the sub block is returned. If the block is
419 * <code>this</code>, then <code>this</code> is returned. If the given block
420 * is not a descendant, <code>null</code> is returned.
421 */
422 private SimulinkBlock getAncestralChild(SimulinkBlock block) {
423 CCSMPre.isFalse(block == null, "Block may not be null");
424 if (block == this) {
425 return this;
426 }
427 while (block != null) {
428 if (block.getParent() == this) {
429 return block;
430 }
431 block = block.getParent();
432 }
433
434 // not a descendant
435 return null;
436 }
437
438 /**
439 * Returns the block that is in the same relation to this block as is
440 * <code>block</code> to <code>root</code>. This may only be called if
441 * <code>block</code> is a descendant of <code>root</code> and such a
442 * relative block actually exists. Otherwise assertion exceptions are
443 * thrown.
444 */
445 @SuppressWarnings("null")
446 private SimulinkBlock resolveRelativeBlock(SimulinkBlock block,
447 SimulinkBlock root) {
448 CCSMAssert.isFalse(block == null,
449 "Block must be a descendant of root block!");
450 if (block == root) {
451 return this;
452 }
453
454 SimulinkBlock resultParent = resolveRelativeBlock(block.getParent(),
455 root);
456 CCSMAssert
457 .isFalse(resultParent == null, "Parent block does not exist.");
458
459 return resultParent.getSubBlock(block.getName());
460 }
461 }