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.&lt;source type of the reference&gt;</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    }