001/* A port supporting clustered graphs. 002 003 Copyright (c) 1997-2018 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.util.Collections; 031import java.util.Enumeration; 032import java.util.Iterator; 033import java.util.LinkedList; 034import java.util.List; 035 036import ptolemy.kernel.util.CrossRefList; 037import ptolemy.kernel.util.IllegalActionException; 038import ptolemy.kernel.util.InvalidStateException; 039import ptolemy.kernel.util.NameDuplicationException; 040import ptolemy.kernel.util.Nameable; 041import ptolemy.kernel.util.NamedObj; 042import ptolemy.kernel.util.Workspace; 043 044/////////////////////////////////////////////////////////////////// 045//// ComponentPort 046 047/** 048 A port supporting hierarchy. A component port can have "inside" 049 links as well as the usual "outside" links supported by the base 050 class. That is, while the basic port has only links to relations 051 that are on the exterior of its containing entity, this port can have 052 links to relations on the interior. 053 An inside link is a link to a relation that is contained by the 054 container of the port. 055 <p> 056 A ComponentPort may be transparent or opaque. If it is transparent, 057 then "deep" accesses of the topology see through the port. 058 Methods that read the topology come in two versions, shallow and deep. 059 The deep versions pass through transparent ports. This is 060 done with a simple rule. If a transparent port is encountered from 061 inside, then the traversal continues with its outside links. If it 062 is encountered from outside, then the traversal continues with its 063 inside links. A ComponentPort is opaque if its container is opaque. 064 (its isOpaque() method returns true). Derived classes may use other 065 strategies to specify whether a port is opaque. 066 <p> 067 Normally, links to a transparent port from the outside are to 068 relations contained by the container of the container of the port. 069 Links from the inside are to relations contained by the container 070 of the port. That is, levels of the hierarchy are not crossed. 071 For a few applications, links that cross levels of the hierarchy 072 are needed. The links in these connections are created 073 using the liberalLink() method. The link() method 074 prohibits such links, throwing an exception if they are attempted 075 (most applications will prohibit level-crossing connections by using 076 only the link() method). 077 <p> 078 A ComponentPort can link to any instance of ComponentRelation. 079 An attempt to link to an instance of Relation will trigger an exception. 080 Derived classes may wish to further constrain links to a subclass 081 of ComponentRelation. To do this, subclasses should override the 082 protected methods _checkLink() and _checkLiberalLink() to throw an exception 083 if their arguments are relations that are not of the appropriate 084 subclass. Similarly, a ComponentPort can only be contained by a 085 ComponentEntity, and an attempt to set the container to an instance 086 of Entity will trigger an exception. If a subclass wishes to 087 constrain the containers of the port to be of a subclass of 088 ComponentEntity, they should override _checkContainer(). 089 090 @author Edward A. Lee, Xiaojun Liu 091 @version $Id$ 092 @since Ptolemy II 0.2 093 @Pt.ProposedRating Green (eal) 094 @Pt.AcceptedRating Green (bart) 095 */ 096public class ComponentPort extends Port { 097 /** Construct a port in the default workspace with an empty string 098 * as its name. Increment the version number of the workspace. 099 * The object is added to the workspace directory. 100 */ 101 public ComponentPort() { 102 super(); 103 } 104 105 /** Construct a port in the specified workspace with an empty 106 * string as a name. You can then change the name with setName(). 107 * If the workspace argument is null, then use the default workspace. 108 * The object is added to the workspace directory. 109 * Increment the version number of the workspace. 110 * @param workspace The workspace that will list the port. 111 */ 112 public ComponentPort(Workspace workspace) { 113 super(workspace); 114 } 115 116 /** Construct a port with the given name contained by the specified 117 * entity. The container argument must not be null, or a 118 * NullPointerException will be thrown. This port will use the 119 * workspace of the container for synchronization and version counts. 120 * If the name argument is null, then the name is set to the empty 121 * string. Increment the version of the workspace. 122 * @param container The container entity. 123 * @param name The name of the port. 124 * @exception IllegalActionException If the port is not of an acceptable 125 * class for the container. 126 * @exception NameDuplicationException If the name coincides with 127 * a port already in the container. 128 */ 129 public ComponentPort(ComponentEntity container, String name) 130 throws IllegalActionException, NameDuplicationException { 131 super(container, name); 132 } 133 134 /////////////////////////////////////////////////////////////////// 135 //// public methods //// 136 137 /** Clone the object into the specified workspace. The new object is 138 * <i>not</i> added to the directory of that workspace (you must do this 139 * yourself if you want it there). 140 * The result is a new port with no connections and no container. 141 * @param workspace The workspace for the cloned object. 142 * @exception CloneNotSupportedException If one or more of the 143 * attributes cannot be cloned. 144 * @return A new ComponentPort. 145 */ 146 @Override 147 public Object clone(Workspace workspace) throws CloneNotSupportedException { 148 ComponentPort newObject = (ComponentPort) super.clone(workspace); 149 newObject._insideLinks = new CrossRefList(newObject); 150 newObject._deepLinkedInPortsVersion = -1L; 151 newObject._deepLinkedPortsVersion = -1L; 152 newObject._isOpaqueVersion = -1L; 153 return newObject; 154 } 155 156 /** Deeply list the ports connected to this port on the outside. 157 * Begin by listing the ports that are connected to this port. 158 * If any of those are transparent ports that we are connected to 159 * from the inside, then list all the ports deeply connected 160 * on the outside to that transparent port. If any are transparent 161 * ports that we are connected to from the outside, then list 162 * opaque ports deeply inside that port. Note that a port may 163 * be listed more than once. This method is read synchronized on the 164 * workspace. 165 * @return An unmodifiable list of ComponentPort objects. 166 */ 167 public List deepConnectedPortList() { 168 try { 169 _workspace.getReadAccess(); 170 return _deepConnectedPortList(null); 171 } finally { 172 _workspace.doneReading(); 173 } 174 } 175 176 /** Deeply enumerate the ports connected to this port on the outside. 177 * Begin by enumerating the ports that are connected to this port. 178 * If any of those are transparent ports that we are connected to 179 * from the inside, then enumerate all the ports deeply connected 180 * on the outside to that transparent port. Note that a port may 181 * be listed more than once. This method read synchronized on the 182 * workspace. 183 * @return An enumeration of ComponentPort objects. 184 * @deprecated Use deepConnectedPortList() instead. 185 */ 186 @Deprecated 187 public Enumeration deepConnectedPorts() { 188 return Collections.enumeration(deepConnectedPortList()); 189 } 190 191 /** Deeply list the ports connected on the inside. 192 * All ports listed are opaque. Note that 193 * the returned list could conceivably be empty, for 194 * example if this port has no inside links. 195 * Also, a port may be listed more than once if more than one 196 * inside connection to it has been established. 197 * @return An unmodifiable list of ComponentPort objects. 198 */ 199 public List deepInsidePortList() { 200 try { 201 _workspace.getReadAccess(); 202 return _deepInsidePortList(null); 203 } finally { 204 _workspace.doneReading(); 205 } 206 } 207 208 /** If this port is transparent, then deeply enumerate the ports 209 * connected on the inside. Otherwise, enumerate 210 * just this port. All ports enumerated are opaque. Note that 211 * the returned enumeration could conceivably be empty, for 212 * example if this port is transparent but has no inside links. 213 * Also, a port may be listed more than once if more than one 214 * inside connection to it has been established. 215 * @return An enumeration of ComponentPort objects. 216 * @deprecated Use deepInsidePortList() instead. 217 */ 218 @Deprecated 219 public Enumeration deepInsidePorts() { 220 return Collections.enumeration(deepInsidePortList()); 221 } 222 223 /** Insert a link to the specified relation at the specified index, 224 * and notify the container by calling its connectionsChanged() method. 225 * This method defaults to adding an inside null link at the given index 226 * if the relation argument is null. Otherwise it simply invokes the 227 * insertLink) method. 228 * <p> 229 * The specified index can be any non-negative integer. 230 * Any links with indices larger than or equal to the one specified 231 * here will henceforth have indices that are larger by one. 232 * If the index is larger than the number of existing 233 * links (as returned by numLinks()), then empty links 234 * are inserted (these will be null elements in the list returned 235 * by linkedRelationsList() or in the enumeration returned by 236 * linkedRelations()). If the specified relation is null, then 237 * an empty inside link is inserted at the specified index. 238 * <p> 239 * Note that a port may be linked to the same relation more than 240 * once, in which case the link will be reported more than once 241 * by the linkedRelations() method. 242 * <p> 243 * In derived classes, the relation may be required to be an 244 * instance of a particular subclass of Relation (this is checked 245 * by the _checkLink() protected method). 246 * <p> 247 * This method is write-synchronized on the workspace and increments 248 * its version number. 249 * @param index The index at which to insert the link. 250 * @param relation The relation to link to this port. 251 * @exception IllegalActionException If the link would cross levels of 252 * the hierarchy, or the relation is incompatible, 253 * or the port has no container, or the port is not in the 254 * same workspace as the relation. 255 */ 256 public void insertInsideLink(int index, Relation relation) 257 throws IllegalActionException { 258 if (relation != null) { 259 insertLink(index, relation); 260 return; 261 } 262 263 try { 264 _workspace.getWriteAccess(); 265 266 // Assume an inside link 267 _insideLinks.insertLink(index, null); 268 269 // NOTE: _checkLink() ensures that the container is 270 // not null, and the class ensures that it is an Entity. 271 ((Entity) getContainer()).connectionsChanged(this); 272 } finally { 273 _workspace.doneWriting(); 274 } 275 } 276 277 /** Insert a link to the specified relation at the specified index, 278 * and notify the container by calling its connectionsChanged() method. 279 * This overrides the base class to allow inside links as well as links 280 * at the same level of the hierarchy. 281 * <p> 282 * The specified index can be any non-negative integer. 283 * Any links with indices larger than or equal to the one specified 284 * here will henceforth have indices that are larger by one. 285 * If the index is larger than the number of existing 286 * links (as returned by numLinks()), then empty links 287 * are inserted (these will be null elements in the list returned 288 * by linkedRelationsList() or in the enumeration returned by 289 * linkedRelations()). If the specified relation is null, then 290 * an empty outside link is inserted at the specified index. 291 * <p> 292 * Note that a port may be linked to the same relation more than 293 * once, in which case the link will be reported more than once 294 * by the linkedRelations() method. 295 * <p> 296 * In derived classes, the relation may be required to be an 297 * instance of a particular subclass of Relation (this is checked 298 * by the _checkLink() protected method). 299 * <p> 300 * This method is write-synchronized on the workspace and increments 301 * its version number. 302 * @param index The index at which to insert the link. 303 * @param relation The relation to link to this port. 304 * @exception IllegalActionException If the link would cross levels of 305 * the hierarchy, or the relation is incompatible, 306 * or the port has no container, or the port is not in the 307 * same workspace as the relation. 308 */ 309 @Override 310 public void insertLink(int index, Relation relation) 311 throws IllegalActionException { 312 if (relation != null && _workspace != relation.workspace()) { 313 throw new IllegalActionException(this, relation, 314 "Cannot link because workspaces are different."); 315 } 316 317 try { 318 _workspace.getWriteAccess(); 319 320 if (relation == null) { 321 // Assume outside link 322 _relationsList.insertLink(index, null); 323 } else { 324 _checkLink(relation); 325 326 if (_isInsideLinkable(relation.getContainer())) { 327 // An inside link 328 _insideLinks.insertLink(index, relation._linkList); 329 } else { 330 // An outside link 331 _relationsList.insertLink(index, relation._linkList); 332 } 333 } 334 335 // NOTE: _checkLink() ensures that the container is 336 // not null, and the class ensures that it is an Entity. 337 ((Entity) getContainer()).connectionsChanged(this); 338 } finally { 339 _workspace.doneWriting(); 340 } 341 } 342 343 /** List the ports connected on the inside to this port. Note that 344 * a port may be listed more than once if more than one inside connection 345 * has been established to it. 346 * This method is read-synchronized on the workspace. 347 * @return A list of ComponentPort objects. 348 */ 349 public List insidePortList() { 350 try { 351 _workspace.getReadAccess(); 352 353 LinkedList result = new LinkedList(); 354 Iterator relations = insideRelationList().iterator(); 355 356 while (relations.hasNext()) { 357 Relation relation = (Relation) relations.next(); 358 359 // A null link might yield a null relation here. 360 if (relation != null) { 361 result.addAll(relation.linkedPortList(this)); 362 } 363 } 364 365 return result; 366 } finally { 367 _workspace.doneReading(); 368 } 369 } 370 371 /** Enumerate the ports connected on the inside to this port. Note that 372 * a port may be listed more than once if more than one inside connection 373 * has been established to it. 374 * This method is read-synchronized on the workspace. 375 * @return An enumeration of ComponentPort objects. 376 * @deprecated Use insidePortList() instead. 377 */ 378 @Deprecated 379 public Enumeration insidePorts() { 380 return Collections.enumeration(insidePortList()); 381 } 382 383 /** List the relations linked on the inside to this port. 384 * Note that a relation may be listed more than once if more than link 385 * to it has been established. 386 * This method is read-synchronized on the workspace. 387 * @return A list of ComponentRelation objects. 388 */ 389 public List insideRelationList() { 390 try { 391 _workspace.getReadAccess(); 392 393 // Unfortunately, CrossRefList returns an enumeration only. 394 // Use it to construct a list. 395 LinkedList result = new LinkedList(); 396 Enumeration relations = _insideLinks.getContainers(); 397 398 while (relations.hasMoreElements()) { 399 result.add(relations.nextElement()); 400 } 401 402 return result; 403 } finally { 404 _workspace.doneReading(); 405 } 406 } 407 408 /** Enumerate the relations linked on the inside to this port. 409 * Note that a relation may be listed more than once if more than link 410 * to it has been established. 411 * This method is read-synchronized on the workspace. 412 * @return An enumeration of ComponentRelation objects. 413 */ 414 public Enumeration insideRelations() { 415 // NOTE: There is no reason to deprecate this because it does 416 // depend on Doug Lea's collections, and it is more efficient than 417 // the list version. 418 try { 419 _workspace.getReadAccess(); 420 return _insideLinks.getContainers(); 421 } finally { 422 _workspace.doneReading(); 423 } 424 } 425 426 /** Return true the the given port is deeply connected with this port. 427 * This method is read-synchronized on the workspace. 428 * @param port The port that is checked for deep connectivity. 429 * @return True if the given port is deeply connected. 430 */ 431 public boolean isDeeplyConnected(ComponentPort port) { 432 if (port == null) { 433 return false; 434 } 435 436 try { 437 _workspace.getReadAccess(); 438 return deepConnectedPortList().contains(port); 439 } finally { 440 _workspace.doneReading(); 441 } 442 } 443 444 /** Return true if the given relation or one in its relation 445 * group is linked to this port on the inside. 446 * @param r The relation. 447 * @return True if the given relation is linked to this port. 448 * @see #isLinked(Relation) 449 */ 450 public boolean isInsideGroupLinked(Relation r) { 451 try { 452 _workspace.getReadAccess(); 453 454 Iterator relations = r.relationGroupList().iterator(); 455 456 while (relations.hasNext()) { 457 Relation groupRelation = (Relation) relations.next(); 458 459 if (isInsideLinked(groupRelation)) { 460 return true; 461 } 462 } 463 464 return false; 465 } finally { 466 _workspace.doneReading(); 467 } 468 } 469 470 /** Return true if the given relation is linked from inside. 471 * Note that this returns true only if the relation is directly 472 * linked to the port. There is no support here for relation groups. 473 * @param relation The relation that is checked. 474 * @return True if the given relation is linked from inside. 475 */ 476 public boolean isInsideLinked(Relation relation) { 477 return _insideLinks.isLinked(relation); 478 } 479 480 /** Return true if the container entity is opaque. 481 * @return True if the container entity is opaque. 482 */ 483 public boolean isOpaque() { 484 long workspaceVersion = _workspace.getVersion(); 485 486 if (_isOpaqueVersion != workspaceVersion) { 487 ComponentEntity entity = (ComponentEntity) getContainer(); 488 489 if (entity == null) { 490 _isOpaque = true; 491 } else { 492 _isOpaque = entity.isOpaque(); 493 } 494 495 _isOpaqueVersion = workspaceVersion; 496 } 497 498 return _isOpaque; 499 } 500 501 /** Link this port with the specified relation. The only constraints are 502 * that the port and the relation share the same workspace, and 503 * that the relation be of a compatible type (ComponentRelation). 504 * They are not required to be at the same level of the hierarchy. 505 * To prohibit links across levels of the hierarchy, use link(). 506 * Note that generally it is a bad idea to allow level-crossing 507 * links, since it breaks modularity. This loss of modularity 508 * means, among other things, that the composite within which this 509 * port exists cannot be cloned. 510 * Nonetheless, this capability is provided for the benefit of users 511 * that feel they just must have it, and who are willing to sacrifice 512 * clonability and modularity. 513 * <p> 514 * Both inside and outside links are supported. Note that a port may 515 * be linked to the same relation more than once, in which case 516 * the link will be reported more than once by the linkedRelations() 517 * method. If the <i>relation</i> argument is null, then create a 518 * null link (on the outside). 519 * This method is write-synchronized on the workspace 520 * and increments its version number. 521 * @param relation The relation to link to. 522 * @exception IllegalActionException If the relation does not share 523 * the same workspace, or the port has no container. 524 */ 525 public void liberalLink(ComponentRelation relation) 526 throws IllegalActionException { 527 if (relation != null) { 528 _checkLiberalLink(relation); 529 } 530 531 _doLink(relation); 532 } 533 534 /** Link this port with the specified relation. Note that a port may 535 * be linked to the same relation more than once, in which case 536 * the link will be reported more than once by the linkedRelations() 537 * method. If the argument is null, then create a null link (on 538 * the outside). 539 * This method is write-synchronized on the workspace 540 * and increments its version number. 541 * @param relation The relation to link to. 542 * @exception IllegalActionException If the link crosses levels of 543 * the hierarchy, or the port has no container, or the relation 544 * is not a ComponentRelation, or if the port is contained 545 * by a class definition. 546 */ 547 @Override 548 public void link(Relation relation) throws IllegalActionException { 549 if (relation != null) { 550 _checkLink(relation); 551 } 552 553 _doLink(relation); 554 } 555 556 /** Return the number of inside links. 557 * This method is read-synchronized on the workspace. 558 * @return The number of inside links. 559 */ 560 public int numInsideLinks() { 561 try { 562 _workspace.getReadAccess(); 563 return _insideLinks.size(); 564 } finally { 565 _workspace.doneReading(); 566 } 567 } 568 569 /** Specify the container entity, adding the port to the list of ports 570 * in the container. This class overrides the base class to remove 571 * all inside links if the given container is null. 572 * This method is write-synchronized on the 573 * workspace and increments its version number. 574 * @param entity The container. 575 * @exception IllegalActionException If this port is not of the 576 * expected class for the container, or it has no name, 577 * or the port and container are not in the same workspace. 578 * @exception NameDuplicationException If the container already has 579 * a port with the name of this port. 580 */ 581 @Override 582 public void setContainer(Entity entity) 583 throws IllegalActionException, NameDuplicationException { 584 if (entity != null && _workspace != entity.workspace()) { 585 throw new IllegalActionException(this, entity, 586 "Cannot set container because workspaces are different."); 587 } 588 589 try { 590 _workspace.getWriteAccess(); 591 super.setContainer(entity); 592 593 if (entity == null) { 594 unlinkAllInside(); 595 } 596 } finally { 597 _workspace.doneWriting(); 598 } 599 } 600 601 /** Unlink the specified Relation. If the Relation 602 * is not linked to this port, do nothing. If the relation is linked 603 * more than once, then unlink all occurrences. 604 * If there is a container, notify it by calling connectionsChanged(). 605 * This overrides the base class to check to see whether the link 606 * is an inside link, based on the container of the relation, and 607 * to call unlinkInside() if it is. 608 * This method is write-synchronized on the 609 * workspace and increments its version number. 610 * @param relation The relation to unlink. 611 */ 612 @Override 613 public void unlink(Relation relation) { 614 if (relation != null && _isInsideLinkable(relation.getContainer())) { 615 // An inside link 616 unlinkInside(relation); 617 } else { 618 super.unlink(relation); 619 } 620 } 621 622 /** Unlink all outside links. 623 * If there is a container, notify it by calling connectionsChanged(). 624 * This method is write-synchronized on the workspace 625 * and increments its version number. 626 */ 627 @Override 628 public void unlinkAll() { 629 // NOTE: This overrides the base class only to update the docs 630 // to refer to _outside_ links. 631 super.unlinkAll(); 632 } 633 634 /** Unlink all inside links. 635 * If there is a container, notify it by calling connectionsChanged(). 636 * This method is write-synchronized on the workspace 637 * and increments its version number. 638 */ 639 public void unlinkAllInside() { 640 try { 641 _workspace.getWriteAccess(); 642 _insideLinks.unlinkAll(); 643 644 Entity container = (Entity) getContainer(); 645 646 if (container != null) { 647 container.connectionsChanged(this); 648 } 649 } finally { 650 _workspace.doneWriting(); 651 } 652 } 653 654 /** Unlink whatever relation is currently linked on the inside 655 * with the specified index number. If the relation 656 * is not linked to this port on the inside, do nothing. 657 * If a link is removed, then any links at higher index numbers 658 * will have their index numbers decremented by one. 659 * If there is a container, notify it by calling connectionsChanged(). 660 * This method is write-synchronized on the workspace 661 * and increments its version number. 662 * @param index The index number of the link to remove. 663 */ 664 public void unlinkInside(int index) { 665 try { 666 _workspace.getWriteAccess(); 667 _insideLinks.unlink(index); 668 669 Entity container = (Entity) getContainer(); 670 671 if (container != null) { 672 container.connectionsChanged(this); 673 } 674 } finally { 675 _workspace.doneWriting(); 676 } 677 } 678 679 /** Unlink the specified relation on the inside. If the relation 680 * is not linked to this port on the inside, do nothing. 681 * If the relation is linked more than once on the inside, 682 * remove all occurrences of the link. 683 * If there is a container, notify it by calling connectionsChanged(). 684 * This method is write-synchronized on the workspace 685 * and increments its version number. 686 * @param relation The relation to unlink. 687 */ 688 public void unlinkInside(Relation relation) { 689 try { 690 _workspace.getWriteAccess(); 691 _insideLinks.unlink(relation); 692 693 Entity container = (Entity) getContainer(); 694 695 if (container != null) { 696 container.connectionsChanged(this); 697 } 698 } finally { 699 _workspace.doneWriting(); 700 } 701 } 702 703 /////////////////////////////////////////////////////////////////// 704 //// protected methods //// 705 706 /** Override the base class to ensure that the proposed container is a 707 * ComponentEntity. 708 * @param container The proposed container. 709 * @exception IllegalActionException If the container is not a 710 * ComponentEntity. 711 */ 712 @Override 713 protected void _checkContainer(Entity container) 714 throws IllegalActionException { 715 if (!(container instanceof ComponentEntity) && container != null) { 716 throw new IllegalActionException(container, this, 717 "ComponentPort can only be contained by ComponentEntity"); 718 } 719 } 720 721 /** Check the validity of a link. 722 * If the link crosses levels of the hierarchy, then set 723 * the container persistent to ensure that MoML is exported for 724 * this link. 725 * This is used in a "strategy pattern," where the link 726 * methods call it to check the validity of a link, and derived 727 * classes perform more elaborate checks. 728 * @param relation The relation to link to. 729 * @exception IllegalActionException If this port has no container or 730 * the relation is not a ComponentRelation, or the relation has 731 * no container, or the link crosses levels of the hierarchy. 732 */ 733 protected void _checkLiberalLink(Relation relation) 734 throws IllegalActionException { 735 if (relation != null) { 736 if (!(relation instanceof ComponentRelation)) { 737 throw new IllegalActionException(this, relation, 738 "Attempt to link to an incompatible relation " 739 + "(expected ComponentRelation)."); 740 } 741 742 Entity container = (Entity) getContainer(); 743 744 if (container == null) { 745 throw new IllegalActionException(this, relation, 746 "Port must have a container to establish a link."); 747 } 748 749 // Check that the container is not a class or that 750 // if it is, that this is an inside link. 751 if (container.isClassDefinition() 752 && container != relation.getContainer()) { 753 throw new IllegalActionException(this, relation, 754 "Cannot establish a link to a port contained " 755 + "by a class definition"); 756 } 757 758 // Throw an exception if this port is not of an acceptable 759 // class for the relation. 760 relation._checkPort(this); 761 762 // Superclass assures that the container is not null. 763 Nameable relationContainer = relation.getContainer(); 764 765 if (container != relationContainer 766 && container.getContainer() != relationContainer) { 767 // Link crosses levels of the hierarchy. 768 // Ensure that an export occurs. 769 // If it's an inside link, then make the container 770 // persistent. Otherwise, make the container's container 771 // persistent. 772 if (container.deepContains(relation)) { 773 container.setPersistent(true); 774 } else { 775 NamedObj containersContainer = container.getContainer(); 776 if (containersContainer != null) { 777 containersContainer.setPersistent(true); 778 } 779 } 780 } 781 } 782 } 783 784 /** Override the base class to throw an exception if the relation is 785 * not a ComponentRelation, or if the container of the port or 786 * relation is null, or if the container of this port is a class 787 * definition and the link is not an inside link. 788 * This method used to also throw an exception 789 * if the link crosses levels of the hierarchy, 790 * but it no longer does. The Vergil user interface provides 791 * no mechanism for creating such links, so this error would 792 * be rather difficult to make. We now assume that the designer 793 * truly intended to do this, so this method is identical 794 * to _checkLiberalLink(). 795 * <p> 796 * This method is used in a "strategy pattern," where the link 797 * methods call it to check the validity of a link, and derived 798 * classes perform more elaborate checks. 799 * This method is <i>not</i> synchronized on the 800 * workspace, so the caller should be. 801 * If the relation argument is null, do nothing. 802 * @param relation The relation to link to. 803 * @exception IllegalActionException If this port has no container, or 804 * the relation is not a ComponentRelation, or the relation has 805 * no container, or the link crosses levels of the hierarchy, or 806 * this port is not an acceptable port for the specified relation, 807 * or if the container of this port is a class definition and the 808 * link is not an inside link. 809 */ 810 @Override 811 protected void _checkLink(Relation relation) throws IllegalActionException { 812 _checkLiberalLink(relation); 813 super._checkLink(relation); 814 if (relation != null) { 815 if (!(relation instanceof ComponentRelation)) { 816 throw new IllegalActionException(this, relation, 817 "Attempt to link to an incompatible relation " 818 + "(expected ComponentRelation)."); 819 } 820 // Check that the container is not a class or that 821 // if it is, that this is an inside link. 822 Entity container = (Entity) getContainer(); 823 // Superclass assures that the container is not null. 824 Nameable relationContainer = relation.getContainer(); 825 if (container.isClassDefinition() 826 && container != relationContainer) { 827 throw new IllegalActionException(this, relation, 828 "Cannot establish a link to a port contained " 829 + "by a class definition"); 830 } 831 } 832 833 /* Removed to support models that have level crossing links in MoML. 834 * EAL 4/21/09. 835 if (relation != null) { 836 Entity container = (Entity) getContainer(); 837 838 // Superclass assures that the container is not null. 839 Nameable relationContainer = relation.getContainer(); 840 841 if ((container != relationContainer) 842 && (container.getContainer() != relationContainer)) { 843 throw new IllegalActionException(this, relation, 844 "Link crosses levels of the hierarchy"); 845 } 846 } 847 */ 848 } 849 850 /** Deeply list the opaque ports connected to this port on the outside. 851 * Begin by listing the ports that are connected to this port. 852 * If any of those are transparent ports that we are connected to 853 * from the inside, then list all the ports deeply connected 854 * on the outside to that transparent port. If any are transparent 855 * ports that we are connected to from the outside, then list 856 * opaque ports deeply inside that port. Note that a port may 857 * be listed more than once. The path argument is the path from 858 * the port that originally calls this method to this port. 859 * If this port is already on the list of ports on the path to this 860 * port in deeply traversing the topology, then there is a loop in 861 * the topology, and an InvalidStateException is thrown. 862 * This method not synchronized on the workspace, so the 863 * caller should. 864 * @param path The list of ports on the path to this port in deeply 865 * traversing the topology. 866 * @return An unmodifiable list of ComponentPort objects. 867 */ 868 protected List _deepConnectedPortList(LinkedList path) { 869 if (_deepLinkedPortsVersion == _workspace.getVersion()) { 870 // Cache is valid. Use it. 871 return _deepLinkedPorts; 872 } 873 874 if (path == null) { 875 path = new LinkedList(); 876 } else { 877 if (path.indexOf(this) >= 0) { 878 throw new InvalidStateException(path, "Loop in topology!"); 879 } 880 } 881 882 path.add(0, this); 883 884 Iterator nearRelations = linkedRelationList().iterator(); 885 LinkedList result = new LinkedList(); 886 887 while (nearRelations.hasNext()) { 888 ComponentRelation relation = (ComponentRelation) nearRelations 889 .next(); 890 891 // A null link (supported since indexed links) might 892 // yield a null relation here. EAL 7/19/00. 893 if (relation != null) { 894 Iterator connectedPorts = relation.linkedPortList(this) 895 .iterator(); 896 897 while (connectedPorts.hasNext()) { 898 ComponentPort port = (ComponentPort) connectedPorts.next(); 899 900 // NOTE: If level-crossing transitions are not allowed, 901 // then a simpler test than that of the following 902 // would work. 903 if (port._isInsideLinkable(relation.getContainer())) { 904 // We are coming at the port from the inside. 905 if (port.isOpaque()) { 906 result.add(port); 907 } else { 908 // Port is transparent 909 result.addAll(port._deepConnectedPortList(path)); 910 } 911 } else { 912 // We are coming at the port from the outside. 913 if (port.isOpaque()) { 914 result.add(port); 915 } else { 916 // It is transparent. 917 result.addAll(port._deepInsidePortList(path)); 918 } 919 } 920 } 921 } 922 } 923 924 _deepLinkedPorts = Collections.unmodifiableList(result); 925 _deepLinkedPortsVersion = _workspace.getVersion(); 926 path.remove(0); 927 return _deepLinkedPorts; 928 } 929 930 /** Deeply enumerate the ports connected to this port on the outside. 931 * Begin by enumerating the ports that are connected to this port. 932 * If any of those are transparent ports that we are connected to 933 * from the inside, then list all the ports deeply connected 934 * on the outside to that transparent port. Note that a port may 935 * be enumerated more than once. The path argument is the path from 936 * the port that originally calls this method to this port. 937 * If this port is already on the list of ports on the path to this 938 * port in deeply traversing the topology, then there is a loop in 939 * the topology, and an InvalidStateException is thrown. 940 * This method not synchronized on the workspace, so the 941 * caller should. 942 * @param path The list of ports on the path to this port in deeply 943 * traversing the topology. 944 * @deprecated Use _deepConnectedPortList() instead. 945 * @return An enumeration of ComponentPort objects. 946 */ 947 @Deprecated 948 protected Enumeration _deepConnectedPorts(LinkedList path) { 949 return Collections.enumeration(_deepConnectedPortList(path)); 950 } 951 952 /** If this port is transparent, then deeply list the ports 953 * connected on the inside. Otherwise, list 954 * just this port. All ports listed are opaque. Note that 955 * the returned list could conceivably be empty, for 956 * example if this port is transparent but has no inside links. 957 * Also, a port may be listed more than once if more than one 958 * inside connection to it has been established. 959 * The path argument is the path from 960 * the port that originally calls this method to this port. 961 * If this port is already on the list of ports on the path to this 962 * port in deeply traversing the topology, then there is a loop in 963 * the topology, and an InvalidStateException is thrown. 964 * This method is read-synchronized on the workspace. 965 * @param path The list of ports on the path to this port in deeply 966 * traversing the topology. 967 * @return An unmodifiable list of ComponentPort objects. 968 */ 969 protected List _deepInsidePortList(LinkedList path) { 970 if (_deepLinkedInPortsVersion == _workspace.getVersion()) { 971 // Cache is valid. Use it. 972 return _deepLinkedInPorts; 973 } 974 975 if (path == null) { 976 path = new LinkedList(); 977 } else { 978 if (path.indexOf(this) >= 0) { 979 throw new InvalidStateException(path, "Loop in topology!"); 980 } 981 } 982 983 path.add(0, this); 984 985 LinkedList result = new LinkedList(); 986 987 // Port is transparent. 988 Iterator relations = insideRelationList().iterator(); 989 990 while (relations.hasNext()) { 991 Relation relation = (Relation) relations.next(); 992 993 // A null link might yield a null relation here. 994 if (relation != null) { 995 Iterator insidePorts = relation.linkedPortList(this).iterator(); 996 997 while (insidePorts.hasNext()) { 998 ComponentPort port = (ComponentPort) insidePorts.next(); 999 1000 // The inside port may not be actually inside, 1001 // in which case we want to look through it 1002 // from the inside (this supports transparent 1003 // entities). 1004 if (port._isInsideLinkable(relation.getContainer())) { 1005 // The inside port is not truly inside. 1006 // Check to see whether it is transparent. 1007 if (port.isOpaque()) { 1008 result.add(port); 1009 } else { 1010 result.addAll(port._deepConnectedPortList(path)); 1011 } 1012 } else { 1013 // We are coming at the port from the outside. 1014 if (port.isOpaque()) { 1015 // The inside port is truly inside. 1016 result.add(port); 1017 } else { 1018 result.addAll(port._deepInsidePortList(path)); 1019 } 1020 } 1021 } 1022 } 1023 } 1024 1025 _deepLinkedInPorts = Collections.unmodifiableList(result); 1026 _deepLinkedInPortsVersion = _workspace.getVersion(); 1027 path.remove(0); 1028 return _deepLinkedInPorts; 1029 } 1030 1031 /** If this port is transparent, then deeply enumerate the ports 1032 * connected on the inside. Otherwise, enumerate 1033 * just this port. All ports enumerated are opaque. Note that 1034 * the returned enumeration could conceivably be empty, for 1035 * example if this port is transparent but has no inside links. 1036 * Also, a port may be listed more than once if more than one 1037 * inside connection to it has been established. 1038 * The path argument is the path from 1039 * the port that originally calls this method to this port. 1040 * If this port is already on the list of ports on the path to this 1041 * port in deeply traversing the topology, then there is a loop in 1042 * the topology, and an InvalidStateException is thrown. 1043 * This method is read-synchronized on the workspace. 1044 * @param path The list of ports on the path to this port in deeply 1045 * traversing the topology. 1046 * @return An enumeration of ComponentPort objects. 1047 * @deprecated Use _deepInsidePortList() instead. 1048 */ 1049 @Deprecated 1050 protected Enumeration _deepInsidePorts(LinkedList path) { 1051 return Collections.enumeration(_deepInsidePortList(path)); 1052 } 1053 1054 /** Return a description of the object. The level of detail depends 1055 * on the argument, which is an or-ing of the static final constants 1056 * defined in the NamedObj class. Lines are indented according to 1057 * to the level argument using the protected method _getIndentPrefix(). 1058 * Zero, one or two brackets can be specified to surround the returned 1059 * description. If one is specified it is the the leading bracket. 1060 * This is used by derived classes that will append to the description. 1061 * Those derived classes are responsible for the closing bracket. 1062 * An argument other than 0, 1, or 2 is taken to be equivalent to 0. 1063 * This method is read-synchronized on the workspace. 1064 * @param detail The level of detail. 1065 * @param indent The amount of indenting. 1066 * @param bracket The number of surrounding brackets (0, 1, or 2). 1067 * @return A description of the object. 1068 * @exception IllegalActionException If thrown by the parent class. 1069 */ 1070 @Override 1071 protected String _description(int detail, int indent, int bracket) 1072 throws IllegalActionException { 1073 try { 1074 _workspace.getReadAccess(); 1075 1076 StringBuffer result = new StringBuffer(); 1077 1078 if (bracket == 1 || bracket == 2) { 1079 result.append(super._description(detail, indent, 1)); 1080 } else { 1081 result.append(super._description(detail, indent, 0)); 1082 } 1083 1084 if ((detail & LINKS) != 0) { 1085 if (result.toString().trim().length() > 0) { 1086 result.append(" "); 1087 } 1088 1089 // To avoid infinite loop, turn off the LINKS flag 1090 // when querying the Ports. 1091 detail &= ~LINKS; 1092 result.append("insidelinks {\n"); 1093 1094 Iterator insideRelations = insideRelationList().iterator(); 1095 1096 while (insideRelations.hasNext()) { 1097 Relation relation = (Relation) insideRelations.next(); 1098 1099 if (relation != null) { 1100 result.append( 1101 relation._description(detail, indent + 1, 2) 1102 + "\n"); 1103 } else { 1104 result.append(_getIndentPrefix(indent + 1) + "null\n"); 1105 } 1106 } 1107 1108 result.append(_getIndentPrefix(indent) + "}"); 1109 } 1110 1111 if (bracket == 2) { 1112 result.append("}"); 1113 } 1114 1115 return result.toString(); 1116 } finally { 1117 _workspace.doneReading(); 1118 } 1119 } 1120 1121 /** Return true if this port is either a port of the specified entity, 1122 * or a port of an entity that (deeply) contains the specified entity. 1123 * This method is read-synchronized on the workspace. 1124 * @param entity A possible container. 1125 * @return True if this port is outside the entity. 1126 */ 1127 protected boolean _isInsideLinkable(Nameable entity) { 1128 try { 1129 _workspace.getReadAccess(); 1130 1131 Nameable portContainer = getContainer(); 1132 1133 while (entity != null) { 1134 if (portContainer == entity) { 1135 return true; 1136 } 1137 1138 entity = entity.getContainer(); 1139 } 1140 1141 return false; 1142 } finally { 1143 _workspace.doneReading(); 1144 } 1145 } 1146 1147 /////////////////////////////////////////////////////////////////// 1148 //// private methods //// 1149 1150 /** Create the link. This method does not do validity checks. 1151 * @param relation The relation to link to. 1152 */ 1153 private void _doLink(Relation relation) throws IllegalActionException { 1154 if (relation != null && _workspace != relation.workspace()) { 1155 throw new IllegalActionException(this, relation, 1156 "Cannot link because workspaces are different."); 1157 } 1158 1159 try { 1160 _workspace.getWriteAccess(); 1161 1162 if (relation == null) { 1163 // Create a null link. 1164 _relationsList.link(null); 1165 } else { 1166 if (_isInsideLinkable(relation.getContainer())) { 1167 // An inside link 1168 _insideLinks.link(relation._linkList); 1169 } else { 1170 // An outside link 1171 _relationsList.link(relation._linkList); 1172 } 1173 } 1174 1175 // NOTE: _checkLink() and _checkLiberalLink() 1176 // ensure that the container is 1177 // not null, and the class ensures that it is an Entity. 1178 ((Entity) getContainer()).connectionsChanged(this); 1179 } finally { 1180 _workspace.doneWriting(); 1181 } 1182 } 1183 1184 /////////////////////////////////////////////////////////////////// 1185 //// private variables //// 1186 // A cache of the deeply linked ports, and the version used to 1187 // construct it. 1188 // 'transient' means that the variable will not be serialized. 1189 private transient List _deepLinkedPorts; 1190 1191 private transient long _deepLinkedPortsVersion = -1; 1192 1193 private transient List _deepLinkedInPorts; 1194 1195 private transient long _deepLinkedInPortsVersion = -1; 1196 1197 // A cache of the opaqueness of this port. 1198 private transient boolean _isOpaque; 1199 1200 private transient long _isOpaqueVersion = -1; 1201}