001/* A CompositeEntity is a cluster in a clustered graph. 002 003 Copyright (c) 1997-2014 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 */ 028package ptolemy.kernel; 029 030import java.io.IOException; 031import java.io.Writer; 032import java.lang.ref.WeakReference; 033import java.util.ArrayList; 034import java.util.Collection; 035import java.util.Collections; 036import java.util.Comparator; 037import java.util.Enumeration; 038import java.util.HashMap; 039import java.util.HashSet; 040import java.util.Iterator; 041import java.util.LinkedList; 042import java.util.List; 043import java.util.Map; 044import java.util.Set; 045import java.util.TreeMap; 046 047import ptolemy.kernel.attributes.VersionAttribute; 048import ptolemy.kernel.util.Attribute; 049import ptolemy.kernel.util.Decorator; 050import ptolemy.kernel.util.IllegalActionException; 051import ptolemy.kernel.util.InternalErrorException; 052import ptolemy.kernel.util.KernelException; 053import ptolemy.kernel.util.NameDuplicationException; 054import ptolemy.kernel.util.Nameable; 055import ptolemy.kernel.util.NamedList; 056import ptolemy.kernel.util.NamedObj; 057import ptolemy.kernel.util.Settable; 058import ptolemy.kernel.util.Workspace; 059import ptolemy.util.StringUtilities; 060 061/////////////////////////////////////////////////////////////////// 062//// CompositeEntity 063 064/** 065 A CompositeEntity is a cluster in a clustered graph. 066 I.e., it is a non-atomic entity, in that 067 it can contain other entities and relations. It supports transparent ports, 068 where, in effect, the port of a contained entity is represented by a port 069 of this entity. Methods that "deeply" traverse the topology 070 see right through transparent ports. 071 It may be opaque, in which case its ports are opaque and methods 072 that "deeply" traverse the topology do not see through them. 073 For instance, deepEntityList() returns the opaque entities 074 directly or indirectly contained by this entity. 075 <p> 076 To add an entity or relation to this composite, call its 077 setContainer() method with this composite as an argument. To 078 remove it, call its setContainer() method with a null argument (or 079 another container). The entity must be an instance of 080 ComponentEntity and the relation of ComponentRelation or an 081 exception is thrown. Derived classes may further constrain these 082 to subclasses. To do that, they should override the protected 083 methods _addEntity() and _addRelation() and the public member 084 newRelation(). 085 <p> 086 A CompositeEntity may be contained by another CompositeEntity. 087 To set that up, call the setContainer() method of the inside entity. 088 Derived classes may further constrain the container to be 089 a subclass of CompositeEntity. To do this, they should override 090 setContainer() to throw an exception. Recursive containment 091 structures, where an entity directly or indirectly contains itself, 092 are disallowed, and an exception is thrown on an attempt to set up 093 such a structure. 094 <p> 095 A CompositeEntity can contain instances of ComponentPort. By default 096 these ports will be transparent, although subclasses of CompositeEntity 097 can make them opaque by overriding the isOpaque() method to return 098 <i>true</i>. Derived classes may further constrain the ports to a 099 subclass of ComponentPort. 100 To do this, they should override the public method newPort() to create 101 a port of the appropriate subclass, and the protected method _addPort() 102 to throw an exception if its argument is a port that is not of the 103 appropriate subclass. 104 <p> 105 Since contained entities implement the 106 {@link ptolemy.kernel.util.Instantiable} interface, 107 some may be class definitions. If an entity is a class definition, 108 then it is not included in the lists returned by 109 {@link #entityList()}, {@link #entityList(Class)}, 110 {@link #deepEntityList()}, and {@link #allAtomicEntityList()}. 111 Correspondingly, if it is not a class definition, then it is not 112 included in the list returned by {@link #classDefinitionList()}. 113 Contained class definitions are nonetheless required to have names 114 distinct from contained entities that are not class definitions, 115 and the method {@link #getEntity(String)} will return either 116 a class definition or an entity that is not a class definition, 117 as long as the name matches. Note that contained entities that 118 are class definitions cannot be connected to other entities. 119 Moreover, they cannot be deleted as long as there are either 120 subclasses or instances present. 121 122 @author John S. Davis II, Edward A. Lee, contributor: Christopher Brooks 123 @version $Id$ 124 @since Ptolemy II 0.2 125 @Pt.ProposedRating Green (eal) 126 @Pt.AcceptedRating Green (hyzheng) 127 */ 128public class CompositeEntity extends ComponentEntity { 129 /** Construct an entity in the default workspace with an empty string 130 * as its name. Add the entity to the workspace directory. 131 * Increment the version number of the workspace. 132 */ 133 public CompositeEntity() { 134 super(); 135 _addIcon(); 136 } 137 138 /** Construct an entity in the specified workspace with an empty 139 * string as a name. You can then change the name with setName(). 140 * If the workspace argument is null, then use the default workspace. 141 * Add the entity to the workspace directory. 142 * Increment the version number of the workspace. 143 * @param workspace The workspace that will list the entity. 144 */ 145 public CompositeEntity(Workspace workspace) { 146 super(workspace); 147 _addIcon(); 148 } 149 150 /** Create an object with a name and a container. 151 * The container argument must not be null, or a 152 * NullPointerException will be thrown. This entity will use the 153 * workspace of the container for synchronization and version counts. 154 * If the name argument is null, then the name is set to the empty string. 155 * Increment the version of the workspace. 156 * @param container The container entity. 157 * @param name The name of the entity. 158 * @exception IllegalActionException If the container is incompatible 159 * with this entity. 160 * @exception NameDuplicationException If the name coincides with 161 * an entity already in the container. 162 */ 163 public CompositeEntity(CompositeEntity container, String name) 164 throws IllegalActionException, NameDuplicationException { 165 super(container, name); 166 _addIcon(); 167 } 168 169 /////////////////////////////////////////////////////////////////// 170 //// public methods //// 171 172 /** Return a list that consists of all the atomic entities in a model. 173 * This method differs from {@link #deepEntityList()} in that 174 * this method looks inside opaque entities, whereas deepEntityList() 175 * does not. The returned list does not include any entities that 176 * are class definitions. 177 * @return a List of all atomic entities in the model. 178 */ 179 public List allAtomicEntityList() { 180 // We don't use an Iterator here so that we can modify the list 181 // rather than having both an Iterator and a result list. 182 // 183 // Note: 184 // deepEntityList() should be renamed to deepOpaqueEntityList() 185 // allAtomicEntityList() to deepAtomicEntityList() 186 // However, the change would require a fair amount of work. 187 //LinkedList entities = (LinkedList) deepEntityList(); 188 List entities = deepEntityList(); 189 190 for (int i = 0; i < entities.size(); i++) { 191 Object entity = entities.get(i); 192 193 if (entity instanceof CompositeEntity) { 194 // Remove the composite actor and add its containees. 195 entities.remove(i); 196 197 // Note that removing an element from the list causes 198 // the indices of later elements to shift forward by 1. 199 // We reduce the index i by one to match the index in 200 // the list. 201 i--; 202 entities.addAll( 203 ((CompositeEntity) entity).allAtomicEntityList()); 204 } 205 } 206 207 return entities; 208 } 209 210 /** Allow or disallow connections that are created using the connect() 211 * method to cross levels of the hierarchy. 212 * The default is that such connections are disallowed. 213 * Generally it is a bad idea to allow level-crossing 214 * connections, since it breaks modularity. This loss of modularity 215 * means, among other things, that this composite cannot be cloned. 216 * Nonetheless, this capability is provided for the benefit of users 217 * that feel they just must have it, and who are willing to sacrifice 218 * clonability and modularity. 219 * @param boole True to allow level-crossing connections. 220 */ 221 public void allowLevelCrossingConnect(boolean boole) { 222 _levelCrossingConnectAllowed = boole; 223 } 224 225 /** List the contained class definitions 226 * in the order they were added 227 * (using their setContainer() method). The returned list does 228 * not include any entities that are not class definitions. 229 * The returned list is static in the sense 230 * that it is not affected by any subsequent additions or removals 231 * of class definitions. 232 * This method is read-synchronized on the workspace. 233 * @return A list of ComponentEntity objects. 234 * @see #entityList() 235 */ 236 public List classDefinitionList() { 237 try { 238 _workspace.getReadAccess(); 239 240 if (_workspace.getVersion() == _classDefinitionListVersion) { 241 return _classDefinitionListCache; 242 } 243 244 List result = new LinkedList(); 245 246 // This might be called from within a superclass constructor, 247 // in which case there are no contained entities yet. 248 if (_containedEntities != null) { 249 Iterator entities = _containedEntities.elementList().iterator(); 250 251 while (entities.hasNext()) { 252 ComponentEntity entity = (ComponentEntity) entities.next(); 253 254 if (entity.isClassDefinition()) { 255 result.add(entity); 256 } 257 } 258 259 _classDefinitionListCache = result; 260 _classDefinitionListVersion = _workspace.getVersion(); 261 } 262 263 return result; 264 } finally { 265 _workspace.doneReading(); 266 } 267 } 268 269 /** Clone the object into the specified workspace. The new object is 270 * <i>not</i> added to the directory of that workspace (you must do this 271 * yourself if you want it there). 272 * This method gets read access on the workspace associated with 273 * this object. 274 * @param workspace The workspace for the cloned object. 275 * @exception CloneNotSupportedException If one of the attributes 276 * cannot be cloned. 277 * @return A new CompositeEntity. 278 */ 279 @Override 280 public Object clone(Workspace workspace) throws CloneNotSupportedException { 281 try { 282 workspace().getReadAccess(); 283 // NOTE: The following assumes we will not do an exportMoML() 284 // at the same time we are doing a clone(). Since clone() is used 285 // to instantiate objects, this seems safe. But the field below 286 // is shared with exportMoML(). 287 _levelCrossingLinks = new LinkedList<LinkRecord>(); 288 289 CompositeEntity newEntity = (CompositeEntity) super.clone( 290 workspace); 291 292 newEntity._containedEntities = new NamedList(newEntity); 293 newEntity._containedRelations = new NamedList(newEntity); 294 295 // Clone the contained relations. 296 Iterator relations = relationList().iterator(); 297 while (relations.hasNext()) { 298 ComponentRelation relation = (ComponentRelation) relations 299 .next(); 300 ComponentRelation newRelation = (ComponentRelation) relation 301 .clone(workspace); 302 // Assume that since we are dealing with clones, 303 // exceptions won't occur normally. If they do, throw a 304 // CloneNotSupportedException. 305 try { 306 newRelation.setContainer(newEntity); 307 // To support relation groups, duplicate any links 308 // to other relations. The links to ports will be 309 // done below in a way that preserves the order of the 310 // links. Links in ports have an order, whereas links 311 // in relations do not. 312 Enumeration links = relation._linkList.getContainers(); 313 while (links.hasMoreElements()) { 314 Object link = links.nextElement(); 315 if (link instanceof Relation) { 316 // Create the link only if the corresponding 317 // relation has been created. This ensures 318 // that the link is created exactly once. 319 // Get the relation using a relative name 320 // in case it's a level-crossing link. 321 Relation farRelation = newEntity.getRelation( 322 ((Nameable) link).getName(this)); 323 if (farRelation != null) { 324 newRelation.link(farRelation); 325 } 326 } 327 } 328 } catch (KernelException ex) { 329 throw new CloneNotSupportedException( 330 "Failed to clone a CompositeEntity: " 331 + ex.getMessage()); 332 } 333 } 334 335 // Clone the contained classes. 336 Iterator classes = classDefinitionList().iterator(); 337 338 while (classes.hasNext()) { 339 ComponentEntity classDefinition = (ComponentEntity) classes 340 .next(); 341 ComponentEntity newSubentity = (ComponentEntity) classDefinition 342 .clone(workspace); 343 344 // Assume that since we are dealing with clones, 345 // exceptions won't occur normally. If they do, throw a 346 // CloneNotSupportedException. 347 try { 348 newSubentity.setContainer(newEntity); 349 } catch (KernelException ex) { 350 throw new CloneNotSupportedException( 351 "Failed to clone a CompositeEntity: " 352 + KernelException.stackTraceToString(ex)); 353 } 354 } 355 356 // Clone the contained entities. 357 Iterator entities = entityList().iterator(); 358 359 while (entities.hasNext()) { 360 ComponentEntity entity = (ComponentEntity) entities.next(); 361 ComponentEntity newSubentity = (ComponentEntity) entity 362 .clone(workspace); 363 364 // Assume that since we are dealing with clones, 365 // exceptions won't occur normally. If they do, throw a 366 // CloneNotSupportedException. 367 try { 368 newSubentity.setContainer(newEntity); 369 } catch (KernelException ex) { 370 throw new CloneNotSupportedException( 371 "Failed to clone a CompositeEntity: " 372 + KernelException.stackTraceToString(ex)); 373 } 374 375 // Clone the links of the ports of the cloned entities. 376 Iterator ports = entity.portList().iterator(); 377 378 while (ports.hasNext()) { 379 ComponentPort port = (ComponentPort) ports.next(); 380 Enumeration linkedRelations = port.linkedRelations(); 381 382 int index = 0; 383 while (linkedRelations.hasMoreElements()) { 384 ComponentRelation rel = (ComponentRelation) linkedRelations 385 .nextElement(); 386 387 // A null link (supported since indexed links) might 388 // yield a null relation here. EAL 7/19/00. 389 if (rel != null) { 390 // if (rel.getContainer() != this) { 391 // throw new CloneNotSupportedException( 392 // "Cannot clone a CompositeEntity with " 393 // + "level crossing transitions." 394 // + " The relation was: " + rel 395 // + ", its container was: " 396 // + rel.getContainer() 397 // + ", which is not equal to " 398 // + this); 399 // } 400 401 Port newPort = newSubentity.getPort(port.getName()); 402 403 // This may be a level-crossing link, in which case we have to 404 // defer it to the common container. 405 if (rel.getContainer() == this) { 406 // Not a level-crossing link. 407 ComponentRelation newRelation = newEntity 408 .getRelation(rel.getName()); 409 try { 410 newPort.link(newRelation); 411 } catch (IllegalActionException ex) { 412 throw new CloneNotSupportedException( 413 "Failed to clone a CompositeEntity: " 414 + ex.getMessage()); 415 } 416 } else { 417 // It is a level-crossing link. 418 // Find the common container. 419 NamedObj container = _commonContainer(port, 420 rel); 421 if (container instanceof CompositeEntity) { 422 List<LinkRecord> linkRecords = ((CompositeEntity) container)._levelCrossingLinks; 423 if (linkRecords == null) { 424 throw new CloneNotSupportedException( 425 "Level crossing link goes outside of the class definition boundary: " 426 + port.getFullName() 427 + " and " 428 + rel.getFullName()); 429 } 430 // NOTE: The record has the new port (after cloning), but 431 // the old relation (before cloning) because we can't be sure 432 // the new relation exists yet. 433 LinkRecord record = new LinkRecord(); 434 record.port = newPort; 435 record.relation1 = rel; 436 record.relation2 = null; 437 record.index = index; 438 linkRecords.add(record); 439 } 440 } 441 } 442 index++; 443 } 444 } 445 } 446 447 // Clone the inside links from the ports of this entity. 448 Iterator ports = portList().iterator(); 449 450 while (ports.hasNext()) { 451 ComponentPort port = (ComponentPort) ports.next(); 452 relations = port.insideRelationList().iterator(); 453 454 while (relations.hasNext()) { 455 Relation relation = (Relation) relations.next(); 456 // To support level-crossing links to the inside, 457 // be sure to get the name of the relation relative to this. 458 ComponentRelation newRelation = newEntity 459 .getRelation(relation.getName(this)); 460 Port newPort = newEntity.getPort(port.getName()); 461 462 try { 463 newPort.link(newRelation); 464 } catch (IllegalActionException ex) { 465 throw new CloneNotSupportedException( 466 "Failed to clone a CompositeEntity: " 467 + ex.getMessage()); 468 } 469 } 470 } 471 472 // Finally, clone level-crossing links, if there are any. 473 if (_levelCrossingLinks != null) { 474 for (LinkRecord record : _levelCrossingLinks) { 475 try { 476 if (record.port != null) { 477 String relationName = record.relation1 478 .getName(this); 479 Relation newRelation = newEntity 480 .getRelation(relationName); 481 if (newRelation == null) { 482 throw new CloneNotSupportedException( 483 "Cloning level-crossing links failed. Relation missing: " 484 + relationName); 485 } 486 record.port.insertLink(record.index, newRelation); 487 } else { 488 record.relation1.link(record.relation2); 489 } 490 } catch (IllegalActionException ex) { 491 throw new CloneNotSupportedException( 492 "Cloning level-crossing links failed: " + ex); 493 } 494 } 495 } 496 497 return newEntity; 498 } finally { 499 _levelCrossingLinks = null; 500 try { 501 workspace().doneReading(); 502 } catch (Throwable ex) { 503 throw new InternalErrorException(this, ex, "Internal Error: " 504 + "workspace().doneReading() failed?"); 505 } 506 } 507 } 508 509 /** Create a new relation and use it to connect two ports. 510 * It creates a new relation using newRelation() with an automatically 511 * generated name and uses it to link the specified ports. 512 * The order of the ports determines the order in which the 513 * links to the relation are established, but otherwise has no 514 * importance. 515 * The name is of the form "_R<i>i</i>" where <i>i</i> is an integer. 516 * Level-crossing connections are not permitted unless 517 * allowLevelCrossingConnect() has been called with a <i>true</i> 518 * argument. Note that is rarely a good idea to permit level crossing 519 * connections, since they break modularity and cloning. 520 * A reference to the newly created relation is returned. 521 * To remove the relation, call its setContainer() method with a null 522 * argument. This method is write-synchronized on the workspace 523 * and increments its version number. 524 * <p>Note that if this method is being called many times, then 525 * it may be more efficient to use 526 * {@link #connect(ComponentPort, ComponentPort, String)} 527 * instead of this method because this method calls 528 * {@link #uniqueName(String)} each time, which 529 * searches the object for attributes, ports, entities and relations 530 * that may match a candidate unique name. 531 * 532 * @param port1 The first port to connect. 533 * @param port2 The second port to connect. 534 * @return The ComponentRelation that is created to connect port1 and 535 * port2. 536 * @exception IllegalActionException If one of the arguments is null, or 537 * if a disallowed level-crossing connection would result. 538 */ 539 public ComponentRelation connect(ComponentPort port1, ComponentPort port2) 540 throws IllegalActionException { 541 try { 542 return connect(port1, port2, uniqueName("_R")); 543 } catch (NameDuplicationException ex) { 544 // This exception should not be thrown. 545 throw new InternalErrorException(this, ex, 546 "Internal error in CompositeEntity.connect() method!"); 547 } 548 } 549 550 /** Create a new relation with the specified name and use it to 551 * connect two ports. Level-crossing connections are not permitted 552 * unless allowLevelCrossingConnect() has been called with a true 553 * argument. Note that is rarely a good idea to permit level crossing 554 * connections, since they break modularity and cloning. 555 * A reference to the newly created alias relation is returned. 556 * To remove the relation, call its setContainer() method with a null 557 * argument. This method is write-synchronized on the workspace 558 * and increments its version number. 559 * @param port1 The first port to connect. 560 * @param port2 The second port to connect. 561 * @param relationName The name of the new relation. 562 * @return The ComponentRelation that is created to connect port1 and 563 * port2. 564 * @exception IllegalActionException If one of the arguments is null, or 565 * if a disallowed level-crossing connection would result, or if the two 566 * ports are not in the same workspace as this entity. 567 * @exception NameDuplicationException If there is already a relation with 568 * the specified name in this entity. 569 */ 570 public ComponentRelation connect(ComponentPort port1, ComponentPort port2, 571 String relationName) 572 throws IllegalActionException, NameDuplicationException { 573 if (port1 == null || port2 == null) { 574 throw new IllegalActionException(this, 575 "Attempt to connect null port."); 576 } 577 578 if (port1.workspace() != port2.workspace() 579 || port1.workspace() != _workspace) { 580 throw new IllegalActionException(port1, port2, 581 "Cannot connect ports because workspaces are different."); 582 } 583 584 try { 585 _workspace.getWriteAccess(); 586 587 ComponentRelation ar = newRelation(relationName); 588 589 if (_levelCrossingConnectAllowed) { 590 port1.liberalLink(ar); 591 } else { 592 port1.link(ar); 593 } 594 595 // Have to catch the exception to restore the original state. 596 try { 597 if (_levelCrossingConnectAllowed) { 598 port2.liberalLink(ar); 599 } else { 600 port2.link(ar); 601 } 602 } catch (IllegalActionException ex) { 603 port1.unlink(ar); 604 throw ex; 605 } 606 607 return ar; 608 } finally { 609 _workspace.doneWriting(); 610 } 611 } 612 613 /** Return an iterator over contained objects. In this class, 614 * this is an iterator over attributes, ports, classes, 615 * entities, and relations. 616 * @return An iterator over instances of NamedObj contained by this 617 * object. 618 */ 619 @Override 620 public Iterator containedObjectsIterator() { 621 return new ContainedObjectsIterator(); 622 } 623 624 /** Return a list that consists of all the transparent and opaque 625 * composite entities in a model. This method differs from 626 * {@link #allAtomicEntityList()} in that this method returns 627 * CompositeEntities and allAtomicEntityList() returns atomic 628 * entities. This method differs from {@link #deepEntityList()} 629 * in that this method returns only opaque and transparent 630 * CompositeEntities, whereas deepEntityList() returns opaque 631 * ComponentEntities. The returned list of this method does not 632 * include any entities that are class definitions. 633 * The {@link #entityList(Class)} method only returns entities in the 634 * current level, this method traverses the hierarchy. 635 * @return a List of all transparent and opaque composite entities in the 636 * model. A transparent composite is a composite that does not 637 * contain a director. An opaque composite is a composite that does 638 * contain a director. Note that class definitions are also returned. 639 */ 640 public List<CompositeEntity> deepCompositeEntityList() { 641 try { 642 _workspace.getReadAccess(); 643 LinkedList result = new LinkedList(); 644 645 // This might be called from within a superclass constructor, 646 // in which case there are no contained entities yet. 647 if (_containedEntities != null) { 648 Iterator entities = _containedEntities.elementList().iterator(); 649 while (entities.hasNext()) { 650 ComponentEntity entity = (ComponentEntity) entities.next(); 651 if (/*!entity.isClassDefinition() */ 652 /* &&!entity.isOpaque() */ 653 entity instanceof CompositeEntity) { 654 result.add(entity); 655 result.addAll(((CompositeEntity) entity) 656 .deepCompositeEntityList()); 657 } 658 } 659 } 660 661 return result; 662 } finally { 663 _workspace.doneReading(); 664 } 665 } 666 667 /** List the opaque entities that are directly or indirectly 668 * contained by this entity. The list will be empty if there 669 * are no such contained entities. This list does not include 670 * class definitions nor anything contained by them. 671 * This method is read-synchronized on the workspace. 672 * @return A list of opaque ComponentEntity objects. 673 * @see #classDefinitionList() 674 * @see #allAtomicEntityList() 675 */ 676 public List deepOpaqueEntityList() { 677 try { 678 _workspace.getReadAccess(); 679 List results = new ArrayList(); 680 _deepOpaqueEntityList(results); 681 return results; 682 } finally { 683 _workspace.doneReading(); 684 } 685 } 686 687 /** List the opaque entities that are directly or indirectly 688 * contained by this entity. The list will be empty if there 689 * are no such contained entities. This list does not include 690 * class definitions nor anything contained by them. 691 * This method is read-synchronized on the workspace. 692 * @return A list of opaque ComponentEntity objects. 693 * @see #classDefinitionList() 694 * @see #allAtomicEntityList() 695 */ 696 public List deepEntityList() { 697 try { 698 _workspace.getReadAccess(); 699 700 LinkedList result = new LinkedList(); 701 702 // This might be called from within a superclass constructor, 703 // in which case there are no contained entities yet. 704 if (_containedEntities != null) { 705 Iterator entities = _containedEntities.elementList().iterator(); 706 while (entities.hasNext()) { 707 ComponentEntity entity = (ComponentEntity) entities.next(); 708 709 if (!entity.isClassDefinition()) { 710 if (entity.isOpaque()) { 711 result.add(entity); 712 } else { 713 result.addAll(((CompositeEntity) entity) 714 .deepEntityList()); 715 } 716 } 717 } 718 } 719 720 return result; 721 } finally { 722 _workspace.doneReading(); 723 } 724 } 725 726 /** List the NamedObjs that are directly or indirectly 727 * contained by this entity. The list will be empty if there 728 * are no such contained NamedObjs. This list does not include 729 * class definitions nor anything contained by them. 730 * This method is read-synchronized on the workspace. 731 * This method differs from deepEntityList() in that deepEntityList() 732 * does not look inside opaques. 733 * @return A list of opaque ComponentEntity objects. 734 * @see #classDefinitionList() 735 * @see #allAtomicEntityList() 736 */ 737 public List deepNamedObjList() { 738 try { 739 _workspace.getReadAccess(); 740 741 LinkedList result = new LinkedList(); 742 743 // This might be called from within a superclass constructor, 744 // in which case there are no contained entities yet. 745 if (_containedEntities != null) { 746 Iterator entities = _containedEntities.elementList().iterator(); 747 while (entities.hasNext()) { 748 ComponentEntity entity = (ComponentEntity) entities.next(); 749 750 if (!entity.isClassDefinition()) { 751 // if (entity.isOpaque()) { 752 result.add(entity); 753 if (!entity.isOpaque()) { 754 result.addAll(((CompositeEntity) entity) 755 .deepNamedObjList()); 756 } 757 } 758 } 759 } 760 761 return result; 762 } finally { 763 _workspace.doneReading(); 764 } 765 } 766 767 /** Return a set with the relations that are directly or indirectly 768 * contained by this entity. The set will be empty if there 769 * are no such contained relations. 770 * This method is read-synchronized on the workspace. 771 * @return A set of ComponentRelation objects. 772 */ 773 public Set<ComponentRelation> deepRelationSet() { 774 try { 775 _workspace.getReadAccess(); 776 777 Set<ComponentRelation> result = new HashSet<ComponentRelation>(); 778 779 _addAll(result, relationList()); 780 781 // This might be called from within a superclass constructor, 782 // in which case there are no contained entities yet. 783 if (_containedEntities != null) { 784 785 for (Object entityObject : _containedEntities.elementList()) { 786 ComponentEntity entity = (ComponentEntity) entityObject; 787 if (!entity.isClassDefinition()) { 788 if (entity instanceof CompositeEntity) { 789 _addAll(result, ((CompositeEntity) entity) 790 .deepRelationSet()); 791 } 792 } 793 } 794 } 795 796 return result; 797 } finally { 798 _workspace.doneReading(); 799 } 800 } 801 802 /** Enumerate the opaque entities that are directly or indirectly 803 * contained by this entity. The enumeration will be empty if there 804 * are no such contained entities. The enumeration does not include 805 * any entities that are class definitions. 806 * This method is read-synchronized on the workspace. 807 * @deprecated Use deepEntityList() instead. 808 * @return An enumeration of opaque ComponentEntity objects. 809 */ 810 @Deprecated 811 public Enumeration deepGetEntities() { 812 return Collections.enumeration(deepEntityList()); 813 } 814 815 /** List the contained entities in the order they were added 816 * (using their setContainer() method). The returned list does 817 * not include any class definitions. 818 * The returned list is static in the sense 819 * that it is not affected by any subsequent additions or removals 820 * of entities. 821 * This method is read-synchronized on the workspace. 822 * @return A list of ComponentEntity objects. 823 * @see #classDefinitionList() 824 */ 825 public List entityList() { 826 try { 827 _workspace.getReadAccess(); 828 829 if (_workspace.getVersion() == _entityListVersion) { 830 List entityList = _entityListCache.get(); 831 if (entityList != null) { 832 return entityList; 833 } 834 } 835 836 List result = new LinkedList(); 837 838 // This might be called from within a superclass constructor, 839 // in which case there are no contained entities yet. 840 if (_containedEntities != null) { 841 Iterator entities = _containedEntities.elementList().iterator(); 842 843 while (entities.hasNext()) { 844 ComponentEntity entity = (ComponentEntity) entities.next(); 845 846 if (!entity.isClassDefinition()) { 847 result.add(entity); 848 } 849 } 850 851 _entityListCache = new WeakReference<List>(result); 852 _entityListVersion = _workspace.getVersion(); 853 } 854 855 return result; 856 } finally { 857 _workspace.doneReading(); 858 } 859 } 860 861 /** Return a list of the component entities contained by this object that 862 * are instances of the specified Java class. If there are no such 863 * instances, then return an empty list. The returned list does not 864 * include class definitions. 865 * This method is read-synchronized on the workspace. 866 * @param filter The class of ComponentEntity of interest. 867 * @param <T> The type corresponding to the class of interest. 868 * @return A list of instances of specified class. 869 * @see #classDefinitionList() 870 */ 871 public <T> List<T> entityList(Class<T> filter) { 872 try { 873 _workspace.getReadAccess(); 874 875 List<T> result = new LinkedList<T>(); 876 877 // This might be called from within a superclass constructor, 878 // in which case there are no contained entities yet. 879 if (_containedEntities != null) { 880 Iterator entities = _containedEntities.elementList().iterator(); 881 882 while (entities.hasNext()) { 883 ComponentEntity entity = (ComponentEntity) entities.next(); 884 885 if (filter.isInstance(entity) 886 && !entity.isClassDefinition()) { 887 result.add((T) entity); 888 } 889 } 890 } 891 892 return result; 893 } finally { 894 _workspace.doneReading(); 895 } 896 } 897 898 /** Return a sequence of MoML link attributes that describe 899 * any link between objects (ports, entities, and relations) that are 900 * present in the <i>filter</i> argument. Both ends of the link 901 * must be present in <i>filter</i> for MoML to be generated for that 902 * link. The <i>filter</i> 903 * argument normally contains ports, relations, and entities 904 * that are contained by this composite entity. If it contains 905 * an entity, then that is equivalent to containing all the ports 906 * contained by that entity. It is recommended to use a collection 907 * class (such as HashSet) for which the contains() method is 908 * efficient. 909 * <p> 910 * If the filter argument is null, then return all the links that this 911 * composite is responsible for (i.e., apply no filtering). If the 912 * argument is an empty collection, then return none of the links. The 913 * links that this entity is responsible for are the inside links of 914 * its ports, and links on ports contained by contained entities. 915 * <p> 916 * If any link is found where both ends of the link are inherited objects, 917 * then that link is not exported. It is assumed that the base class 918 * will export that link. For this purpose, a port of a contained 919 * entity is deemed to be an inherited object if it is itself a class 920 * element <i>and</i> its container is an inherited object. 921 * @param depth The depth below the MoML export in the hierarchy. 922 * @param filter A collection of ports, parameters, and entities, or 923 * null to apply no filtering. 924 * @return A string that describes the links present in the 925 * <i>filter</i>. 926 * @exception IOException If an I/O error occurs. 927 */ 928 public String exportLinks(int depth, Collection filter) throws IOException { 929 // To get the ordering right, 930 // we read the links from the ports, not from the relations. 931 StringBuffer result = new StringBuffer(); 932 933 // First, produce the inside links on contained ports. 934 Iterator ports = portList().iterator(); 935 936 while (ports.hasNext()) { 937 ComponentPort port = (ComponentPort) ports.next(); 938 // Skip the port if it is not persistent. 939 if (port == null || !port.isPersistent()) { 940 continue; 941 } 942 Iterator relations = port.insideRelationList().iterator(); 943 944 // The following variables are used to determine whether to 945 // specify the index of the link explicitly, or to leave 946 // it implicit. 947 int index = -1; 948 boolean useIndex = false; 949 950 while (relations.hasNext()) { 951 index++; 952 953 ComponentRelation relation = (ComponentRelation) relations 954 .next(); 955 956 // Skip the relation if it is not persistent. 957 if (relation != null && !relation.isPersistent()) { 958 continue; 959 } 960 961 if (relation == null) { 962 // Gap in the links. The next link has to use an 963 // explicit index. 964 useIndex = true; 965 continue; 966 } 967 968 // If both ends of the link are inherited objects, then 969 // suppress the export. This depends on the level of export 970 // because if both ends of the link are implied, then the 971 // link is implied. 972 if (_commonImplier(relation, depth, port, depth)) { 973 continue; 974 } 975 976 // Apply filter. 977 if (filter == null 978 || filter.contains(relation) && (filter.contains(port) 979 || filter.contains(port.getContainer()))) { 980 // If the relation is not persistent, then do not export the link. 981 if (relation != null && !relation.isPersistent()) { 982 continue; 983 } 984 // In order to support level-crossing links, consider the 985 // possibility that the relation is not contained by this. 986 String relationName; 987 988 if (relation.getContainer() == this) { 989 relationName = relation.getName(); 990 } else { 991 if (deepContains(relation)) { 992 // NOTE: This used to export the full name, but the 993 // relative name is sufficient. 994 relationName = relation.getName(this); 995 } else { 996 // Can't export the link here since when the 997 // MoML file is re-read there is no assurance that 998 // the relation exists when the link is to be 999 // created. Need to delegate to the least common 1000 // container. 1001 _recordLevelCrossingLink(port, relation, null, 1002 index); 1003 continue; 1004 } 1005 } 1006 1007 String escapedPortName = StringUtilities 1008 .escapeForXML(port.getName()); 1009 String escapedRelationName = StringUtilities 1010 .escapeForXML(relationName); 1011 if (useIndex) { 1012 useIndex = false; 1013 result.append(_getIndentPrefix(depth) + "<link port=\"" 1014 + escapedPortName + "\" insertAt=\"" + index 1015 + "\" relation=\"" + escapedRelationName 1016 + "\"/>\n"); 1017 } else { 1018 result.append(_getIndentPrefix(depth) + "<link port=\"" 1019 + escapedPortName + "\" relation=\"" 1020 + escapedRelationName + "\"/>\n"); 1021 } 1022 } 1023 } 1024 } 1025 1026 // Next, produce the links on ports contained by contained entities. 1027 Iterator entities = entityList().iterator(); 1028 1029 while (entities.hasNext()) { 1030 ComponentEntity entity = (ComponentEntity) entities.next(); 1031 1032 // Skip the entity if it is not persistent. 1033 if (entity == null || !entity.isPersistent()) { 1034 continue; 1035 } 1036 1037 ports = entity.portList().iterator(); 1038 1039 while (ports.hasNext()) { 1040 ComponentPort port = (ComponentPort) ports.next(); 1041 // Skip the port if it is not persistent. 1042 if (port == null || !port.isPersistent()) { 1043 continue; 1044 } 1045 Iterator relations = port.linkedRelationList().iterator(); 1046 1047 // The following variables are used to determine whether to 1048 // specify the index of the link explicitly, or to leave 1049 // it implicit. 1050 int index = -1; 1051 boolean useIndex = false; 1052 1053 while (relations.hasNext()) { 1054 index++; 1055 1056 ComponentRelation relation = (ComponentRelation) relations 1057 .next(); 1058 1059 // Skip the relation if it is not persistent. 1060 if (relation != null && !relation.isPersistent()) { 1061 continue; 1062 } 1063 1064 if (relation == null) { 1065 // Gap in the links. The next link has to use an 1066 // explicit index. 1067 useIndex = true; 1068 continue; 1069 } 1070 1071 // If both ends of the link are inherited objects, then 1072 // suppress the export. This depends on the level of export 1073 // because if both ends of the link are implied, then the 1074 // link is implied. Note that we need for both the port 1075 // to be implied and the port's container to share a 1076 // common implier with the relation. We know that the port 1077 // is contained within its container, so we don't have to 1078 // check it separately for a common implier. 1079 if (port.getDerivedLevel() <= depth + 1 && _commonImplier( 1080 relation, depth, port.getContainer(), depth)) { 1081 continue; 1082 } 1083 // Used to have the previous logic here, skipping the link export, 1084 // instead of the above. 1085 // But careful! It may be that the both the relation and 1086 // the port are derived, but not from the same object. 1087 // This can happen with level-crossing links. 1088 // Check that the container above at which these two objects 1089 // are implied is the same container. 1090 // EAL 6/6/09 1091 /* 1092 int relationLevel = relation.getDerivedLevel(); 1093 int portLevel = port.getDerivedLevel(); 1094 if ((relationLevel <= depth) 1095 && (portLevel <= (depth + 1)) 1096 && ((port.getContainer()).getDerivedLevel() <= depth)) { 1097 continue; 1098 } 1099 */ 1100 1101 // Apply filter. 1102 if (filter == null || filter.contains(relation) 1103 && (filter.contains(port) 1104 || filter.contains(port.getContainer()))) { 1105 // If the relation is not persistent, then do 1106 // not export the link. 1107 if (relation == null || !relation.isPersistent()) { 1108 continue; 1109 } 1110 // In order to support level-crossing links, 1111 // consider the possibility that the relation 1112 // is not contained by this. 1113 String relationName; 1114 1115 if (relation.getContainer() == this) { 1116 relationName = relation.getName(); 1117 } else { 1118 if (deepContains(relation)) { 1119 // NOTE: This used to export the full name, but the 1120 // relative name is sufficient. 1121 relationName = relation.getName(this); 1122 } else { 1123 // Can't export the link here since when the 1124 // MoML file is re-read there is no assurance that 1125 // the relation exists when the link is to be 1126 // created. Need to delegate to the least common 1127 // container. 1128 _recordLevelCrossingLink(port, relation, null, 1129 index); 1130 continue; 1131 } 1132 } 1133 1134 // Escape any < character that occurs in name. 1135 // setName(String). 1136 String escapedName = StringUtilities 1137 .escapeForXML(entity.getName()); 1138 String escapedPortName = StringUtilities 1139 .escapeForXML(port.getName()); 1140 String escapedRelationName = StringUtilities 1141 .escapeForXML(relationName); 1142 if (useIndex) { 1143 useIndex = false; 1144 result.append(_getIndentPrefix(depth) 1145 + "<link port=\"" + escapedName + "." 1146 + escapedPortName + "\" insertAt=\"" + index 1147 + "\" relation=\"" + escapedRelationName 1148 + "\"/>\n"); 1149 } else { 1150 result.append(_getIndentPrefix(depth) 1151 + "<link port=\"" + escapedName + "." 1152 + escapedPortName + "\" relation=\"" 1153 + escapedRelationName + "\"/>\n"); 1154 } 1155 } 1156 } 1157 } 1158 } 1159 1160 // Finally, produce the links that are between contained 1161 // relations only. Slight trickiness here: Both relations 1162 // on either side of a link have links to each other, 1163 // but we only want to represent one of the links. 1164 // It doesn't matter which one. We do this by accumulating 1165 // a set of visited relations. 1166 Set visitedRelations = new HashSet(); 1167 Iterator relations = relationList().iterator(); 1168 1169 while (relations.hasNext()) { 1170 ComponentRelation relation = (ComponentRelation) relations.next(); 1171 visitedRelations.add(relation); 1172 1173 // Skip the relation if it is not persistent. 1174 if (relation == null || !relation.isPersistent()) { 1175 continue; 1176 } 1177 1178 Iterator portsAndRelations = relation.linkedObjectsList() 1179 .iterator(); 1180 1181 while (portsAndRelations.hasNext()) { 1182 Object portOrRelation = portsAndRelations.next(); 1183 1184 if (portOrRelation instanceof Relation) { 1185 Relation otherRelation = (Relation) portOrRelation; 1186 1187 // Skip the relation if it is not persistent. 1188 if (otherRelation == null 1189 || !otherRelation.isPersistent()) { 1190 continue; 1191 } 1192 1193 // If we have visited the other relation already, then 1194 // we have already represented the link. Skip this. 1195 if (visitedRelations.contains(otherRelation)) { 1196 continue; 1197 } 1198 1199 // If both ends of the link are inherited objects, then 1200 // suppress the export. This depends on the level of export 1201 // because if both ends of the link are implied, then the 1202 // link is implied. 1203 if (_commonImplier(relation, depth, otherRelation, depth)) { 1204 continue; 1205 } 1206 1207 // Apply filter. 1208 if (filter == null || filter.contains(relation) 1209 && filter.contains(otherRelation)) { 1210 // In order to support level-crossing links, consider the 1211 // possibility that the relation is not contained by this. 1212 String relationName; 1213 1214 if (relation.getContainer() == this) { 1215 relationName = relation.getName(); 1216 } else { 1217 if (deepContains(relation)) { 1218 // NOTE: This used to export the full name, but the 1219 // relative name is sufficient. 1220 relationName = relation.getName(this); 1221 } else { 1222 // Can't export the link here since when the 1223 // MoML file is re-read there is no assurance that 1224 // the relation exists when the link is to be 1225 // created. Need to delegate to the least common 1226 // container. 1227 _recordLevelCrossingLink(null, relation, 1228 otherRelation, 0); 1229 continue; 1230 } 1231 } 1232 1233 String otherRelationName; 1234 1235 if (otherRelation.getContainer() == this) { 1236 otherRelationName = otherRelation.getName(); 1237 } else { 1238 // Can't export the link here since when the 1239 // MoML file is re-read there is no assurance that 1240 // the relation exists when the link is to be 1241 // created. Need to delegate to the least common 1242 // container. 1243 _recordLevelCrossingLink(null, relation, 1244 otherRelation, 0); 1245 continue; 1246 } 1247 1248 result.append( 1249 _getIndentPrefix(depth) + "<link relation1=\"" 1250 + relationName + "\" relation2=\"" 1251 + otherRelationName + "\"/>\n"); 1252 } 1253 } 1254 } 1255 } 1256 1257 return result.toString(); 1258 } 1259 1260 /** Override the base class to initialize a data structure that can 1261 * capture and then export level-crossing links deeply contained 1262 * structure within. Otherwise, this delegates to the base 1263 * class to do all the work. 1264 * @param output The output stream to write to. 1265 * @param depth The depth in the hierarchy, to determine indenting. 1266 * @param name The name to use in the exported MoML. 1267 * @exception IOException If an I/O error occurs. 1268 * @see ptolemy.kernel.util.MoMLExportable 1269 */ 1270 @Override 1271 public void exportMoML(Writer output, int depth, String name) 1272 throws IOException { 1273 try { 1274 _levelCrossingLinks = new LinkedList<LinkRecord>(); 1275 super.exportMoML(output, depth, name); 1276 } finally { 1277 _levelCrossingLinks = null; 1278 } 1279 } 1280 1281 /** Get the attribute with the given name. The name may be compound, 1282 * with fields separated by periods, in which case the attribute 1283 * returned is contained by a (deeply) contained attribute, port, 1284 * relation, or entity. 1285 * If the name contains one or more periods, then it is assumed 1286 * to be the relative name of an attribute contained by one of 1287 * the contained attributes, ports, entities or relations. 1288 * This method is read-synchronized on the workspace. 1289 * @param name The name of the desired attribute. 1290 * @return The requested attribute if it is found, null otherwise. 1291 */ 1292 @Override 1293 public Attribute getAttribute(String name) { 1294 try { 1295 _workspace.getReadAccess(); 1296 1297 // Check attributes and ports first. 1298 Attribute result = super.getAttribute(name); 1299 1300 if (result == null) { 1301 // Check entities first. 1302 String[] subnames = _splitName(name); 1303 1304 if (subnames[1] != null) { 1305 ComponentEntity entity = getEntity(subnames[0]); 1306 1307 if (entity != null) { 1308 result = entity.getAttribute(subnames[1]); 1309 } 1310 1311 if (result == null) { 1312 // Check relations. 1313 ComponentRelation relation = getRelation(subnames[0]); 1314 1315 if (relation != null) { 1316 result = relation.getAttribute(subnames[1]); 1317 } 1318 } 1319 } 1320 } 1321 1322 return result; 1323 } finally { 1324 _workspace.doneReading(); 1325 } 1326 } 1327 1328 /** Enumerate the contained entities in the order they were added 1329 * (using their setContainer() method). 1330 * The returned enumeration is static in the sense 1331 * that it is not affected by any subsequent additions or removals 1332 * of entities. 1333 * This method is read-synchronized on the workspace. 1334 * @deprecated Use entityList() instead. 1335 * @return An enumeration of ComponentEntity objects. 1336 */ 1337 @Deprecated 1338 public Enumeration getEntities() { 1339 return Collections.enumeration(entityList()); 1340 } 1341 1342 /** Get a contained entity by name. The name may be compound, 1343 * with fields separated by periods, in which case the entity 1344 * returned is contained by a (deeply) contained entity. 1345 * This method will return class definitions 1346 * and ordinary entities. 1347 * This method is read-synchronized on the workspace. 1348 * @param name The name of the desired entity. 1349 * @return An entity with the specified name, or null if none exists. 1350 */ 1351 public ComponentEntity getEntity(String name) { 1352 try { 1353 _workspace.getReadAccess(); 1354 1355 // This might be called from within a superclass constructor, 1356 // in which case there are no contained entities yet. 1357 if (_containedEntities == null) { 1358 return null; 1359 } 1360 1361 String[] subnames = _splitName(name); 1362 1363 if (subnames[1] == null) { 1364 return (ComponentEntity) _containedEntities.get(name); 1365 } else { 1366 Object match = _containedEntities.get(subnames[0]); 1367 1368 if (match == null) { 1369 return null; 1370 } else { 1371 if (match instanceof CompositeEntity) { 1372 return ((CompositeEntity) match).getEntity(subnames[1]); 1373 } else { 1374 return null; 1375 } 1376 } 1377 } 1378 } finally { 1379 _workspace.doneReading(); 1380 } 1381 } 1382 1383 /** Get a contained port by name. The name may be compound, 1384 * with fields separated by periods, in which case the port returned is 1385 * contained by a (deeply) contained entity. 1386 * This method is read-synchronized on the workspace. 1387 * @param name The name of the desired port. 1388 * @return A port with the specified name, or null if none exists. 1389 */ 1390 @Override 1391 public Port getPort(String name) { 1392 try { 1393 _workspace.getReadAccess(); 1394 1395 String[] subnames = _splitName(name); 1396 1397 if (subnames[1] == null) { 1398 return super.getPort(name); 1399 } else { 1400 ComponentEntity match = getEntity(subnames[0]); 1401 1402 if (match == null) { 1403 return null; 1404 } else { 1405 return match.getPort(subnames[1]); 1406 } 1407 } 1408 } finally { 1409 _workspace.doneReading(); 1410 } 1411 } 1412 1413 /** Get a contained relation by name. The name may be compound, 1414 * with fields separated by periods, in which case the relation 1415 * returned is contained by a (deeply) contained entity. 1416 * This method is read-synchronized on the workspace. 1417 * @param name The name of the desired relation. 1418 * @return A relation with the specified name, or null if none exists. 1419 */ 1420 public ComponentRelation getRelation(String name) { 1421 try { 1422 _workspace.getReadAccess(); 1423 1424 String[] subnames = _splitName(name); 1425 1426 if (subnames[1] == null) { 1427 return (ComponentRelation) _containedRelations.get(name); 1428 } else { 1429 ComponentEntity match = getEntity(subnames[0]); 1430 1431 if (match == null) { 1432 return null; 1433 } else { 1434 if (match instanceof CompositeEntity) { 1435 return ((CompositeEntity) match) 1436 .getRelation(subnames[1]); 1437 } else { 1438 return null; 1439 } 1440 } 1441 } 1442 } finally { 1443 _workspace.doneReading(); 1444 } 1445 } 1446 1447 /** Enumerate the relations contained by this entity. 1448 * The returned enumeration is static in the sense 1449 * that it is not affected by any subsequent additions or removals 1450 * of relations. 1451 * This method is read-synchronized on the workspace. 1452 * @deprecated Use relationList() instead. 1453 * @return An enumeration of ComponentRelation objects. 1454 */ 1455 @Deprecated 1456 public Enumeration getRelations() { 1457 return Collections.enumeration(relationList()); 1458 } 1459 1460 /** Return false since CompositeEntities are not atomic. 1461 * Note that this will return false even if there are no contained 1462 * entities or relations. Derived classes may not override this. 1463 * To hide the contents of the entity, they should override isOpaque(). 1464 * @return False. 1465 */ 1466 @Override 1467 public final boolean isAtomic() { 1468 return false; 1469 } 1470 1471 /** Return false. Derived classes may return true in order to hide 1472 * their components behind opaque ports. 1473 * @return True if the entity is opaque. 1474 * @see ptolemy.kernel.CompositeEntity 1475 */ 1476 @Override 1477 public boolean isOpaque() { 1478 return false; 1479 } 1480 1481 /** Lazy version of {@link #allAtomicEntityList()}. 1482 * In this base class, this is identical to allAtomicEntityList(), 1483 * except that if any inside entities are lazy, their contents 1484 * are listed lazily. Derived classes may omit from the returned list any 1485 * entities whose instantiation is deferred. 1486 * @return A list of ComponentEntity objects. 1487 */ 1488 public List lazyAllAtomicEntityList() { 1489 // We don't use an Iterator here so that we can modify the list 1490 // rather than having both an Iterator and a result list. 1491 // 1492 // Note: 1493 // deepEntityList() should be renamed to deepOpaqueEntityList() 1494 // allAtomicEntityList() to deepAtomicEntityList() 1495 // However, the change would require a fair amount of work. 1496 //LinkedList entities = (LinkedList) deepEntityList(); 1497 List entities = lazyDeepEntityList(); 1498 1499 for (int i = 0; i < entities.size(); i++) { 1500 Object entity = entities.get(i); 1501 1502 if (entity instanceof CompositeEntity) { 1503 // Remove the composite actor and add its containees. 1504 entities.remove(i); 1505 1506 // Note that removing an element from the list causes 1507 // the indices of later elements to shift forward by 1. 1508 // We reduce the index i by one to match the index in 1509 // the list. 1510 i--; 1511 entities.addAll( 1512 ((CompositeEntity) entity).lazyAllAtomicEntityList()); 1513 } 1514 } 1515 1516 return entities; 1517 } 1518 1519 /** Lazy version of {#link #allCompositeEntityList()}. 1520 * In this base class, this is identical to allCompositeEntityList(), 1521 * except that if any contained composite is lazy, its contents 1522 * are listed lazily. 1523 * Derived classes may omit from the returned list any class 1524 * definitions whose instantiation is deferred. 1525 * @return A list of transparent ComponentEntity objects. 1526 */ 1527 public List lazyAllCompositeEntityList() { 1528 try { 1529 _workspace.getReadAccess(); 1530 1531 LinkedList result = new LinkedList(); 1532 1533 // This might be called from within a superclass constructor, 1534 // in which case there are no contained entities yet. 1535 if (_containedEntities != null) { 1536 // Note that by directly using _containedEntities rather than 1537 // entityList() we are automatically lazy. 1538 Iterator entities = _containedEntities.elementList().iterator(); 1539 1540 while (entities.hasNext()) { 1541 ComponentEntity entity = (ComponentEntity) entities.next(); 1542 if (/*!entity.isClassDefinition()&& */!entity 1543 .isOpaque() /*entity instanceof CompositeEntity*/) { 1544 result.add(entity); 1545 result.addAll(((CompositeEntity) entity) 1546 .lazyAllCompositeEntityList()); 1547 1548 } 1549 } 1550 } 1551 1552 return result; 1553 } finally { 1554 _workspace.doneReading(); 1555 } 1556 } 1557 1558 /** Return all the transparent and opaque composites. 1559 * In this base class, if any contained composite is lazy, its contents 1560 * are listed lazily. 1561 * Derived classes may omit from the returned list any class 1562 * definitions whose instantiation is deferred. 1563 * @return A list of transparent ComponentEntity objects. 1564 */ 1565 public List lazyAllCompositeTransparentAndOpaqueEntityList() { 1566 try { 1567 _workspace.getReadAccess(); 1568 1569 LinkedList result = new LinkedList(); 1570 1571 // This might be called from within a superclass constructor, 1572 // in which case there are no contained entities yet. 1573 if (_containedEntities != null) { 1574 // Note that by directly using _containedEntities rather than 1575 // entityList() we are automatically lazy. 1576 Iterator entities = _containedEntities.elementList().iterator(); 1577 while (entities.hasNext()) { 1578 ComponentEntity entity = (ComponentEntity) entities.next(); 1579 if (/*!entity.isClassDefinition()&& !entity.isOpaque()*/ 1580 entity instanceof CompositeEntity) { 1581 result.add(entity); 1582 result.addAll(((CompositeEntity) entity) 1583 .lazyAllCompositeTransparentAndOpaqueEntityList()); 1584 1585 } 1586 } 1587 } 1588 1589 return result; 1590 } finally { 1591 _workspace.doneReading(); 1592 } 1593 } 1594 1595 /** Lazy version of {@link #classDefinitionList()}. 1596 * In this base class, this is identical to classDefinitionList(), 1597 * but derived classes may omit from the returned list any class 1598 * definitions whose instantiation is deferred. 1599 * @return A list of ComponentEntity objects. 1600 */ 1601 public List lazyClassDefinitionList() { 1602 return classDefinitionList(); 1603 } 1604 1605 /** Lazy version of {@link #deepEntityList()}. 1606 * In this base class, this is identical to deepEntityList(), 1607 * except that if any contained composite is lazy, its contents 1608 * are listed lazily. 1609 * Derived classes may omit from the returned list any entities 1610 * whose instantiation is deferred. 1611 * @return A list of ComponentEntity objects. 1612 */ 1613 public List lazyDeepEntityList() { 1614 try { 1615 _workspace.getReadAccess(); 1616 1617 LinkedList result = new LinkedList(); 1618 1619 // This might be called from within a superclass constructor, 1620 // in which case there are no contained entities yet. 1621 if (_containedEntities != null) { 1622 // Note that by directly using _containedEntities rather than 1623 // entityList() we are automatically lazy. 1624 Iterator entities = _containedEntities.elementList().iterator(); 1625 while (entities.hasNext()) { 1626 ComponentEntity entity = (ComponentEntity) entities.next(); 1627 1628 if (!entity.isClassDefinition()) { 1629 if (entity.isOpaque()) { 1630 result.add(entity); 1631 } else { 1632 result.addAll(((CompositeEntity) entity) 1633 .lazyDeepEntityList()); 1634 } 1635 } 1636 } 1637 } 1638 1639 return result; 1640 } finally { 1641 _workspace.doneReading(); 1642 } 1643 } 1644 1645 /** Lazy version of {@link #entityList()}. 1646 * In this base class, this is identical to entityList(), 1647 * but derived classes may omit from the returned list any 1648 * entities whose instantiation is deferred. 1649 * @return A list of ComponentEntity objects. 1650 */ 1651 public List lazyEntityList() { 1652 return entityList(); 1653 } 1654 1655 /** Lazy version of {@link #relationList()}. 1656 * In this base class, this is identical to relationList(), 1657 * but derived classes may omit from the returned list any 1658 * relations whose instantiation is deferred. 1659 * @return A list of Relation objects. 1660 */ 1661 public List lazyRelationList() { 1662 return relationList(); 1663 } 1664 1665 /** Create a new relation with the specified name, add it to the 1666 * relation list, and return it. Derived classes can override 1667 * this to create domain-specific subclasses of ComponentRelation. 1668 * This method is write-synchronized on the workspace and increments 1669 * its version number. 1670 * @param name The name of the new relation. 1671 * @return The new relation. 1672 * @exception IllegalActionException If name argument is null. 1673 * @exception NameDuplicationException If name collides with a name 1674 * already in the container. 1675 */ 1676 public ComponentRelation newRelation(String name) 1677 throws IllegalActionException, NameDuplicationException { 1678 try { 1679 _workspace.getWriteAccess(); 1680 1681 ComponentRelation rel = new ComponentRelation(this, name); 1682 return rel; 1683 } finally { 1684 _workspace.doneWriting(); 1685 } 1686 } 1687 1688 /** Return the number of contained entities, not including 1689 * class definitions. 1690 * This method is read-synchronized on the workspace. 1691 * @return The number of entities. 1692 * @deprecated Use numberOfEntities 1693 * @see #numberOfEntities() 1694 */ 1695 @Deprecated 1696 public int numEntities() { 1697 return numberOfEntities(); 1698 } 1699 1700 /** Return the number of contained relations. 1701 * This method is read-synchronized on the workspace. 1702 * @return The number of relations. 1703 * @deprecated Use numberOfRelations. 1704 */ 1705 @Deprecated 1706 public int numRelations() { 1707 return numberOfRelations(); 1708 } 1709 1710 /** Return the number of contained entities, not including 1711 * class definitions. 1712 * This method is read-synchronized on the workspace. 1713 * @return The number of entities. 1714 */ 1715 public int numberOfEntities() { 1716 try { 1717 _workspace.getReadAccess(); 1718 return entityList().size(); 1719 } finally { 1720 _workspace.doneReading(); 1721 } 1722 } 1723 1724 /** Return the number of contained relations. 1725 * This method is read-synchronized on the workspace. 1726 * @return The number of relations. 1727 */ 1728 public int numberOfRelations() { 1729 try { 1730 _workspace.getReadAccess(); 1731 return _containedRelations.size(); 1732 } finally { 1733 _workspace.doneReading(); 1734 } 1735 } 1736 1737 /** List the relations contained by this entity. 1738 * The returned list is static in the sense 1739 * that it is not affected by any subsequent additions or removals 1740 * of relations. 1741 * This method is read-synchronized on the workspace. 1742 * @return An unmodifiable list of ComponentRelation objects. 1743 */ 1744 public List relationList() { 1745 try { 1746 _workspace.getReadAccess(); 1747 1748 // Copy the list so we can create a static enumeration. 1749 NamedList relationsCopy = new NamedList(_containedRelations); 1750 return relationsCopy.elementList(); 1751 } finally { 1752 _workspace.doneReading(); 1753 } 1754 } 1755 1756 /** Remove all contained entities and unlink them from all relations. 1757 * This is done by setting their containers to null. 1758 * This method is read-synchronized on the workspace 1759 * and increments its version number. 1760 */ 1761 public void removeAllEntities() { 1762 try { 1763 _workspace.getReadAccess(); 1764 1765 Iterator entities = entityList().iterator(); 1766 1767 while (entities.hasNext()) { 1768 ComponentEntity entity = (ComponentEntity) entities.next(); 1769 1770 try { 1771 entity.setContainer(null); 1772 } catch (KernelException ex) { 1773 // This exception should not be thrown. 1774 throw new InternalErrorException(this, ex, 1775 "Internal error in CompositeEntity." 1776 + "removeAllEntities() method!"); 1777 } 1778 } 1779 } finally { 1780 _workspace.doneReading(); 1781 } 1782 } 1783 1784 /** Remove all contained relations and unlink them from everything. 1785 * This is done by setting their containers to null. 1786 * This method is write-synchronized on the workspace 1787 * and increments its version number. 1788 */ 1789 public void removeAllRelations() { 1790 try { 1791 _workspace.getWriteAccess(); 1792 1793 Iterator relations = relationList().iterator(); 1794 1795 while (relations.hasNext()) { 1796 ComponentRelation relation = (ComponentRelation) relations 1797 .next(); 1798 1799 try { 1800 relation.setContainer(null); 1801 } catch (KernelException ex) { 1802 // This exception should not be thrown. 1803 throw new InternalErrorException(this, ex, 1804 "Internal error in CompositeEntity." 1805 + "removeAllRelations() method!"); 1806 } 1807 } 1808 } finally { 1809 _workspace.doneWriting(); 1810 } 1811 } 1812 1813 /** Specify whether this object is a class definition. 1814 * If the argument is true and this entity is not a class 1815 * definition, then all level crossing relations that 1816 * This method overrides the base class to check that if the 1817 * argument is true, then this entity contains no ports with links. 1818 * This method is write synchronized on the workspace. 1819 * @param isClass True to make this object a class definition. 1820 * @exception IllegalActionException If the argument is true and 1821 * this entity contains ports with links. 1822 */ 1823 @Override 1824 public void setClassDefinition(boolean isClass) 1825 throws IllegalActionException { 1826 // The situation is that an AO class definition is not allowed to have 1827 // any connections to other things. Thus, if an instance is converted 1828 // to a class, it should be first disconnected. However, a Subscriber 1829 // has a "hidden" connection. This connection may or may not cross 1830 // the boundary of the class definition. It should only be disconnected 1831 // if it does. 1832 1833 // We need to disconnect upon invocation of 1834 // setClassDefinition(). 1835 // It does have to traverse the whole tree below the actor being 1836 // converted to a class and disconnect any level-crossing link that 1837 // traverses to outside the class definition. 1838 1839 // We also need to worry about the converse: When a class is converted 1840 // to an instance, we need to find all inside Publisher/Subscriber actors 1841 // and call _updateLinks(). (FIXME: this is not done) 1842 if (isClass && !isClassDefinition()) { 1843 try { 1844 workspace().getWriteAccess(); 1845 // Converting from an instance to a class. 1846 super.setClassDefinition(isClass); 1847 _unlinkLevelCrossingLinksToOutside(this); 1848 } finally { 1849 workspace().doneWriting(); 1850 } 1851 } else { 1852 super.setClassDefinition(isClass); 1853 } 1854 } 1855 1856 /** Override the base class so that if the argument is null, all 1857 * level-crossing links from inside this composite to outside this 1858 * composite are removed. 1859 * @param container The proposed container. 1860 * @exception IllegalActionException If the action would result in a 1861 * recursive containment structure, or if 1862 * this entity and container are not in the same workspace, or 1863 * if the protected method _checkContainer() throws it, or if 1864 * a contained Settable becomes invalid and the error handler 1865 * throws it. 1866 * @exception NameDuplicationException If the name of this entity 1867 * collides with a name already in the container. 1868 * @see #getContainer() 1869 */ 1870 @Override 1871 public void setContainer(CompositeEntity container) 1872 throws IllegalActionException, NameDuplicationException { 1873 if (container == null) { 1874 // This composite is being removed from the model. 1875 // Remove level-crossing links. 1876 try { 1877 _workspace.getWriteAccess(); 1878 _unlinkLevelCrossingLinksToOutside(this); 1879 // Findbugs reports "load of known null value" so we use null 1880 super.setContainer(null); 1881 } finally { 1882 _workspace.doneWriting(); 1883 } 1884 } else { 1885 super.setContainer(container); 1886 } 1887 } 1888 1889 /** Return a string describing how many actors, parameters, 1890 * ports, and relations are in this CompositeEntity. 1891 * Entities whose instantiation is deferred are not 1892 * included. 1893 * @param className If non-null and non-empty, then also 1894 * include the number of objects with the give name. 1895 * @return a string describing the number of components. 1896 * @exception IllegalActionException If the class named by 1897 * actorClassName cannot be found. 1898 */ 1899 public String statistics(String className) throws IllegalActionException { 1900 // FIXME: The right way to do this is to have each class 1901 // in the hierarchy have a statistics method. 1902 try { 1903 _workspace.getReadAccess(); 1904 1905 Class clazz = null; 1906 try { 1907 if (className != null && className.length() > 0) { 1908 clazz = Class.forName(className); 1909 } 1910 } catch (Exception ex) { 1911 throw new IllegalActionException(null, ex, 1912 "Failed to instantiate \"" + className + "\""); 1913 } 1914 1915 // Use the lazy version to avoid triggering a populate of LazyTypedCompositeActor. 1916 List atomicEntities = lazyAllAtomicEntityList(); 1917 int entityCount = atomicEntities.size(); 1918 1919 Map<String, Integer> actorMap = new HashMap<String, Integer>(); 1920 Integer one = Integer.valueOf(1); 1921 1922 int attributeCount = 0, entityClassCount = 0; 1923 Iterator entities = atomicEntities.iterator(); 1924 while (entities.hasNext()) { 1925 ComponentEntity entity = (ComponentEntity) entities.next(); 1926 List attributeList = entity.attributeList(); 1927 attributeCount += attributeList.size(); 1928 1929 Class entityClass = entity.getClass(); 1930 1931 // Create a map with the count of actors 1932 String entityClassName = entityClass.getName(); 1933 if (!actorMap.containsKey(entityClassName)) { 1934 actorMap.put(entityClassName, one); 1935 } else { 1936 actorMap.put(entityClassName, 1937 Integer.valueOf(actorMap.get(entityClassName) + 1)); 1938 } 1939 1940 if (clazz != null) { 1941 if (clazz.isAssignableFrom(entityClass)) { 1942 entityClassCount++; 1943 } else { 1944 // Search the attributes 1945 Iterator attributes = attributeList.iterator(); 1946 while (attributes.hasNext()) { 1947 Attribute attribute = (Attribute) attributes.next(); 1948 if (clazz.isAssignableFrom(attribute.getClass())) { 1949 entityClassCount++; 1950 } 1951 } 1952 } 1953 } 1954 } 1955 1956 ArrayList actorArrayList = new ArrayList(actorMap.entrySet()); 1957 //Sort the values based on values first and then keys. 1958 Collections.sort(actorArrayList, new CountComparator()); 1959 1960 StringBuffer actorNames = new StringBuffer(); 1961 Iterator actors = actorArrayList.iterator(); 1962 while (actors.hasNext()) { 1963 Map.Entry<String, Integer> actor = (Map.Entry) actors.next(); 1964 actorNames 1965 .append(actor.getKey() + " " + actor.getValue() + "\n"); 1966 } 1967 1968 int compositeEntityCount = 0; 1969 int opaqueCompositeEntityCount = 0; 1970 List relationList = lazyRelationList(); 1971 int relationCount = relationList.size(); 1972 if (clazz != null) { 1973 // Search the relations 1974 Iterator relations = relationList.iterator(); 1975 while (relations.hasNext()) { 1976 Relation relation = (Relation) relations.next(); 1977 if (clazz.isAssignableFrom(relation.getClass())) { 1978 entityClassCount++; 1979 } 1980 } 1981 } 1982 1983 Map<Integer, Integer> compositeEntityDepthMap = new TreeMap<Integer, Integer>(); 1984 entities = lazyAllCompositeTransparentAndOpaqueEntityList() 1985 .iterator(); 1986 1987 while (entities.hasNext()) { 1988 Entity entity = (Entity) entities.next(); 1989 if (entity instanceof CompositeEntity) { 1990 compositeEntityCount++; 1991 if (((CompositeEntity) entity).isOpaque()) { 1992 opaqueCompositeEntityCount++; 1993 } 1994 // Find the depth and add it to the list 1995 Integer depth = Integer.valueOf(entity.depthInHierarchy()); 1996 if (!compositeEntityDepthMap.containsKey(depth)) { 1997 compositeEntityDepthMap.put(depth, one); 1998 } else { 1999 compositeEntityDepthMap.put(depth, Integer.valueOf( 2000 compositeEntityDepthMap.get(depth) + 1)); 2001 } 2002 2003 relationList = ((CompositeEntity) entity) 2004 .lazyRelationList(); 2005 relationCount += relationList.size(); 2006 if (clazz != null) { 2007 if (clazz.isAssignableFrom(entity.getClass())) { 2008 entityClassCount++; 2009 } else { 2010 // Search the relations 2011 Iterator relations = relationList.iterator(); 2012 while (relations.hasNext()) { 2013 Relation relation = (Relation) relations.next(); 2014 if (clazz.isAssignableFrom( 2015 relation.getClass())) { 2016 entityClassCount++; 2017 } 2018 } 2019 } 2020 } 2021 } 2022 } 2023 2024 // Generate a string with the depths 2025 StringBuffer compositeEntityDepths = new StringBuffer(); 2026 for (Map.Entry<Integer, Integer> depth : compositeEntityDepthMap 2027 .entrySet()) { 2028 compositeEntityDepths.append("Depth: " + depth.getKey() 2029 + " # of Composites at that depth: " + depth.getValue() 2030 + "\n"); 2031 } 2032 2033 return "Size Statistics for " + getFullName() + "\nAtomicEntities: " 2034 + entityCount + "\nCompositeEntities: " 2035 + compositeEntityCount + "\nOpaqueCompositeEntities: " 2036 + opaqueCompositeEntityCount + "\nRelations: " 2037 + relationCount + "\nAttributes: " + attributeCount 2038 + (clazz == null ? "" 2039 : "\nEntities of type \"" + clazz.getName() + "\": " 2040 + entityClassCount) 2041 + "\nAtomic Actor Names and Counts:\n" + actorNames 2042 + "\nComposite Entity Depths and Counts:\n" 2043 + compositeEntityDepths; 2044 2045 } finally { 2046 _workspace.doneReading(); 2047 } 2048 } 2049 2050 /** Return a name that is guaranteed to not be the name of 2051 * any contained attribute, port, class, entity, or relation. 2052 * In this implementation, the argument 2053 * is stripped of any numeric suffix, and then a numeric suffix 2054 * is appended and incremented until a name is found that does not 2055 * conflict with a contained attribute, port, class, entity, or relation. 2056 * If this composite entity or any composite entity that it contains 2057 * defers its MoML definition (i.e., it is an instance of a class or 2058 * a subclass), then the prefix gets appended with "_<i>n</i>_", 2059 * where <i>n</i> is the depth of this deferral. That is, if the object 2060 * deferred to also defers, then <i>n</i> is incremented. 2061 * <p>Note that this method should be called judiciously from when 2062 * the CompositeEntity is large. The reason is that this method 2063 * searches for matching attributes, ports, classes, entities 2064 * and relations, which can result in slow performance. 2065 * 2066 * @param prefix A prefix for the name. 2067 * @return A unique name. 2068 */ 2069 @Override 2070 public String uniqueName(String prefix) { 2071 if (prefix == null) { 2072 prefix = "null"; 2073 } 2074 2075 prefix = _stripNumericSuffix(prefix); 2076 2077 String candidate = prefix; 2078 2079 // NOTE: The list returned by getPrototypeList() has 2080 // length equal to the number of containers of this object 2081 // that return non-null to getParent(). That number is 2082 // assured to be at least one greater than the corresponding 2083 // number for any of the parents returned by getParent(). 2084 // Hence, we can use that number to minimize the likelyhood 2085 // of inadvertent capture. 2086 try { 2087 int depth = getPrototypeList().size(); 2088 2089 if (depth > 0) { 2090 prefix = prefix + "_" + depth + "_"; 2091 } 2092 } catch (IllegalActionException e) { 2093 // Derivation invariant is not satisified. 2094 throw new InternalErrorException(e); 2095 } 2096 2097 // FIXME: because we start with 2 each time, then if 2098 // we are calling this method many times we will need 2099 // to search the CompositeEntity for matching 2100 // attributes, ports, entities and releations. 2101 // This will have poor behaviour for large CompositeEntities. 2102 // However, if we cached the uniqueNameIndex, then 2103 // it would tend to increase over time, which would be 2104 // unusual if we created a relation (_R2), deleted it 2105 // and created another relation, which would get the name 2106 // _R3, instead of _R2. 2107 2108 int uniqueNameIndex = 2; 2109 2110 while (getAttribute(candidate) != null || getPort(candidate) != null 2111 || getEntity(candidate) != null 2112 || getRelation(candidate) != null) { 2113 candidate = prefix + uniqueNameIndex++; 2114 } 2115 2116 return candidate; 2117 } 2118 2119 /////////////////////////////////////////////////////////////////// 2120 //// protected methods //// 2121 2122 /** Add an entity or class definition to this container. This method 2123 * should not be used directly. Call the setContainer() method of 2124 * the entity instead. This method does not set 2125 * the container of the entity to point to this composite entity. 2126 * It assumes that the entity is in the same workspace as this 2127 * container, but does not check. The caller should check. 2128 * Derived classes may override this method to constrain the 2129 * the entity to a subclass of ComponentEntity. 2130 * This method is <i>not</i> synchronized on the workspace, so the 2131 * caller should be. 2132 * @param entity Entity to contain. 2133 * @exception IllegalActionException If the entity has no name, or the 2134 * action would result in a recursive containment structure. 2135 * @exception NameDuplicationException If the name collides with a name 2136 * already in the entity. 2137 */ 2138 protected void _addEntity(ComponentEntity entity) 2139 throws IllegalActionException, NameDuplicationException { 2140 if (entity.deepContains(this)) { 2141 throw new IllegalActionException(entity, this, 2142 "Attempt to construct recursive containment"); 2143 } 2144 2145 _containedEntities.append(entity); 2146 } 2147 2148 /** Add a relation to this container. This method should not be used 2149 * directly. Call the setContainer() method of the relation instead. 2150 * This method does not set 2151 * the container of the relation to refer to this container. 2152 * This method is <i>not</i> synchronized on the workspace, so the 2153 * caller should be. 2154 * @param relation Relation to contain. 2155 * @exception IllegalActionException If the relation has no name. 2156 * @exception NameDuplicationException If the name collides with a name 2157 * already on the contained relations list. 2158 */ 2159 protected void _addRelation(ComponentRelation relation) 2160 throws IllegalActionException, NameDuplicationException { 2161 _containedRelations.append(relation); 2162 } 2163 2164 /** Adjust the deferrals in this object. This method should 2165 * be called on any newly created object that is created by 2166 * cloning. While cloning, parent relations are set to null. 2167 * That is, no object in the clone has a parent. This method 2168 * identifies the correct parent for any object in the clone. 2169 * To do this, it uses the class name. Specifically, if this 2170 * object has a class name that refers to a class in scope, 2171 * then it replaces the current parent with that object. 2172 * To look for a class in scope, we go up the hierarchy, but 2173 * no more times than the return value of getDerivedLevel(). 2174 * The reason for this is that if the class from which this 2175 * object is defined is above that level, then we do not want 2176 * to establish a parent relationship with that class. This 2177 * object is implied, and the parent relationship of the object 2178 * from which it is implied is sufficient. 2179 * <p> 2180 * Derived classes that contain other objects should recursively 2181 * call this method on contained objects. 2182 * @exception IllegalActionException If the class found in scope 2183 * cannot be set. 2184 */ 2185 @Override 2186 protected void _adjustDeferrals() throws IllegalActionException { 2187 super._adjustDeferrals(); 2188 2189 Iterator containedClasses = lazyClassDefinitionList().iterator(); 2190 2191 while (containedClasses.hasNext()) { 2192 NamedObj containedObject = (NamedObj) containedClasses.next(); 2193 2194 if (containedObject instanceof ComponentEntity) { 2195 ((ComponentEntity) containedObject)._adjustDeferrals(); 2196 } 2197 } 2198 2199 Iterator containedEntities = lazyEntityList().iterator(); 2200 2201 while (containedEntities.hasNext()) { 2202 NamedObj containedObject = (NamedObj) containedEntities.next(); 2203 2204 if (containedObject instanceof ComponentEntity) { 2205 ((ComponentEntity) containedObject)._adjustDeferrals(); 2206 } 2207 } 2208 } 2209 2210 /** Return a list of decorators contained by this object. 2211 * This overrides the base class to include not only attributes that 2212 * implement the {@link Decorator} interface, but also entities. 2213 * @return A list of contained decorators. 2214 */ 2215 @Override 2216 protected List<Decorator> _containedDecorators() { 2217 List<Decorator> result = super._containedDecorators(); 2218 result.addAll(entityList(Decorator.class)); 2219 return result; 2220 } 2221 2222 /** List the opaque entities that are directly or indirectly 2223 * contained by this entity. The list will be empty if there 2224 * are no such contained entities. This list does not include 2225 * class definitions nor anything contained by them. 2226 * This method is <b>not</b> read-synchronized on the workspace, 2227 * its caller should be read-synchronized. 2228 * @param result The list of opaque ComponentEntity objects. 2229 * @see #classDefinitionList() 2230 * @see #allAtomicEntityList() 2231 * @see #deepEntityList() 2232 */ 2233 protected void _deepOpaqueEntityList(List result) { 2234 2235 // This might be called from within a superclass constructor, 2236 // in which case there are no contained entities yet. 2237 if (_containedEntities != null) { 2238 Iterator entities = _containedEntities.elementList().iterator(); 2239 2240 while (entities.hasNext()) { 2241 ComponentEntity entity = (ComponentEntity) entities.next(); 2242 if (!entity.isClassDefinition()) { 2243 if (entity.isOpaque()) { 2244 result.add(entity); 2245 } else { 2246 ((CompositeEntity) entity) 2247 ._deepOpaqueEntityList(result); 2248 } 2249 } 2250 } 2251 } 2252 } 2253 2254 /** Return a description of the object. The level of detail depends 2255 * on the argument, which is an or-ing of the static final constants 2256 * defined in the NamedObj class. Lines are indented according to 2257 * to the level argument using the protected method _getIndentPrefix(). 2258 * Zero, one or two brackets can be specified to surround the returned 2259 * description. If one is specified it is the the leading bracket. 2260 * This is used by derived classes that will append to the description. 2261 * Those derived classes are responsible for the closing bracket. 2262 * An argument other than 0, 1, or 2 is taken to be equivalent to 0. 2263 * This method is read-synchronized on the workspace. 2264 * @param detail The level of detail. 2265 * @param indent The amount of indenting. 2266 * @param bracket The number of surrounding brackets (0, 1, or 2). 2267 * @return A description of the object. 2268 * @exception IllegalActionException If thrown while getting the 2269 * description of subcomponents. 2270 */ 2271 @Override 2272 protected String _description(int detail, int indent, int bracket) 2273 throws IllegalActionException { 2274 try { 2275 _workspace.getReadAccess(); 2276 2277 StringBuffer result = new StringBuffer(); 2278 2279 if (bracket == 1 || bracket == 2) { 2280 result.append(super._description(detail, indent, 1)); 2281 } else { 2282 result.append(super._description(detail, indent, 0)); 2283 } 2284 2285 if ((detail & CONTENTS) != 0) { 2286 if (result.toString().trim().length() > 0) { 2287 result.append(" "); 2288 } 2289 2290 result.append("classes {\n"); 2291 2292 Iterator classes = classDefinitionList().iterator(); 2293 2294 while (classes.hasNext()) { 2295 ComponentEntity entity = (ComponentEntity) classes.next(); 2296 result.append( 2297 entity._description(detail, indent + 1, 2) + "\n"); 2298 } 2299 2300 result.append(_getIndentPrefix(indent) + "} entities {\n"); 2301 2302 Iterator entities = entityList().iterator(); 2303 2304 while (entities.hasNext()) { 2305 ComponentEntity entity = (ComponentEntity) entities.next(); 2306 result.append( 2307 entity._description(detail, indent + 1, 2) + "\n"); 2308 } 2309 2310 result.append(_getIndentPrefix(indent) + "} relations {\n"); 2311 2312 Iterator relations = relationList().iterator(); 2313 2314 while (relations.hasNext()) { 2315 Relation relation = (Relation) relations.next(); 2316 result.append(relation._description(detail, indent + 1, 2) 2317 + "\n"); 2318 } 2319 2320 result.append(_getIndentPrefix(indent) + "}"); 2321 } 2322 2323 if (bracket == 2) { 2324 result.append("}"); 2325 } 2326 2327 return result.toString(); 2328 } finally { 2329 _workspace.doneReading(); 2330 } 2331 } 2332 2333 /** Write a MoML description of the contents of this object, which 2334 * in this class are the attributes, ports, contained relations, 2335 * and contained entities, plus all links. The links are written 2336 * in an order that respects the ordering in ports, but not necessarily 2337 * the ordering in relations. This method is called 2338 * by exportMoML(). Each description is indented according to the 2339 * specified depth and terminated with a newline character. 2340 * @param output The output to write to. 2341 * @param depth The depth in the hierarchy, to determine indenting. 2342 * @exception IOException If an I/O error occurs. 2343 */ 2344 @Override 2345 protected void _exportMoMLContents(Writer output, int depth) 2346 throws IOException { 2347 if (depth == 1 && getContainer() == null) { 2348 if (getAttribute("_createdBy") == null) { 2349 // If there is no _createdBy attribute, then add one. 2350 output.write(_getIndentPrefix(depth) 2351 + "<property name=\"_createdBy\" " + "class=\"" 2352 + VersionAttribute.CURRENT_VERSION.getClass().getName() 2353 + "\" value=\"" 2354 + VersionAttribute.CURRENT_VERSION.getExpression() 2355 + "\">\n"); 2356 output.write(_getIndentPrefix(depth) + "</property>\n"); 2357 } else if (getAttribute("_createdBy") != null) { 2358 try { 2359 ((VersionAttribute) getAttribute("_createdBy")) 2360 .setExpression(VersionAttribute.CURRENT_VERSION 2361 .getExpression()); 2362 } catch (IllegalActionException ex) { 2363 throw new InternalErrorException(this, ex, 2364 "Failed to update _createdBy"); 2365 } 2366 } 2367 } 2368 2369 super._exportMoMLContents(output, depth); 2370 2371 Iterator classes = classDefinitionList().iterator(); 2372 2373 while (classes.hasNext()) { 2374 ComponentEntity entity = (ComponentEntity) classes.next(); 2375 entity.exportMoML(output, depth); 2376 } 2377 2378 Iterator entities = entityList().iterator(); 2379 2380 while (entities.hasNext()) { 2381 ComponentEntity entity = (ComponentEntity) entities.next(); 2382 entity.exportMoML(output, depth); 2383 } 2384 2385 Iterator relations = relationList().iterator(); 2386 2387 while (relations.hasNext()) { 2388 ComponentRelation relation = (ComponentRelation) relations.next(); 2389 relation.exportMoML(output, depth); 2390 } 2391 2392 // NOTE: We used to write the links only if 2393 // this object did not defer to another 2394 // (getMoMLInfo().deferTo was null), and 2395 // would instead record links in a MoMLAttribute. 2396 // That mechanism was far too fragile. 2397 // EAL 3/10/04 2398 output.write(exportLinks(depth, null)); 2399 2400 // Export level crossing links, if there are any. 2401 if (_levelCrossingLinks != null) { 2402 for (LinkRecord record : _levelCrossingLinks) { 2403 if (record.port != null) { 2404 // Do not export if the relation and port are derived and 2405 // share a common container that has the parent-child 2406 // relation that implies them. 2407 if (!_commonImplier(record.relation1, 2408 depth + _depthInside(record.relation1), record.port, 2409 depth + _depthInside(record.port))) { 2410 2411 // Escape any < character in name. unescapeForXML occurs in 2412 // setName(String). 2413 String escapedRecordPortName = StringUtilities 2414 .escapeForXML(record.port.getName(this)); 2415 String escapedRecordRelation1Name = StringUtilities 2416 .escapeForXML(record.relation1.getName(this)); 2417 output.write(_getIndentPrefix(depth) + "<link port=\"" 2418 + escapedRecordPortName + "\" insertAt=\"" 2419 + record.index + "\" relation=\"" 2420 + escapedRecordRelation1Name + "\"/>\n"); 2421 } 2422 } else { 2423 // Do not export if both relations are derived and 2424 // share a common container that has the parent-child 2425 // relation that implies them. 2426 if (!_commonImplier(record.relation1, 2427 depth + _depthInside(record.relation1), 2428 record.relation2, 2429 depth + _depthInside(record.relation2))) { 2430 2431 String escapedRecordRelation1Name = StringUtilities 2432 .escapeForXML(record.relation1.getName(this)); 2433 String escapedRecordRelation2Name = StringUtilities 2434 .escapeForXML(record.relation2.getName(this)); 2435 2436 output.write(_getIndentPrefix(depth) 2437 + "<link relation1=\"" 2438 + escapedRecordRelation1Name + "\" relation2=\"" 2439 + escapedRecordRelation2Name + "\"/>\n"); 2440 } 2441 } 2442 } 2443 } 2444 } 2445 2446 /** Notify this entity that the given entity has been added inside it. 2447 * This base class does nothing. Derived classes may override it to 2448 * do something useful in responds to the notification. 2449 * It is <i>not</i> synchronized on the workspace, so the 2450 * caller should be. 2451 * 2452 * @param entity The contained entity. 2453 */ 2454 protected void _finishedAddEntity(ComponentEntity entity) { 2455 } 2456 2457 /** Remove the specified entity. This method should not be used 2458 * directly. Call the setContainer() method of the entity instead with 2459 * a null argument. 2460 * The entity is assumed to be contained by this composite (otherwise, 2461 * nothing happens). This does not alter the entity in any way. 2462 * This method is <i>not</i> synchronized on the workspace, so the 2463 * caller should be. 2464 * @param entity The entity to remove. 2465 */ 2466 protected void _removeEntity(ComponentEntity entity) { 2467 _containedEntities.remove(entity); 2468 } 2469 2470 /** Remove the specified relation. This method should not be used 2471 * directly. Call the setContainer() method of the relation instead with 2472 * a null argument. 2473 * The relation is assumed to be contained by this composite (otherwise, 2474 * nothing happens). This does not alter the relation in any way. 2475 * This method is <i>not</i> synchronized on the workspace, so the 2476 * caller should be. 2477 * @param relation The relation to remove. 2478 */ 2479 protected void _removeRelation(ComponentRelation relation) { 2480 _containedRelations.remove(relation); 2481 } 2482 2483 /** Validate attributes deeply contained by this object if they 2484 * implement the Settable interface by calling their validate() method. 2485 * This method overrides the base class to check attributes contained 2486 * by the contained entities and relations. 2487 * Errors that are triggered by this validation are handled by calling 2488 * handleModelError(). 2489 * @param attributesValidated A HashSet of Attributes that have 2490 * already been validated. For example, Settables that implement 2491 * the SharedSettable interface are validated only once. 2492 * @see ptolemy.kernel.util.NamedObj#handleModelError(NamedObj, IllegalActionException) 2493 * @exception IllegalActionException If the superclass throws it 2494 * or if handleModelError() throws it. 2495 */ 2496 @Override 2497 protected void _validateSettables(Collection attributesValidated) 2498 throws IllegalActionException { 2499 super._validateSettables(attributesValidated); 2500 Iterator classes = classDefinitionList().iterator(); 2501 while (classes.hasNext()) { 2502 Entity entity = (Entity) classes.next(); 2503 if (entity instanceof Settable) { 2504 try { 2505 Collection validated = ((Settable) entity).validate(); 2506 if (validated != null) { 2507 attributesValidated.addAll(validated); 2508 } 2509 attributesValidated.add(entity); 2510 } catch (IllegalActionException ex) { 2511 if (!handleModelError(this, ex)) { 2512 throw ex; 2513 } 2514 } 2515 } 2516 entity._validateSettables(attributesValidated); 2517 } 2518 2519 Iterator entities = entityList().iterator(); 2520 while (entities.hasNext()) { 2521 Entity entity = (Entity) entities.next(); 2522 if (entity instanceof Settable) { 2523 try { 2524 Collection validated = ((Settable) entity).validate(); 2525 if (validated != null) { 2526 attributesValidated.addAll(validated); 2527 } 2528 attributesValidated.add(entity); 2529 } catch (IllegalActionException ex) { 2530 if (!handleModelError(this, ex)) { 2531 throw ex; 2532 } 2533 } 2534 } 2535 entity._validateSettables(attributesValidated); 2536 } 2537 2538 Iterator relations = relationList().iterator(); 2539 while (relations.hasNext()) { 2540 Relation relation = (Relation) relations.next(); 2541 if (relation instanceof Settable) { 2542 try { 2543 Collection validated = ((Settable) relation).validate(); 2544 if (validated != null) { 2545 attributesValidated.addAll(validated); 2546 } 2547 attributesValidated.add(relation); 2548 } catch (IllegalActionException ex) { 2549 if (!handleModelError(this, ex)) { 2550 throw ex; 2551 } 2552 } 2553 } 2554 relation.validateSettables(); 2555 } 2556 } 2557 2558 /////////////////////////////////////////////////////////////////// 2559 //// protected variables //// 2560 2561 /** Level-crossing links within this composite for which this composite 2562 * is responsible. This data structure is populated when exportMoML() 2563 * is called. 2564 */ 2565 protected List<LinkRecord> _levelCrossingLinks; 2566 2567 /////////////////////////////////////////////////////////////////// 2568 //// private methods //// 2569 2570 /** 2571 * Add all elements from the sourceList into the targetList. 2572 */ 2573 @SuppressWarnings("unchecked") 2574 private static <T> void _addAll(Set<T> result, Collection<?> sourceList) { 2575 for (Object object : sourceList) { 2576 result.add((T) object); 2577 } 2578 } 2579 2580 /** Add a default icon description. */ 2581 private void _addIcon() { 2582 _attachText("_iconDescription", _defaultIcon); 2583 } 2584 2585 /** Find the least common container of the two objects. 2586 * @param object1 The first object. 2587 * @param object2 The second object. 2588 * @return The least common container, or null if there 2589 * isn't one. 2590 */ 2591 private NamedObj _commonContainer(NamedObj object1, NamedObj object2) { 2592 NamedObj container = object1.getContainer(); 2593 while (container != null) { 2594 if (container.deepContains(object2)) { 2595 return container; 2596 } 2597 container = container.getContainer(); 2598 } 2599 return null; 2600 } 2601 2602 /** Return true if the two specified objects are both derived, 2603 * it is the same container above them whose parent-child 2604 * relationship makes them derived, or if one of the containers 2605 * contains the other, and those containers are 2606 * no more than <i>depth1</i> and <i>depth2</i> above them, 2607 * respectively. 2608 * @param object1 The first object. 2609 * @param depth1 The depth of the first object. 2610 * @param object2 The second object. 2611 * @param depth2 The depth of the second object. 2612 * @return True if the two specified objects are both derived 2613 * at a common level no more than depth above them. 2614 */ 2615 private boolean _commonImplier(NamedObj object1, int depth1, 2616 NamedObj object2, int depth2) { 2617 if (object1 == null || object2 == null) { 2618 return false; 2619 } 2620 int object1Level = object1.getDerivedLevel(); 2621 int object2Level = object2.getDerivedLevel(); 2622 if (object1Level <= depth1 && object2Level <= depth2) { 2623 NamedObj object1Container = object1; 2624 while (object1Level > 0) { 2625 object1Container = object1Container.getContainer(); 2626 object1Level--; 2627 // It's not clear to me how this occur, but if relationCaontiner 2628 // is null, then clearly there is no common container that 2629 // implies the two objects. 2630 if (object1Container == null) { 2631 return false; 2632 } 2633 } 2634 NamedObj object2Container = object2; 2635 while (object2Level > 0) { 2636 object2Container = object2Container.getContainer(); 2637 object2Level--; 2638 // It's not clear to me how this occur, but if relationCaontiner 2639 // is null, then clearly there is no common container that 2640 // implies the two objects. 2641 if (object2Container == null) { 2642 return false; 2643 } 2644 } 2645 if (object1Container == object2Container 2646 || object1Container.deepContains(object2Container) 2647 || object2Container.deepContains(object1Container)) { 2648 return true; 2649 } 2650 } 2651 return false; 2652 } 2653 2654 /** Return the depth of specified object inside this. 2655 * That is, return 0 if the specified object is this, 1 if this 2656 * contains it, 2 if this contains the container 2657 * of it, etc. 2658 * @param containee The object contained. 2659 * @return The depth of the containment, or -1 if the specified object 2660 * is not deeply contained by this. 2661 */ 2662 private int _depthInside(NamedObj containee) { 2663 int result = 0; 2664 NamedObj candidate = containee; 2665 while (candidate != null) { 2666 if (candidate == this) { 2667 return result; 2668 } 2669 result++; 2670 candidate = candidate.getContainer(); 2671 } 2672 return -1; 2673 } 2674 2675 /** Record a level-crossing link with the least common container if there 2676 * is such a least common container and if that least common container is 2677 * currently exporting MoML. Otherwise, do nothing. 2678 * @param port The port, or null for a link between relations. 2679 * @param relation1 The first relation. 2680 * @param relation2 The second relation. 2681 * @param index The index of the link. 2682 */ 2683 private void _recordLevelCrossingLink(Port port, Relation relation1, 2684 Relation relation2, int index) { 2685 // Find the least common container. 2686 NamedObj container; 2687 if (port != null) { 2688 container = _commonContainer(port, relation1); 2689 } else { 2690 // This is a link between relations. 2691 // Find the common container. 2692 container = _commonContainer(relation1, relation2); 2693 // We have to make sure 2694 // that the link only appears once in the export MoML. 2695 // To do this, check to see whether the common container 2696 // already contains a link record with relation2 in the 2697 // position of relation1. 2698 if (container instanceof CompositeEntity) { 2699 List<LinkRecord> linkRecords = ((CompositeEntity) container)._levelCrossingLinks; 2700 if (linkRecords != null) { 2701 for (LinkRecord record : linkRecords) { 2702 if (record.relation1 == relation2) { 2703 return; 2704 } 2705 } 2706 } 2707 } 2708 } 2709 if (container instanceof CompositeEntity) { 2710 List<LinkRecord> linkRecords = ((CompositeEntity) container)._levelCrossingLinks; 2711 // If the common container is outside the scope of what is being exported, 2712 // then linkRecords will be null. 2713 if (linkRecords != null) { 2714 LinkRecord record = new LinkRecord(); 2715 record.port = port; 2716 record.relation1 = relation1; 2717 record.relation2 = relation2; 2718 record.index = index; 2719 linkRecords.add(record); 2720 } 2721 } 2722 } 2723 2724 /** Remove all level-crossing links from relations contained by 2725 * the specified entity to ports or relations outside this 2726 * composite entity, and from ports contained by entities 2727 * contained by the specified entity to relations outside this 2728 * composite entity. 2729 * @param entity The entity in which to look for relations or 2730 * (if it is an instance of CompositeEntity), entities with ports. 2731 */ 2732 private void _unlinkLevelCrossingLinksToOutside(CompositeEntity entity) { 2733 // Look for relations with level crossing links first. 2734 // Here we use the lazy version so as to not trigger evaluation 2735 // of lazy contents. We assume that if and when those contents 2736 // are evaluated, if there is a level-crossing list, it will 2737 // trigger an error. 2738 Iterator relations = entity.lazyRelationList().iterator(); 2739 while (relations.hasNext()) { 2740 ComponentRelation relation = (ComponentRelation) relations.next(); 2741 Iterator linkedObjects = relation.linkedObjectsList().iterator(); 2742 while (linkedObjects.hasNext()) { 2743 Object linkedObject = linkedObjects.next(); 2744 2745 Nameable relationContainer = relation.getContainer(); 2746 if (linkedObject instanceof Relation) { 2747 2748 Relation linkedRelation = (Relation) linkedObject; 2749 Nameable linkedObjectContainer = linkedRelation 2750 .getContainer(); 2751 if (relationContainer != linkedObjectContainer 2752 && linkedObjectContainer 2753 .getContainer() != relationContainer) { 2754 relation.unlink(linkedRelation); 2755 } 2756 } else { 2757 // Must be a port. 2758 Port linkedPort = (Port) linkedObject; 2759 Nameable linkedObjectContainer = linkedPort.getContainer(); 2760 if (relationContainer != linkedObjectContainer 2761 && linkedObjectContainer 2762 .getContainer() != relationContainer) { 2763 linkedPort.unlink(relation); 2764 } 2765 } 2766 } 2767 } 2768 // Next look for ports with level-crossing links. 2769 Iterator entities = entity.entityList().iterator(); 2770 while (entities.hasNext()) { 2771 ComponentEntity containedEntity = (ComponentEntity) entities.next(); 2772 // If the contained entity is a composite entity, then unlink 2773 // anything inside it as well. 2774 if (containedEntity instanceof CompositeEntity) { 2775 _unlinkLevelCrossingLinksToOutside( 2776 (CompositeEntity) containedEntity); 2777 } 2778 // Now unlink its ports. 2779 Iterator ports = containedEntity.portList().iterator(); 2780 while (ports.hasNext()) { 2781 ComponentPort port = (ComponentPort) ports.next(); 2782 Iterator linkedRelations = port.linkedRelationList().iterator(); 2783 while (linkedRelations.hasNext()) { 2784 ComponentRelation relation = (ComponentRelation) linkedRelations 2785 .next(); 2786 if (relation != null && !deepContains(relation)) { 2787 port.unlink(relation); 2788 } 2789 } 2790 } 2791 } 2792 } 2793 2794 /////////////////////////////////////////////////////////////////// 2795 //// friendly variables //// 2796 // The following are friendly to support the move* methods of 2797 // Relation and ComponentEntity. 2798 2799 /** List of contained entities. */ 2800 NamedList _containedEntities = new NamedList(this); 2801 2802 /** @serial List of contained ports. */ 2803 NamedList _containedRelations = new NamedList(this); 2804 2805 /////////////////////////////////////////////////////////////////// 2806 //// private variables //// 2807 2808 /** Cache of class definition list. */ 2809 private transient List _classDefinitionListCache; 2810 2811 /** Workspace version for cache. */ 2812 private transient long _classDefinitionListVersion = -1L; 2813 2814 /** The default value icon. This is static so that we avoid doing 2815 * string concatenation each time we construct this object. 2816 */ 2817 private static String _defaultIcon = "<svg>\n" 2818 + "<rect x=\"-30\" y=\"-20\" width=\"60\" " 2819 + "height=\"40\" style=\"fill:red\"/>\n" 2820 + "<rect x=\"-28\" y=\"-18\" width=\"56\" " 2821 + "height=\"36\" style=\"fill:lightgrey\"/>\n" 2822 + "<rect x=\"-15\" y=\"-10\" width=\"10\" height=\"8\" " 2823 + "style=\"fill:white\"/>\n" 2824 + "<rect x=\"-15\" y=\"2\" width=\"10\" height=\"8\" " 2825 + "style=\"fill:white\"/>\n" 2826 + "<rect x=\"5\" y=\"-4\" width=\"10\" height=\"8\" " 2827 + "style=\"fill:white\"/>\n" 2828 + "<line x1=\"-5\" y1=\"-6\" x2=\"0\" y2=\"-6\"/>" 2829 + "<line x1=\"-5\" y1=\"6\" x2=\"0\" y2=\"6\"/>" 2830 + "<line x1=\"0\" y1=\"-6\" x2=\"0\" y2=\"6\"/>" 2831 + "<line x1=\"0\" y1=\"0\" x2=\"5\" y2=\"0\"/>" + "</svg>\n"; 2832 2833 /** Cache of entity list. */ 2834 private transient WeakReference<List> _entityListCache; 2835 2836 /** Workspace version for cache. */ 2837 private transient long _entityListVersion = -1L; 2838 2839 /** @serial Flag indicating whether level-crossing connect is permitted. */ 2840 private boolean _levelCrossingConnectAllowed = false; 2841 2842 /////////////////////////////////////////////////////////////////// 2843 //// inner classes //// 2844 2845 /** This class is an iterator over all the contained objects 2846 * (all instances of NamedObj). In this class, the contained 2847 * objects are attributes first, then ports, then entities, 2848 * then relations. 2849 */ 2850 protected class ContainedObjectsIterator 2851 extends Entity.ContainedObjectsIterator { 2852 /** Create an iterator over all the contained objects, which 2853 * for CompositeEntities are attributes, ports, classes 2854 * entities, and relations. 2855 */ 2856 public ContainedObjectsIterator() { 2857 super(); 2858 _classListIterator = classDefinitionList().iterator(); 2859 _entityListIterator = entityList().iterator(); 2860 _relationListIterator = relationList().iterator(); 2861 } 2862 2863 /** Return true if the iteration has more elements. 2864 * In this class, this returns true if there are more 2865 * attributes, ports, classes, entities, or relations. 2866 * @return True if there are more elements. 2867 */ 2868 @Override 2869 public boolean hasNext() { 2870 if (super.hasNext()) { 2871 return true; 2872 } 2873 if (_classListIterator.hasNext()) { 2874 return true; 2875 } 2876 if (_entityListIterator.hasNext()) { 2877 return true; 2878 } 2879 return _relationListIterator.hasNext(); 2880 } 2881 2882 /** Return the next element in the iteration. 2883 * In this base class, this is the next attribute or port. 2884 * @return The next attribute or port. 2885 */ 2886 @Override 2887 public Object next() { 2888 if (super.hasNext()) { 2889 return super.next(); 2890 } 2891 2892 if (_classListIterator.hasNext()) { 2893 return _classListIterator.next(); 2894 } 2895 2896 if (_entityListIterator.hasNext()) { 2897 return _entityListIterator.next(); 2898 } 2899 2900 return _relationListIterator.next(); 2901 } 2902 2903 /** The remove() method is not supported because is is not 2904 * supported in NamedObj.ContainedObjectsIterator.remove(). 2905 */ 2906 @Override 2907 public void remove() { 2908 super.remove(); 2909 } 2910 2911 private Iterator _classListIterator = null; 2912 2913 private Iterator _entityListIterator = null; 2914 2915 private Iterator _relationListIterator = null; 2916 } 2917 2918 /** A comparator for a <String><Integer> Map. */ 2919 private static class CountComparator implements Comparator { 2920 @Override 2921 public int compare(Object object1, Object object2) { 2922 int result = 0; 2923 Map.Entry entry1 = (Map.Entry) object1; 2924 Map.Entry entry2 = (Map.Entry) object2; 2925 2926 Integer value1 = (Integer) entry1.getValue(); 2927 Integer value2 = (Integer) entry2.getValue(); 2928 2929 if (value1.compareTo(value2) == 0) { 2930 String className1 = (String) entry1.getKey(); 2931 String className2 = (String) entry2.getKey(); 2932 result = className1.compareTo(className2); 2933 } else { 2934 result = value2.compareTo(value1); 2935 } 2936 2937 return result; 2938 } 2939 } 2940 2941 /** A data structure for level-crossing links. */ 2942 private static class LinkRecord { 2943 public Port port; 2944 public Relation relation1; 2945 public Relation relation2; 2946 public int index; 2947 } 2948}