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 }