001/* A Port is an aggregation of links to relations. 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.util.Collections; 031import java.util.Enumeration; 032import java.util.Iterator; 033import java.util.LinkedList; 034import java.util.List; 035 036import ptolemy.kernel.util.ChangeRequest; 037import ptolemy.kernel.util.CrossRefList; 038import ptolemy.kernel.util.IllegalActionException; 039import ptolemy.kernel.util.InternalErrorException; 040import ptolemy.kernel.util.NameDuplicationException; 041import ptolemy.kernel.util.NamedObj; 042import ptolemy.kernel.util.Workspace; 043 044/////////////////////////////////////////////////////////////////// 045//// Port 046 047/** 048 A Port is the interface of an Entity to any number of Relations. 049 Normally, a Port is contained by an Entity, although a port 050 may exist with no container. The role of a port is to aggregate 051 a set of links to relations. Thus, for example, to represent 052 a directed graph, entities can be created with two ports, one for 053 incoming arcs and one for outgoing arcs. More generally, the arcs 054 to an entity may be divided into any number of subsets, with one port 055 representing each subset. 056 <p> 057 058 A Port can link to any instance of Relation. Derived classes may wish 059 to constrain links to a subclass of Relation. To do this, subclasses 060 should override the protected method {@link #_checkLink(Relation)} to 061 throw an exception if its argument is a relation that is not of the 062 appropriate subclass. Similarly, if a subclass wishes to constrain 063 the containers of the port to be of a subclass of Entity, they should 064 override the protected method {@link #_checkContainer(Entity)}. 065 066 @author Mudit Goel, Edward A. Lee, Jie Liu 067 @version $Id$ 068 @since Ptolemy II 0.2 069 @Pt.ProposedRating Green (eal) 070 @Pt.AcceptedRating Green (cxh) 071 @see Entity 072 @see Relation 073 */ 074public class Port extends NamedObj { 075 /** Construct a port in the default workspace with an empty string 076 * as its name. 077 * The object is added to the workspace directory. 078 * Increment the version number of the workspace. 079 */ 080 public Port() { 081 super(); 082 _elementName = "port"; 083 } 084 085 /** Construct a port in the specified workspace with an empty 086 * string as a name. You can then change the name with setName(). 087 * If the workspace argument 088 * is null, then use the default workspace. 089 * The object is added to the workspace directory. 090 * Increment the version number of the workspace. 091 * @param workspace The workspace that will list the port. 092 */ 093 public Port(Workspace workspace) { 094 super(workspace); 095 _elementName = "port"; 096 } 097 098 /** Construct a port with the given name contained by the specified 099 * entity. The container argument must not be null, or a 100 * NullPointerException will be thrown. This port will use the 101 * workspace of the container for synchronization and version counts. 102 * If the name argument is null, then the name is set to the empty string. 103 * The object is not added to the workspace directory, 104 * unless the container is null. 105 * Increment the version of the workspace. 106 * @param container The parent entity. 107 * @param name The name of the Port. 108 * @exception IllegalActionException If the port is not of an acceptable 109 * class for the container. 110 * @exception NameDuplicationException If the name coincides with 111 * a port already in the container. 112 */ 113 public Port(Entity container, String name) 114 throws IllegalActionException, NameDuplicationException { 115 super(container.workspace(), name); 116 _elementName = "port"; 117 setContainer(container); 118 } 119 120 /////////////////////////////////////////////////////////////////// 121 //// public methods //// 122 123 /** Clone the object into the specified workspace. The new object is 124 * <i>not</i> added to the directory of that workspace (you must do this 125 * yourself if you want it there). 126 * The result is a new port with no connections and no container. 127 * @param workspace The workspace for the cloned object. 128 * @exception CloneNotSupportedException If one of the attributes 129 * cannot be cloned. 130 * @return A new Port. 131 */ 132 @Override 133 public Object clone(Workspace workspace) throws CloneNotSupportedException { 134 Port newObject = (Port) super.clone(workspace); 135 newObject._relationsList = new CrossRefList(newObject); 136 newObject._insideLinks = new CrossRefList(newObject); 137 newObject._container = null; 138 return newObject; 139 } 140 141 /** List the connected ports. Note that a port may be listed 142 * more than once if more than one connection to it has been established. 143 * This method is read-synchronized on the workspace. 144 * @return An unmodifiable list of Port objects. 145 */ 146 public List connectedPortList() { 147 try { 148 _workspace.getReadAccess(); 149 150 LinkedList result = new LinkedList(); 151 Iterator relations = linkedRelationList().iterator(); 152 153 while (relations.hasNext()) { 154 Relation relation = (Relation) relations.next(); 155 156 // A null link (supported since indexed links) might 157 // yield a null relation here. EAL 7/19/00. 158 if (relation != null) { 159 result.addAll(relation.linkedPortList(this)); 160 } 161 } 162 163 return Collections.unmodifiableList(result); 164 } finally { 165 _workspace.doneReading(); 166 } 167 } 168 169 /** Enumerate the connected ports. Note that a port may be listed 170 * more than once if more than one connection to it has been established. 171 * This method is read-synchronized on the workspace. 172 * @deprecated Use connectedPortList() instead. 173 * @return An enumeration of Port objects. 174 */ 175 @Deprecated 176 public Enumeration connectedPorts() { 177 return Collections.enumeration(connectedPortList()); 178 } 179 180 /** Get the container entity. 181 * @return An instance of Entity. 182 * @see #setContainer(Entity) 183 */ 184 @Override 185 public NamedObj getContainer() { 186 return _container; 187 } 188 189 /** Insert a link to the specified relation at the specified index, 190 * and notify the container by calling its connectionsChanged() method. 191 * The relation is required to be at the same level of the hierarchy 192 * as the entity that contains this port, meaning that the container 193 * of the relation is the same as the container of the container of 194 * the port. That is, level-crossing links are not allowed. 195 * <p> 196 * The specified index can be any non-negative integer. 197 * Any links with indices larger than or equal to the one specified 198 * here will henceforth have indices that are larger by one. 199 * If the index is larger than the number of existing 200 * links (as returned by numLinks()), then empty links 201 * are inserted (these will be null elements in the list returned 202 * by linkedRelationsList() or in the enumeration returned by 203 * linkedRelations()). If the specified relation is null, then 204 * an empty link is inserted at the specified index. 205 * <p> 206 * Note that a port may be linked to the same relation more than 207 * once, in which case the link will be reported more than once 208 * by the linkedRelations() method. 209 * <p> 210 * In derived classes, the relation may be required to be an 211 * instance of a particular subclass of Relation (this is checked 212 * by the _checkLink() protected method). 213 * <p> 214 * This method is write-synchronized on the workspace and increments 215 * its version number. 216 * @param index The index at which to insert the link. 217 * @param relation The relation to link to this port. 218 * @exception IllegalActionException If the link would cross levels of 219 * the hierarchy, or the relation is incompatible, 220 * or the port has no container, or the port is not in the 221 * same workspace as the relation, or if the port is contained 222 * by a class definition. 223 */ 224 public void insertLink(int index, Relation relation) 225 throws IllegalActionException { 226 if (_workspace != relation.workspace()) { 227 throw new IllegalActionException(this, relation, 228 "Cannot link because workspaces are different."); 229 } 230 231 try { 232 _workspace.getWriteAccess(); 233 _checkLink(relation); 234 _relationsList.insertLink(index, relation._linkList); 235 236 if (_container != null) { 237 _container.connectionsChanged(this); 238 } 239 } finally { 240 _workspace.doneWriting(); 241 } 242 } 243 244 /** Return true if the given relation or one in its relation 245 * group is linked to this port. 246 * @param r The relation. 247 * @return True if the given relation is linked to this port. 248 * @see #isLinked(Relation) 249 */ 250 public boolean isGroupLinked(Relation r) { 251 try { 252 _workspace.getReadAccess(); 253 254 Iterator relations = r.relationGroupList().iterator(); 255 256 while (relations.hasNext()) { 257 Relation groupRelation = (Relation) relations.next(); 258 259 if (_relationsList.isLinked(groupRelation)) { 260 return true; 261 } 262 } 263 264 return false; 265 } finally { 266 _workspace.doneReading(); 267 } 268 } 269 270 /** Return true if the given relation is linked to this port. 271 * Note that this returns true only if the relation is directly 272 * linked to the port. There is no support here for relation groups. 273 * This method is read-synchronized on the workspace. 274 * @param r The relation. 275 * @return True if the given relation is linked to this port. 276 * @see #isGroupLinked(Relation) 277 */ 278 public boolean isLinked(Relation r) { 279 try { 280 _workspace.getReadAccess(); 281 return _relationsList.isLinked(r); 282 } finally { 283 _workspace.doneReading(); 284 } 285 } 286 287 /** Link this port with a relation, and notify the container by 288 * calling its connectionsChanged() method. The relation is required 289 * to be at the same level of the hierarchy as the entity that contains 290 * this port, meaning that the container of the relation 291 * is the same as the container of the container of the port. 292 * That is, level-crossing links are not allowed. 293 * <p> 294 * If the argument is null, then create a null link. Note that a port 295 * may be linked to the same relation more than once, in which case 296 * the link will be reported more than once by the linkedRelations() 297 * method. In derived classes, the relation may be required to be an 298 * instance of a particular subclass of Relation (this is checked 299 * by the _checkLink() protected method). 300 * This method is write-synchronized on the workspace and increments 301 * its version number. 302 * @param relation The relation to link to this port. 303 * @exception IllegalActionException If the link would cross levels of 304 * the hierarchy, or the relation is incompatible, 305 * or the port has no container, or the port is not in the 306 * same workspace as the relation, or if the port is contained 307 * by a class definition. 308 */ 309 public void link(Relation relation) throws IllegalActionException { 310 if (relation != null && _workspace != relation.workspace()) { 311 throw new IllegalActionException(this, relation, 312 "Cannot link because workspaces are different."); 313 } 314 315 try { 316 _workspace.getWriteAccess(); 317 318 if (relation != null) { 319 _checkLink(relation); 320 _relationsList.link(relation._linkList); 321 } else { 322 _relationsList.link(null); 323 } 324 325 if (_container != null) { 326 _container.connectionsChanged(this); 327 } 328 } finally { 329 _workspace.doneWriting(); 330 } 331 } 332 333 /** List the linked relations. Note that a relation may appear 334 * more than once if more than one link to it has been established. 335 * Also, some entries in the list may be null, indicating a <b>null 336 * link</b>, where there is no linked relation. A null link causes 337 * a skip in the link indexes. 338 * This method is read-synchronized on the workspace. 339 * @return A list of Relation objects. 340 */ 341 public List linkedRelationList() { 342 try { 343 _workspace.getReadAccess(); 344 345 // Unfortunately, CrossRefList returns an enumeration only. 346 // Use it to construct a list. 347 // NOTE: This list should be cached. 348 LinkedList result = new LinkedList(); 349 Enumeration relations = _relationsList.getContainers(); 350 351 while (relations.hasMoreElements()) { 352 result.add(relations.nextElement()); 353 } 354 355 return result; 356 } finally { 357 _workspace.doneReading(); 358 } 359 } 360 361 /** Enumerate the linked relations. Note that a relation may appear 362 * more than once if more than one link to it has been established. 363 * Also, some entries in the enumeration may be null, indicating a 364 * <b>null link</b>, where there is no linked relation. A null link 365 * causes a skip in the link indexes. 366 * This method is read-synchronized on the workspace. 367 * @return An enumeration of Relation objects. 368 */ 369 public Enumeration linkedRelations() { 370 // NOTE: There is no reason to deprecate this because it does not 371 // depend on Doug Lea's collections, and it is more efficient than 372 // the list version. 373 try { 374 _workspace.getReadAccess(); 375 return _relationsList.getContainers(); 376 } finally { 377 _workspace.doneReading(); 378 } 379 } 380 381 /** Move this object down by one in the list of ports of 382 * its container. If this object is already last, do nothing. 383 * Increment the version of the workspace. 384 * @return The index of the specified object prior to moving it, 385 * or -1 if it is not moved. 386 * @exception IllegalActionException If this object has 387 * no container. 388 */ 389 @Override 390 public int moveDown() throws IllegalActionException { 391 Entity container = (Entity) getContainer(); 392 393 if (container == null) { 394 throw new IllegalActionException(this, "Has no container."); 395 } 396 397 try { 398 _workspace.getWriteAccess(); 399 400 int result = container._portList.moveDown(this); 401 402 // Propagate. 403 Iterator derivedObjects = getDerivedList().iterator(); 404 405 while (derivedObjects.hasNext()) { 406 NamedObj derived = (NamedObj) derivedObjects.next(); 407 container = (Entity) derived.getContainer(); 408 container._portList.moveDown(derived); 409 } 410 411 return result; 412 } finally { 413 _workspace.doneWriting(); 414 } 415 } 416 417 /** Move this object to the first position in the list 418 * of ports of the container. If this object is already first, 419 * do nothing. Increment the version of the workspace. 420 * @return The index of the specified object prior to moving it, 421 * or -1 if it is not moved. 422 * @exception IllegalActionException If this object has 423 * no container. 424 */ 425 @Override 426 public int moveToFirst() throws IllegalActionException { 427 Entity container = (Entity) getContainer(); 428 429 if (container == null) { 430 throw new IllegalActionException(this, "Has no container."); 431 } 432 433 try { 434 _workspace.getWriteAccess(); 435 436 int result = container._portList.moveToFirst(this); 437 438 // Propagate. 439 Iterator derivedObjects = getDerivedList().iterator(); 440 441 while (derivedObjects.hasNext()) { 442 NamedObj derived = (NamedObj) derivedObjects.next(); 443 container = (Entity) derived.getContainer(); 444 container._portList.moveToFirst(derived); 445 } 446 447 return result; 448 } finally { 449 _workspace.doneWriting(); 450 } 451 } 452 453 /** Move this object to the specified position in the list 454 * of ports of the container. If this object is already at the 455 * specified position, do nothing. Increment the version of the 456 * workspace. 457 * @param index The position to move this object to. 458 * @return The index of the specified object prior to moving it, 459 * or -1 if it is not moved. 460 * @exception IllegalActionException If this object has 461 * no container or if the index is out of bounds. 462 */ 463 @Override 464 public int moveToIndex(int index) throws IllegalActionException { 465 Entity container = (Entity) getContainer(); 466 467 if (container == null) { 468 throw new IllegalActionException(this, "Has no container."); 469 } 470 471 try { 472 _workspace.getWriteAccess(); 473 474 int result = container._portList.moveToIndex(this, index); 475 476 // Propagate. 477 Iterator derivedObjects = getDerivedList().iterator(); 478 479 while (derivedObjects.hasNext()) { 480 NamedObj derived = (NamedObj) derivedObjects.next(); 481 container = (Entity) derived.getContainer(); 482 container._portList.moveToIndex(derived, index); 483 } 484 485 return result; 486 } finally { 487 _workspace.doneWriting(); 488 } 489 } 490 491 /** Move this object to the last position in the list 492 * of ports of the container. If this object is already last, 493 * do nothing. Increment the version of the workspace. 494 * @return The index of the specified object prior to moving it, 495 * or -1 if it is not moved. 496 * @exception IllegalActionException If this object has 497 * no container. 498 */ 499 @Override 500 public int moveToLast() throws IllegalActionException { 501 Entity container = (Entity) getContainer(); 502 503 if (container == null) { 504 throw new IllegalActionException(this, "Has no container."); 505 } 506 507 try { 508 _workspace.getWriteAccess(); 509 510 int result = container._portList.moveToLast(this); 511 512 // Propagate. 513 Iterator derivedObjects = getDerivedList().iterator(); 514 515 while (derivedObjects.hasNext()) { 516 NamedObj derived = (NamedObj) derivedObjects.next(); 517 container = (Entity) derived.getContainer(); 518 container._portList.moveToLast(derived); 519 } 520 521 return result; 522 } finally { 523 _workspace.doneWriting(); 524 } 525 } 526 527 /** Move this object up by one in the list of 528 * ports of the container. If this object is already first, do 529 * nothing. Increment the version of the workspace. 530 * @return The index of the specified object prior to moving it, 531 * or -1 if it is not moved. 532 * @exception IllegalActionException If this object has 533 * no container. 534 */ 535 @Override 536 public int moveUp() throws IllegalActionException { 537 Entity container = (Entity) getContainer(); 538 539 if (container == null) { 540 throw new IllegalActionException(this, "Has no container."); 541 } 542 543 try { 544 _workspace.getWriteAccess(); 545 546 int result = container._portList.moveUp(this); 547 548 // Propagate. 549 Iterator derivedObjects = getDerivedList().iterator(); 550 551 while (derivedObjects.hasNext()) { 552 NamedObj derived = (NamedObj) derivedObjects.next(); 553 container = (Entity) derived.getContainer(); 554 container._portList.moveUp(derived); 555 } 556 557 return result; 558 } finally { 559 _workspace.doneWriting(); 560 } 561 } 562 563 /** Return the number of links to relations. 564 * This method is read-synchronized on the workspace. 565 * @return The number of links, a non-negative integer. 566 */ 567 public int numLinks() { 568 try { 569 _workspace.getReadAccess(); 570 return _relationsList.size(); 571 } finally { 572 _workspace.doneReading(); 573 } 574 } 575 576 /** Specify the container entity, adding the port to the list of ports 577 * in the container. If the container already contains 578 * a port with the same name, then throw an exception and do not make 579 * any changes. Similarly, if the container is not in the same 580 * workspace as this port, throw an exception. If the port is 581 * a class element and the proposed container does not match 582 * the current container, then also throw an exception. 583 * If the port is already contained by the entity, do nothing. 584 * If the port already has a container, remove 585 * this port from its port list first. Otherwise, remove it from 586 * the workspace directory, if it is present. 587 * If the argument is null, then 588 * unlink the port from any relations and remove it from its container. 589 * It is not added to the workspace directory, so this could result in 590 * this port being garbage collected. This method validates all 591 * deeply contained instances of Settable, since they may no longer 592 * be valid in the new context. 593 * This method is write-synchronized on the 594 * workspace and increments its version number. 595 * @param entity The container. 596 * @exception IllegalActionException If this port is not of the 597 * expected class for the container, or it has no name, 598 * or the port and container are not in the same workspace, or if 599 * a contained Settable becomes invalid and the error handler 600 * throws it, or if this port is a class element and the argument 601 * does not match the current container. 602 * @exception NameDuplicationException If the container already has 603 * a port with the name of this port. 604 * @see #getContainer() 605 * @see #_checkContainer(Entity) 606 */ 607 public void setContainer(Entity entity) 608 throws IllegalActionException, NameDuplicationException { 609 if (entity != null && _workspace != entity.workspace()) { 610 throw new IllegalActionException(this, entity, 611 "Cannot set container because workspaces are different."); 612 } 613 614 try { 615 _workspace.getWriteAccess(); 616 _checkContainer(entity); 617 618 Entity previousContainer = _container; 619 620 if (previousContainer == entity) { 621 return; 622 } 623 624 _notifyHierarchyListenersBeforeChange(); 625 626 try { 627 _container = entity; 628 629 // Do this first, because it may throw an exception. 630 if (entity != null) { 631 try { 632 entity._addPort(this); 633 } catch (IllegalActionException ex) { 634 _container = previousContainer; 635 throw ex; 636 } catch (NameDuplicationException ex) { 637 _container = previousContainer; 638 throw ex; 639 } 640 641 if (previousContainer == null) { 642 _workspace.remove(this); 643 } 644 645 // We have successfully set a new container for this 646 // object. Mark it modified to ensure MoML export. 647 // FIXME: Inappropriate? 648 // setOverrideDepth(0); 649 } 650 651 if (previousContainer != null) { 652 previousContainer._removePort(this); 653 } 654 655 if (entity == null) { 656 unlinkAll(); 657 } else { 658 // Transfer any queued change requests to the 659 // new container. There could be queued change 660 // requests if this component is deferring change 661 // requests. 662 if (_changeRequests != null) { 663 Iterator requests = _changeRequests.iterator(); 664 665 while (requests.hasNext()) { 666 ChangeRequest request = (ChangeRequest) requests 667 .next(); 668 entity.requestChange(request); 669 } 670 671 _changeRequests = null; 672 } 673 } 674 675 // Validate all deeply contained settables, since 676 // they may no longer be valid in the new context. 677 validateSettables(); 678 } finally { 679 // Since we definitely notified the listeners 680 // before the change, we must definitely notify 681 // them after the change, even if the change caused 682 // some exceptions. Note that this too may trigger 683 // exceptions. 684 _notifyHierarchyListenersAfterChange(); 685 } 686 } finally { 687 _workspace.doneWriting(); 688 } 689 } 690 691 /** Set the name of the port. If there is already an port 692 * of the container entity with the same name, then throw an 693 * exception. 694 * @exception IllegalActionException If the name has a period. 695 * @exception NameDuplicationException If there is already a port 696 * with the same name in the container. 697 */ 698 @Override 699 public void setName(String name) 700 throws IllegalActionException, NameDuplicationException { 701 if (name == null) { 702 name = ""; 703 } 704 705 Entity container = (Entity) getContainer(); 706 707 if (container != null) { 708 Port another = container.getPort(name); 709 710 if (another != null && another != this) { 711 throw new NameDuplicationException(container, 712 "Name duplication: " + name); 713 } 714 } 715 716 super.setName(name); 717 } 718 719 /** Unlink whatever relation is currently linked at the specified index 720 * number. If there is no such relation, do nothing. 721 * If a link is removed, then any links at higher index numbers 722 * will have their index numbers decremented by one. 723 * If there is a container, notify it by calling connectionsChanged(). 724 * This method is write-synchronized on the 725 * workspace and increments its version number. 726 * @param index The index number of the link to remove. 727 */ 728 public void unlink(int index) { 729 try { 730 _workspace.getWriteAccess(); 731 _relationsList.unlink(index); 732 733 if (_container != null) { 734 _container.connectionsChanged(this); 735 } 736 } finally { 737 _workspace.doneWriting(); 738 } 739 } 740 741 /** Unlink the specified Relation. If the Relation 742 * is not linked to this port, do nothing. If the relation is linked 743 * more than once, then unlink all occurrences. 744 * If there is a container, notify it by calling connectionsChanged(). 745 * This method is write-synchronized on the 746 * workspace and increments its version number. 747 * @param relation The relation to unlink. 748 */ 749 public void unlink(Relation relation) { 750 try { 751 _workspace.getWriteAccess(); 752 _relationsList.unlink(relation); 753 754 if (_container != null) { 755 _container.connectionsChanged(this); 756 } 757 } finally { 758 _workspace.doneWriting(); 759 } 760 } 761 762 /** Unlink all relations. 763 * If there is a container, notify it by calling connectionsChanged(). 764 * This method is write-synchronized on the 765 * workspace and increments its version number. 766 */ 767 public void unlinkAll() { 768 try { 769 _workspace.getWriteAccess(); 770 _relationsList.unlinkAll(); 771 772 if (_container != null) { 773 _container.connectionsChanged(this); 774 } 775 } finally { 776 _workspace.doneWriting(); 777 } 778 } 779 780 /////////////////////////////////////////////////////////////////// 781 //// protected methods //// 782 783 /** Check that the specified container is of a suitable class for 784 * this port. In this base class, this method returns immediately 785 * without doing anything. 786 * @param container The proposed container. 787 * @exception IllegalActionException If the container is not of 788 * an acceptable class. Not thrown in this base class. 789 */ 790 protected void _checkContainer(Entity container) 791 throws IllegalActionException { 792 } 793 794 /** Check that this port is compatible with the specified relation, 795 * that it has a container. If the argument is null, do nothing. 796 * If this port has no container, throw an exception. 797 * Derived classes may constrain the argument to be a subclass of 798 * Relation. Level-crossing links are allowed. 799 * This port and the relation are assumed to be in the same workspace, 800 * but this is not checked here. The caller should check. 801 * This method is used in a "strategy pattern," where the link 802 * methods call it to check the validity of a link, and derived 803 * classes perform more elaborate checks. 804 * This method is <i>not</i> synchronized on the 805 * workspace, so the caller should be. 806 * @param relation The relation to link to. 807 * @exception IllegalActionException If this port has no container, 808 * or if this port is not an acceptable port for the specified 809 * relation. 810 */ 811 protected void _checkLink(Relation relation) throws IllegalActionException { 812 if (relation != null) { 813 if (_container == null) { 814 throw new IllegalActionException(this, relation, 815 "Port must have a container to establish a link."); 816 } 817 818 // Throw an exception if this port is not of an acceptable 819 // class for the relation. 820 relation._checkPort(this); 821 } 822 } 823 824 /** Return a description of the object. The level of detail depends 825 * on the argument, which is an or-ing of the static final constants 826 * defined in the NamedObj class. Lines are indented according to 827 * to the level argument using the protected method _getIndentPrefix(). 828 * Zero, one or two brackets can be specified to surround the returned 829 * description. If one is specified it is the the leading bracket. 830 * This is used by derived classes that will append to the description. 831 * Those derived classes are responsible for the closing bracket. 832 * An argument other than 0, 1, or 2 is taken to be equivalent to 0. 833 * This method is read-synchronized on the workspace. 834 * @param detail The level of detail. 835 * @param indent The amount of indenting. 836 * @param bracket The number of surrounding brackets (0, 1, or 2). 837 * @return A description of the object. 838 * @exception IllegalActionException If thrown by the parent class. 839 */ 840 @Override 841 protected String _description(int detail, int indent, int bracket) 842 throws IllegalActionException { 843 try { 844 _workspace.getReadAccess(); 845 846 StringBuffer result = new StringBuffer(); 847 848 if (bracket == 1 || bracket == 2) { 849 result.append(super._description(detail, indent, 1)); 850 } else { 851 result.append(super._description(detail, indent, 0)); 852 } 853 854 if ((detail & LINKS) != 0) { 855 if (result.toString().trim().length() > 0) { 856 result.append(" "); 857 } 858 859 // To avoid infinite loop, turn off the LINKS flag 860 // when querying the Ports. 861 detail &= ~LINKS; 862 result.append("links {\n"); 863 864 Enumeration linkedRelations = linkedRelations(); 865 866 while (linkedRelations.hasMoreElements()) { 867 Relation relation = (Relation) linkedRelations 868 .nextElement(); 869 870 if (relation != null) { 871 result.append( 872 relation._description(detail, indent + 1, 2) 873 + "\n"); 874 } else { 875 // A null link (supported since indexed links) might 876 // yield a null relation here. EAL 7/19/00. 877 result.append(_getIndentPrefix(indent + 1) + "null\n"); 878 } 879 } 880 881 result.append(_getIndentPrefix(indent) + "}"); 882 } 883 884 if (bracket == 2) { 885 result.append("}"); 886 } 887 888 return result.toString(); 889 } finally { 890 _workspace.doneReading(); 891 } 892 } 893 894 /** Get a port with the specified name in the specified container. 895 * The returned object is assured of being an 896 * instance of the same class as this object. 897 * @param relativeName The name relative to the container. 898 * @param container The container expected to contain the object, which 899 * must be an instance of Entity. 900 * @return An object of the same class as this object, or null if there 901 * is none. 902 * @exception IllegalActionException If the object exists 903 * and has the wrong class, or if the specified container is not 904 * an instance of CompositeEntity. 905 */ 906 @Override 907 protected NamedObj _getContainedObject(NamedObj container, 908 String relativeName) throws IllegalActionException { 909 if (!(container instanceof Entity)) { 910 throw new IllegalActionException(this, 911 "Expected " + container.getFullName() 912 + " to be an instance of ptolemy.kernel.Entity," 913 + " but it is " + container.getClass().getName()); 914 } 915 916 Port candidate = ((Entity) container).getPort(relativeName); 917 918 if (candidate != null && !getClass().isInstance(candidate)) { 919 throw new IllegalActionException(this, 920 "Expected " + candidate.getFullName() 921 + " to be an instance of " + getClass().getName() 922 + ", but it is " + candidate.getClass().getName()); 923 } 924 925 return candidate; 926 } 927 928 /** Propagate existence of this object to the 929 * specified object. This overrides the base class 930 * to set the container. 931 * @param container Object to contain the new object. 932 * @exception IllegalActionException If the object 933 * cannot be cloned. 934 * @return A new object of the same class and name 935 * as this one. 936 */ 937 @Override 938 protected NamedObj _propagateExistence(NamedObj container) 939 throws IllegalActionException { 940 try { 941 Port newObject = (Port) super._propagateExistence(container); 942 // FindBugs warns that the cast of container is 943 // unchecked. 944 if (!(container instanceof Entity)) { 945 throw new InternalErrorException( 946 container + " is not a Entity."); 947 } else { 948 newObject.setContainer((Entity) container); 949 } 950 return newObject; 951 } catch (NameDuplicationException e) { 952 throw new InternalErrorException(e); 953 } 954 } 955 956 /////////////////////////////////////////////////////////////////// 957 //// protected variables //// 958 // NOTE: This is defined here in the base class rather than in 959 // ComponentPort even though it is not used until ComponentPort 960 // so that derived classes can safely create links to ports in 961 // the _addPort() method. This is a bit of a kludge, but I see 962 // no other way to make this possible. EAL 963 964 /** The list of inside relations for this port. */ 965 protected CrossRefList _insideLinks = new CrossRefList(this); 966 967 /** The list of relations for this port. */ 968 protected CrossRefList _relationsList = new CrossRefList(this); 969 970 /////////////////////////////////////////////////////////////////// 971 //// private variables //// 972 973 /** @serial The entity that contains this port. */ 974 private Entity _container; 975}