001/* An Entity is an aggregation of ports. 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.io.IOException; 031import java.io.Writer; 032import java.lang.reflect.Field; 033import java.util.Collection; 034import java.util.Collections; 035import java.util.Enumeration; 036import java.util.Iterator; 037import java.util.LinkedList; 038import java.util.List; 039 040import ptolemy.kernel.util.Attribute; 041import ptolemy.kernel.util.IllegalActionException; 042import ptolemy.kernel.util.InternalErrorException; 043import ptolemy.kernel.util.InvalidStateException; 044import ptolemy.kernel.util.KernelException; 045import ptolemy.kernel.util.NameDuplicationException; 046import ptolemy.kernel.util.NamedList; 047import ptolemy.kernel.util.NamedObj; 048import ptolemy.kernel.util.Settable; 049import ptolemy.kernel.util.Workspace; 050 051/////////////////////////////////////////////////////////////////// 052//// Entity 053 054/** 055 An Entity is a vertex in a generalized graph. It is an aggregation 056 of ports. The ports can be linked to relations. The 057 relations thus represent connections between ports, and hence, 058 connections between entities. To add a port to an entity, simply 059 set its container to the entity. To remove it, set its container 060 to null, or to some other entity. 061 <p> 062 Entities are intended for flat graphs. Derived classes support 063 hierarchy (clustered graphs) by defining entities that aggregate 064 other entities. 065 <p> 066 An Entity can contain any instance of Port. Derived classes may 067 wish to constrain to a subclass of Port. To do this, subclasses 068 should override the public method newPort() to create a port of 069 the appropriate subclass, and the protected method _addPort() to throw 070 an exception if its argument is a port that is not of the appropriate 071 subclass. 072 <p> 073 An Entity is created within a workspace. If the workspace is 074 not specified as a constructor argument, then the default workspace 075 is used. The workspace is used to synchronize simultaneous accesses 076 to a topology from multiple threads. The workspace is immutable 077 (it cannot be changed during the lifetime of the Entity). 078 079 @author John S. Davis II, Edward A. Lee 080 @version $Id$ 081 @since Ptolemy II 0.2 082 @Pt.ProposedRating Green (eal) 083 @Pt.AcceptedRating Green (johnr) 084 @see ptolemy.kernel.Port 085 @see ptolemy.kernel.Relation 086 */ 087public class Entity<T extends Port> extends InstantiableNamedObj { 088 /** Construct an entity in the default workspace with an empty string 089 * as its name. 090 * The object is added to the workspace directory. 091 * Increment the version number of the workspace. 092 */ 093 public Entity() { 094 super(); 095 _portList = new NamedList(this); 096 } 097 098 /** Construct an entity in the default workspace with the given name. 099 * If the name argument 100 * is null, then the name is set to the empty string. 101 * The object is added to the workspace directory. 102 * Increment the version number of the workspace. 103 * @param name The name of this object. 104 * @exception IllegalActionException If the name has a period. 105 */ 106 public Entity(String name) throws IllegalActionException { 107 super(name); 108 _portList = new NamedList(this); 109 } 110 111 /** Construct an entity in the given workspace with an empty string 112 * as a name. 113 * If the workspace argument is null, use the default workspace. 114 * The object is added to the workspace directory. 115 * Increment the version of the workspace. 116 * @param workspace The workspace for synchronization and version tracking. 117 */ 118 public Entity(Workspace workspace) { 119 super(workspace); 120 _portList = new NamedList(this); 121 } 122 123 /** Construct an entity in the given workspace with the given name. 124 * If the workspace argument is null, use the default workspace. 125 * If the name argument 126 * is null, then the name is set to the empty string. 127 * The object is added to the workspace directory. 128 * Increment the version of the workspace. 129 * @param workspace The workspace for synchronization and version tracking. 130 * @param name The name of this object. 131 * @exception IllegalActionException If the name has a period. 132 */ 133 public Entity(Workspace workspace, String name) 134 throws IllegalActionException { 135 super(workspace, name); 136 _portList = new NamedList(this); 137 } 138 139 /////////////////////////////////////////////////////////////////// 140 //// public methods //// 141 142 /** Clone the object into the specified workspace. The new object is 143 * <i>not</i> added to the directory of that workspace (you must do this 144 * yourself if you want it there). 145 * The result is a new entity with clones of the ports of the original 146 * entity. The ports are set to the ports of the new entity. 147 * This method gets read access on the workspace associated with 148 * this object. 149 * @param workspace The workspace for the cloned object. 150 * @exception CloneNotSupportedException If cloned ports cannot have 151 * as their container the cloned entity (this should not occur), or 152 * if one of the attributes cannot be cloned. 153 * @return The new Entity. 154 */ 155 @Override 156 public Object clone(Workspace workspace) throws CloneNotSupportedException { 157 try { 158 workspace().getReadAccess(); 159 160 Entity newEntity = (Entity) super.clone(workspace); 161 newEntity._portList = new NamedList(newEntity); // FIXME: parameterize NamedList? 162 163 // Clone the ports. 164 Iterator<T> ports = portList().iterator(); 165 166 while (ports.hasNext()) { 167 Port port = ports.next(); 168 Port newPort = (Port) port.clone(workspace); 169 170 // Assume that since we are dealing with clones, 171 // exceptions won't occur normally (the original was successfully 172 // incorporated, so this one should be too). If they do, throw an 173 // InvalidStateException. 174 try { 175 newPort.setContainer(newEntity); 176 } catch (KernelException ex) { 177 workspace.remove(newEntity); 178 throw new InvalidStateException(this, 179 "Failed to clone an Entity: " + ex.getMessage()); 180 } 181 } 182 183 Class<?> myClass = getClass(); 184 Field[] fields = myClass.getFields(); 185 186 for (Field field : fields) { 187 try { 188 if (field.get(newEntity) instanceof Port) { 189 // Get the port name. Note that by convention, 190 // this is the same as the field name. But it might 191 // not be. 192 String portName = ((Port) field.get(this)).getName(); 193 Port port = newEntity.getPort(portName); 194 195 if (port == null) { 196 throw new IllegalActionException(this, 197 "Could not find a port named '" + portName 198 + "';"); 199 } 200 201 field.set(newEntity, port); 202 } 203 } catch (Throwable throwable) { 204 // CloneNotSupportedException does not have a 205 // constructor that takes a cause argument, so we call 206 // initCause() and then throw. 207 CloneNotSupportedException cloneException = new CloneNotSupportedException( 208 "Problem cloning '" + field.getName() + "'"); 209 cloneException.initCause(throwable); 210 throw cloneException; 211 } 212 } 213 214 _cloneFixAttributeFields(newEntity); 215 return newEntity; 216 } finally { 217 workspace().doneReading(); 218 } 219 } 220 221 /** Return a list of the ports that are connected to contained ports. 222 * Ports in this entity are not included unless there is a loopback, 223 * meaning that two distinct ports of this entity are linked to the same 224 * relation. The connected entities can be obtained from the ports 225 * using getContainer(). Note that a port may be listed more than 226 * once if there is more than one connection to it. 227 * This method is read-synchronized on the workspace. 228 * @return An unmodifiable list of Port objects. 229 */ 230 public List<T> connectedPortList() { 231 try { 232 _workspace.getReadAccess(); 233 234 // This works by constructing a linked list and returning it. 235 // That list will not be corrupted by changes 236 // in the topology. 237 // The linked list is cached for efficiency. 238 if (_workspace.getVersion() != _connectedPortsVersion) { 239 // Cache is not valid, so update it. 240 _connectedPorts = new LinkedList<T>(); 241 242 Iterator<T> ports = _portList.elementList().iterator(); 243 244 while (ports.hasNext()) { 245 Port port = ports.next(); 246 _connectedPorts.addAll(port.connectedPortList()); 247 } 248 249 _connectedPortsVersion = _workspace.getVersion(); 250 } 251 252 return Collections.unmodifiableList(_connectedPorts); 253 } finally { 254 _workspace.doneReading(); 255 } 256 } 257 258 /** Enumerate all ports that are connected to contained ports. 259 * Ports in this entity are not included unless there is a loopback, 260 * meaning that two distinct ports of this entity are linked to the same 261 * relation. The connected entities can be obtained from the ports 262 * using getContainer(). Note that a port may be listed more than 263 * once if there is more than one connection to it. 264 * This method is read-synchronized on the workspace. 265 * @deprecated Use connectedPortList() instead. 266 * @return An enumeration of Port objects. 267 */ 268 @Deprecated 269 public Enumeration connectedPorts() { 270 return Collections.enumeration(connectedPortList()); 271 } 272 273 /** Notify this entity that the links to the specified port have 274 * been altered. The default implementation in this base class 275 * is to do nothing, but derived classes may want to react to new 276 * connections. 277 * @param port The port to which connections have changed. 278 279 */ 280 public void connectionsChanged(Port port) { 281 } 282 283 /** Return an iterator over contained objects. In this class, this 284 * is simply an iterator over attributes and ports. In derived 285 * classes, the iterator will also traverse classes, entities, 286 * and relations. The caller of this method should have read 287 * access on the workspace and hold it for the duration of the 288 * use of the iterator. Moreover, it should not modify the port 289 * or attribute list while using the iterator or it will get a 290 * ConcurrentModificationException. 291 * @return An iterator over instances of NamedObj contained by this 292 * object. 293 */ 294 @Override 295 public Iterator containedObjectsIterator() { 296 return new ContainedObjectsIterator(); 297 } 298 299 /** Get the attribute with the given name. The name may be compound, 300 * with fields separated by periods, in which case the attribute 301 * returned is (deeply) contained by a contained attribute or port. 302 * This method is read-synchronized on the workspace. 303 * @param name The name of the desired attribute. 304 * @return The requested attribute if it is found, null otherwise. 305 */ 306 @Override 307 public Attribute getAttribute(String name) { 308 // FindBugs states that MoMLParser.endElement() might pass 309 // null to getAttribute() because there is a chance 310 // that _currentDocName is null. 311 if (name == null) { 312 return null; 313 } 314 try { 315 _workspace.getReadAccess(); 316 317 Attribute result = super.getAttribute(name); 318 319 if (result == null) { 320 // Check ports. 321 String[] subnames = _splitName(name); 322 323 if (subnames[1] != null) { 324 Port match = getPort(subnames[0]); 325 326 if (match != null) { 327 result = match.getAttribute(subnames[1]); 328 } 329 } 330 } 331 332 return result; 333 } finally { 334 _workspace.doneReading(); 335 } 336 } 337 338 /** Return the port contained by this entity that has the specified name. 339 * If there is no such port, return null. 340 * This method is read-synchronized on the workspace. 341 * @param name The name of the desired port. 342 * @return A port with the given name, or null if none exists. 343 */ 344 public Port getPort(String name) { 345 try { 346 _workspace.getReadAccess(); 347 return (Port) _portList.get(name); 348 } finally { 349 _workspace.doneReading(); 350 } 351 } 352 353 /** Enumerate the ports belonging to this entity. 354 * The order is the order in which they became contained by this entity. 355 * This method is read-synchronized on the workspace. 356 * @deprecated Use portList() instead. 357 * @return An enumeration of Port objects. 358 */ 359 @Deprecated 360 public Enumeration getPorts() { 361 return Collections.enumeration(portList()); 362 } 363 364 /** Get all relations that are linked to ports contained by this 365 * entity. Note that a relation may be listed more than once. 366 * This method is read-synchronized on the workspace. 367 * @return An unmodifiable list of Relation objects. 368 */ 369 public List linkedRelationList() { 370 try { 371 _workspace.getReadAccess(); 372 373 // This method constructs a list and then enumerates it. 374 // The list is cached for efficiency. 375 if (_workspace.getVersion() != _linkedRelationsVersion) { 376 // Cache is not valid. Update it. 377 _linkedRelations = new LinkedList(); 378 379 Iterator ports = _portList.elementList().iterator(); 380 381 while (ports.hasNext()) { 382 Port port = (Port) ports.next(); 383 _linkedRelations.addAll(port.linkedRelationList()); 384 } 385 386 _linkedRelationsVersion = _workspace.getVersion(); 387 } 388 389 return Collections.unmodifiableList(_linkedRelations); 390 } finally { 391 _workspace.doneReading(); 392 } 393 } 394 395 /** Enumerate relations that are linked to ports contained by this 396 * entity. Note that a relation may be listed more than once. 397 * This method is read-synchronized on the workspace. 398 * @deprecated Use linkedRelationList() instead. 399 * @return An enumeration of Relation objects. 400 */ 401 @Deprecated 402 public Enumeration linkedRelations() { 403 return Collections.enumeration(linkedRelationList()); 404 } 405 406 /** Create a new port with the specified name. Set its container 407 * to be this entity. Derived classes should override this method 408 * to create a subclass of Port, if they require subclasses of Port. 409 * If the name argument is null, then the name used is an empty string. 410 * This method is write-synchronized on the workspace, and increments 411 * its version number. 412 * @param name The name to assign to the newly created port. 413 * @return The new port. 414 * @exception IllegalActionException If the port created is not 415 * of an acceptable class (this is a programming 416 * error; failed to override this method in derived classes). 417 * @exception NameDuplicationException If the entity already has a port 418 * with the specified name. 419 */ 420 public Port newPort(String name) 421 throws IllegalActionException, NameDuplicationException { 422 try { 423 _workspace.getWriteAccess(); 424 425 Port port = new Port(this, name); 426 return port; 427 } finally { 428 _workspace.doneWriting(); 429 } 430 } 431 432 /** Get the ports belonging to this entity. 433 * The order is the order in which they became contained by this entity. 434 * This method is read-synchronized on the workspace. 435 * @return An unmodifiable list of Port objects. 436 */ 437 public List<T> portList() { 438 try { 439 _workspace.getReadAccess(); 440 return _portList.elementList(); 441 } finally { 442 _workspace.doneReading(); 443 } 444 } 445 446 /** Remove all ports by setting their container to null. 447 * As a side effect, the ports will be unlinked from all relations. 448 * This method is write-synchronized on the workspace, and increments 449 * its version number. 450 */ 451 public void removeAllPorts() { 452 try { 453 _workspace.getWriteAccess(); 454 455 // Have to copy _portList to avoid corrupting the iterator. 456 // NOTE: Could use a ListIterator here instead. 457 NamedList portListCopy = new NamedList(_portList); 458 Iterator ports = portListCopy.elementList().iterator(); 459 460 while (ports.hasNext()) { 461 Port port = (Port) ports.next(); 462 463 try { 464 port.setContainer(null); 465 } catch (KernelException ex) { 466 // Should not be thrown. 467 throw new InternalErrorException( 468 "Internal error in Port constructor!" 469 + ex.getMessage()); 470 } 471 } 472 } finally { 473 _workspace.doneWriting(); 474 } 475 } 476 477 /** Specify whether this object is a class definition. 478 * This method overrides the base class to check that if the 479 * argument is true, then this entity contains no ports with links. 480 * This method is write synchronized on the workspace. 481 * @param isClass True to make this object a class definition. 482 * @exception IllegalActionException If the argument is true and 483 * this entity contains ports with links. 484 */ 485 @Override 486 public/*final*/void setClassDefinition(boolean isClass) 487 throws IllegalActionException { 488 if (isClass && !isClassDefinition()) { 489 // Converting from an instance to a class. 490 // Check that there are no links 491 // to ports contained by this entity. 492 Iterator ports = portList().iterator(); 493 494 while (ports.hasNext()) { 495 Port port = (Port) ports.next(); 496 497 if (port.numLinks() > 0) { 498 throw new IllegalActionException(this, 499 "Cannot convert an entity to a class definition " 500 + "while it contains ports with links."); 501 } 502 } 503 } 504 505 super.setClassDefinition(isClass); 506 } 507 508 /** Return a name that is guaranteed to not be the name of 509 * any contained attribute or port. In derived classes, this should be 510 * overridden so that the returned name is guaranteed to not conflict 511 * with any contained object. In this implementation, the argument 512 * is stripped of any numeric suffix, and then a numeric suffix 513 * is appended and incremented until a name is found that does not 514 * conflict with a contained attribute or port. 515 * @param prefix A prefix for the name. 516 * @return A unique name. 517 */ 518 @Override 519 public String uniqueName(String prefix) { 520 if (prefix == null) { 521 prefix = "null"; 522 } 523 524 prefix = _stripNumericSuffix(prefix); 525 526 String candidate = prefix; 527 int uniqueNameIndex = 2; 528 529 while (getAttribute(candidate) != null || getPort(candidate) != null) { 530 candidate = prefix + uniqueNameIndex++; 531 } 532 533 return candidate; 534 } 535 536 /////////////////////////////////////////////////////////////////// 537 //// protected methods //// 538 539 /** Add a port to this entity. This method should not be used 540 * directly. Call the setContainer() method of the port instead. 541 * This method does not set 542 * the container of the port to point to this entity. 543 * It assumes that the port is in the same workspace as this 544 * entity, but does not check. The caller should check. 545 * Derived classes should override this method if they require 546 * a subclass of Port to throw an exception if the argument is 547 * not of an acceptable class. 548 * This method is <i>not</i> synchronized on the workspace, so the 549 * caller should be. 550 * @param port The port to add to this entity. 551 * @exception IllegalActionException If the port has no name. 552 * @exception NameDuplicationException If the port name collides with a 553 * name already in the entity. 554 */ 555 protected void _addPort(T port) 556 throws IllegalActionException, NameDuplicationException { 557 _portList.append(port); 558 } 559 560 /** Return a description of the object. The level of detail depends 561 * on the argument, which is an or-ing of the static final constants 562 * defined in the NamedObj class. Lines are indented according to 563 * to the level argument using the protected method _getIndentPrefix(). 564 * Zero, one or two brackets can be specified to surround the returned 565 * description. If one is specified it is the the leading bracket. 566 * This is used by derived classes that will append to the description. 567 * Those derived classes are responsible for the closing bracket. 568 * An argument other than 0, 1, or 2 is taken to be equivalent to 0. 569 * This method is read-synchronized on the workspace. 570 * @param detail The level of detail. 571 * @param indent The amount of indenting. 572 * @param bracket The number of surrounding brackets (0, 1, or 2). 573 * @return A description of the object. 574 * @exception IllegalActionException If there is a problem 575 * accessing subcomponents of this object. 576 */ 577 @Override 578 protected String _description(int detail, int indent, int bracket) 579 throws IllegalActionException { 580 try { 581 _workspace.getReadAccess(); 582 583 StringBuffer result = new StringBuffer(); 584 585 if (bracket == 1 || bracket == 2) { 586 result.append(super._description(detail, indent, 1)); 587 } else { 588 result.append(super._description(detail, indent, 0)); 589 } 590 591 if ((detail & CONTENTS) != 0 || (detail & LINKS) != 0) { 592 if (result.toString().trim().length() > 0) { 593 result.append(" "); 594 } 595 596 result.append("ports {\n"); 597 598 Iterator portLists = portList().iterator(); 599 600 while (portLists.hasNext()) { 601 Port port = (Port) portLists.next(); 602 result.append( 603 port._description(detail, indent + 1, 2) + "\n"); 604 } 605 606 result.append(_getIndentPrefix(indent) + "}"); 607 } 608 609 if (bracket == 2) { 610 result.append("}"); 611 } 612 613 return result.toString(); 614 } finally { 615 _workspace.doneReading(); 616 } 617 } 618 619 /** Write a MoML description of the contents of this object, which 620 * in this class are the attributes plus the ports. This method is called 621 * by exportMoML(). Each description is indented according to the 622 * specified depth and terminated with a newline character. 623 * @param output The output to write to. 624 * @param depth The depth in the hierarchy, to determine indenting. 625 * @exception IOException If an I/O error occurs. 626 */ 627 @Override 628 protected void _exportMoMLContents(Writer output, int depth) 629 throws IOException { 630 super._exportMoMLContents(output, depth); 631 632 Iterator ports = portList().iterator(); 633 634 while (ports.hasNext()) { 635 Port port = (Port) ports.next(); 636 port.exportMoML(output, depth); 637 } 638 } 639 640 /** Remove the specified port. This method should not be used 641 * directly. Call the setContainer() method of the port instead 642 * with a null argument. The port is assumed to be contained 643 * by this entity (otherwise, nothing happens). This 644 * method does not alter the container of the port. 645 * This method is <i>not</i> synchronized on the workspace, so the 646 * caller should be. 647 * @param port The port being removed from this entity. 648 */ 649 protected void _removePort(Port port) { 650 _portList.remove(port); 651 } 652 653 /** Validate attributes deeply contained by this object if they 654 * implement the Settable interface by calling their validate() method. 655 * This method overrides the base class to check attributes contained 656 * by the contained ports. 657 * Errors that are triggered by this validation are handled by calling 658 * handleModelError(). 659 * @param attributesValidated A HashSet of Attributes that have 660 * already been validated. For example, Settables that implement 661 * the SharedSettable interface are validated only once. 662 * @see NamedObj#handleModelError(NamedObj context, IllegalActionException exception) 663 * @exception IllegalActionException If the superclass throws it 664 * or if handleModelError() throws it. 665 */ 666 @Override 667 protected void _validateSettables(Collection attributesValidated) 668 throws IllegalActionException { 669 670 super._validateSettables(attributesValidated); 671 Iterator ports = portList().iterator(); 672 while (ports.hasNext()) { 673 Port port = (Port) ports.next(); 674 if (port instanceof Settable) { 675 try { 676 Collection validated = ((Settable) port).validate(); 677 if (validated != null) { 678 attributesValidated.addAll(validated); 679 } 680 attributesValidated.add(port); 681 } catch (IllegalActionException ex) { 682 if (!handleModelError(this, ex)) { 683 throw ex; 684 } 685 } 686 } 687 port.validateSettables(); 688 } 689 } 690 691 /////////////////////////////////////////////////////////////////// 692 //// friendly variables //// 693 // The following is package friendly so port can access it. 694 695 /** A list of Ports owned by this Entity. */ 696 NamedList _portList; 697 698 /////////////////////////////////////////////////////////////////// 699 //// private variables //// 700 // Cached list of connected ports. 701 private transient LinkedList<T> _connectedPorts; 702 703 private transient long _connectedPortsVersion = -1; 704 705 // @serial Cached list of linked relations. 706 private transient LinkedList _linkedRelations; 707 708 private transient long _linkedRelationsVersion = -1; 709 710 /////////////////////////////////////////////////////////////////// 711 //// inner classes //// 712 713 /** This class is an iterator over all the contained objects 714 * (all instances of NamedObj). In this class, the contained 715 * objects are attributes first, then ports. In derived classes, 716 * they include relations, and entities as well. 717 * The user of this class should have read 718 * access on the workspace and hold it for the duration of the 719 * use of the iterator. Moreover, it should not modify the port 720 * or attribute list while using the iterator or it will get a 721 * ConcurrentModificationException. 722 */ 723 protected class ContainedObjectsIterator 724 extends NamedObj.ContainedObjectsIterator { 725 /** Create an iterator over all the contained objects, which 726 * for Entities are attributes and then ports. 727 */ 728 public ContainedObjectsIterator() { 729 super(); 730 _portListIterator = portList().iterator(); 731 } 732 733 /** Return true if the iteration has more elements. 734 * In this base class, this returns true if there are more 735 * attributes or ports. 736 * @return True if there are more attributes or ports. 737 */ 738 @Override 739 public boolean hasNext() { 740 if (super.hasNext()) { 741 return true; 742 } 743 return _portListIterator.hasNext(); 744 } 745 746 /** Return the next element in the iteration. 747 * In this base class, this is the next attribute or port. 748 * @return The next attribute or port. 749 */ 750 @Override 751 public Object next() { 752 if (super.hasNext()) { 753 return super.next(); 754 } 755 return _portListIterator.next(); 756 } 757 758 /** The remove() method is not supported because is is not 759 * supported in NamedObj.ContainedObjectsIterator.remove(). 760 */ 761 @Override 762 public void remove() { 763 // Findbugs complains about Call to unsupported method. 764 throw new UnsupportedOperationException("remove() not supported " 765 + "because attributeList().iterator() returns a NamedList " 766 + "that is unmodifiable"); 767 } 768 769 private Iterator _portListIterator = null; 770 } 771}