001/* A port supporting message passing. 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 Review vectorized methods. 028 Review broadcast/get/send/hasRoom/hasToken. 029 Review setInput/setOutput/setMultiport. 030 Review isKnown/broadcastClear/sendClear. 031 createReceivers creates inside receivers based solely on insideWidth, and 032 outsideReceivers based solely on outside width. 033 connectionsChanged: no longer validates the attributes of this port. This is 034 now done in Manager.initialize(). 035 Review sendInside, getInside, getWidthInside, transferInputs/Outputs, etc. 036 */ 037package ptolemy.actor; 038 039import java.io.IOException; 040import java.io.Writer; 041import java.util.ArrayList; 042import java.util.Collections; 043import java.util.Enumeration; 044import java.util.HashMap; 045import java.util.HashSet; 046import java.util.Iterator; 047import java.util.LinkedList; 048import java.util.List; 049import java.util.Set; 050import java.util.concurrent.CopyOnWriteArrayList; 051 052import ptolemy.actor.util.Time; 053import ptolemy.data.ArrayToken; 054import ptolemy.data.BooleanToken; 055import ptolemy.data.IntToken; 056import ptolemy.data.ObjectToken; 057import ptolemy.data.SmoothToken; 058import ptolemy.data.Token; 059import ptolemy.data.expr.Parameter; 060import ptolemy.kernel.ComponentEntity; 061import ptolemy.kernel.ComponentPort; 062import ptolemy.kernel.ComponentRelation; 063import ptolemy.kernel.Entity; 064import ptolemy.kernel.Relation; 065import ptolemy.kernel.util.Attribute; 066import ptolemy.kernel.util.Decorator; 067import ptolemy.kernel.util.IllegalActionException; 068import ptolemy.kernel.util.InternalErrorException; 069import ptolemy.kernel.util.InvalidStateException; 070import ptolemy.kernel.util.NameDuplicationException; 071import ptolemy.kernel.util.Nameable; 072import ptolemy.kernel.util.Workspace; 073 074/////////////////////////////////////////////////////////////////// 075//// IOPort 076 077/** 078 This class supports exchanging data between entities via message passing. 079 It can serve as an input port, an output port, or both. If it is an 080 input port, then it contains some number of receivers, which are 081 responsible for receiving data from remote entities. If it is an 082 output port, then it can send data to remote receivers. 083 084 <p> 085 Its receivers are created by a director. It must therefore be 086 contained by an actor that has a director. If it is not, then 087 any attempt to read data or list the receivers will trigger 088 an exception. 089 090 <p> 091 If this port is at the boundary of an composite actor, then it 092 can have both inside and outside links, with corresponding inside 093 and outside receivers if it opaque. The inside links are to 094 relations inside the opaque composite actor, whereas the outside 095 links are to relations outside. If it is not specified, then a link 096 is an outside link. 097 098 <p> 099 The port has a <i>defaultValue</i> parameter that, by default, is 100 empty. If this parameter is not empty, the port always has a token. 101 The value of the port is initially specified by the defaultValue. 102 Afterwards, the previous token of the port is remembered. 103 The defaultValue may optionally be an array, in which case a different 104 default value can be different for each channel. 105 If the port is wider than the array, then only the first <i>n</i> 106 channels will have default values, where <i>n</i> is the length of 107 the array. 108 109 <p> 110 The port has a <i>width</i>, which by default is constrained to 111 be either zero or one. 112 The width is the sum of the widths of the linked relations. 113 A port with a width greater than one behaves as a bus interface, 114 so if the width is <i>w</i>, then the port can simultaneously 115 handle <i>w</i> distinct input or output channels of data. 116 117 <p> 118 In general, an input port might have more than one receiver for 119 each channel. This occurs particularly for transparent input ports, 120 which treat the receivers of the ports linked on the inside as its own. 121 This might also occur for opaque ports in some derived classes. 122 Each receiver in the group is sent the same data. Thus, an input port in 123 general will have <i>w</i> distinct groups of receivers, and can receive 124 <i>w</i> distinct channels. 125 126 <p> 127 By default, the maximum width of the port is one, so only one 128 channel is handled. A port that allows a width greater than one 129 is called a <i>multiport</i>. Calling setMultiport() with a 130 <i>true</i> argument converts the port to a multiport. 131 132 <p> 133 The width of the port is not set directly. It is the sum of the 134 widths of the relations that the port is linked to on the outside. 135 The sum of the widths of the relations linked on the inside can be 136 more or less than the width. If it is more, then the excess inside 137 relations will be treated as if they are unconnected. If it is 138 less, then the excess outside relations will be treated as if they 139 are unconnected. 140 141 <p> 142 An IOPort can only link to instances of IORelation. Derived classes 143 may further constrain links to a subclass of IORelation. To do 144 this, they should override the protected methods _checkLink() and 145 _checkLiberalLink() to throw an exception if their arguments are 146 not of the appropriate type. Similarly, an IOPort can only be 147 contained by a class derived from ComponentEntity and implementing 148 the Actor interface. Subclasses may further constrain the 149 containers by overriding the protected method _checkContainer(). 150 151 @author Edward A. Lee, Jie Liu, Neil Smyth, Lukito Muliadi, Contributor: Bert Rodiers 152 @version $Id$ 153 @since Ptolemy II 0.2 154 @Pt.ProposedRating Green (eal) 155 @Pt.AcceptedRating Red (neuendor) 156 */ 157public class IOPort extends ComponentPort { 158 159 /** Construct an IOPort with no container and no name that is 160 * neither an input nor an output. 161 */ 162 public IOPort() { 163 super(); 164 try { 165 _init(); 166 } catch (IllegalActionException e) { 167 // TODO Auto-generated catch block 168 e.printStackTrace(); 169 } 170 } 171 172 /** Construct a port in the specified workspace with an empty 173 * string as a name. You can then change the name with setName(). 174 * If the workspace argument 175 * is null, then use the default workspace. 176 * The object is added to the workspace directory. 177 * Increment the version number of the workspace. 178 * @param workspace The workspace that will list the port. 179 * @exception IllegalActionException If thrown by the superclass 180 * or while initializing 181 */ 182 public IOPort(Workspace workspace) throws IllegalActionException { 183 super(workspace); 184 _init(); 185 } 186 187 /** Construct an IOPort with a containing actor and a name 188 * that is neither an input nor an output. The specified container 189 * must implement the Actor interface, or an exception will be thrown. 190 * 191 * @param container The container actor. 192 * @param name The name of the port. 193 * @exception IllegalActionException If the port is not of an acceptable 194 * class for the container, or if the container does not implement the 195 * Actor interface. 196 * @exception NameDuplicationException If the name coincides with 197 * a port already in the container. 198 */ 199 public IOPort(ComponentEntity container, String name) 200 throws IllegalActionException, NameDuplicationException { 201 super(container, name); 202 _init(); 203 } 204 205 /** Construct an IOPort with a container and a name that is 206 * either an input, an output, or both, depending on the third 207 * and fourth arguments. The specified container must implement 208 * the Actor interface or an exception will be thrown. 209 * 210 * @param container The container actor. 211 * @param name The name of the port. 212 * @param isInput True if this is to be an input port. 213 * @param isOutput True if this is to be an output port. 214 * @exception IllegalActionException If the port is not of an acceptable 215 * class for the container, or if the container does not implement the 216 * Actor interface. 217 * @exception NameDuplicationException If the name coincides with 218 * a port already in the container. 219 */ 220 public IOPort(ComponentEntity container, String name, boolean isInput, 221 boolean isOutput) 222 throws IllegalActionException, NameDuplicationException { 223 this(container, name); 224 setInput(isInput); 225 setOutput(isOutput); 226 } 227 228 /////////////////////////////////////////////////////////////////// 229 //// public parameters //// 230 231 /** The default value of the port. By default, this parameter is 232 * empty. If this value is not empty, then the port is persistent, 233 * which means that the get methods always return a token (they never 234 * throw NoTokenException), and that {@link #hasToken(int)}, 235 * {@link #hasToken(int, int)}, 236 * and {@link #hasTokenInside(int)} 237 * always return true, indicating that a token is available. 238 * To determine whether there is a new token, use 239 * {@link #hasNewToken(int)} or {@link #hasNewTokenInside(int)}. 240 * <p> 241 * The defaultValue may optionally be an array, in which case a different 242 * default value can be different for each channel. 243 * If the port is wider than the array, then only the first <i>n</i> 244 * channels will have default values, where <i>n</i> is the length of 245 * the array. 246 * <p> 247 * If this port is an output port, then the persistent value is 248 * used only when retrieving a token from the inside. I.e., it 249 * will be used only if the output port belongs to an opaque 250 * composite actor. 251 */ 252 public Parameter defaultValue; 253 254 /////////////////////////////////////////////////////////////////// 255 //// public methods //// 256 257 /** Append a listener to the current set of port event listeners. 258 * If the listener is already in the set, it will not be added again. 259 * Note that this method is basically the same as addDebugListener 260 * in the class NamedObj. 261 * @param listener The listener to which to send token sent messages. 262 * @see #removeIOPortEventListener(IOPortEventListener) 263 */ 264 public void addIOPortEventListener(IOPortEventListener listener) { 265 // NOTE: This method needs to be synchronized to prevent two 266 // threads from each creating a new _portEventListeners list. 267 synchronized (this) { 268 if (_portEventListeners == null) { 269 // Sean Riddle writes: "I am writing an 270 // IOPortEventListener that under certain 271 // circumstances removes itself, so it is not notified 272 // again. This triggers a 273 // ConcurrentModificationException, but this can be 274 // avoided with the attached patch. If one listener 275 // removes another listener that has not been called 276 // yet, then that listener will be called one last 277 // time in that iteration through the now-stale copy." 278 // 279 // See IOPortEventListener-1.1 in test/IOPortEventListener.tcl 280 _portEventListeners = new CopyOnWriteArrayList<IOPortEventListener>(); 281 } 282 } 283 284 // NOTE: This has to be synchronized to prevent 285 // concurrent modification exceptions. 286 synchronized (_portEventListeners) { 287 if (_portEventListeners.contains(listener)) { 288 return; 289 } else { 290 _portEventListeners.add(listener); 291 } 292 293 _hasPortEventListeners = true; 294 } 295 } 296 297 /** If a communication aspect is added, removed or modified, 298 * invalidate the list of communication aspects which is read again 299 * in the preinitialize phase. 300 * @param attribute The attribute that changed. 301 * @exception IllegalActionException If the new color 302 * attribute cannot be created. 303 */ 304 @Override 305 public void attributeChanged(Attribute attribute) 306 throws IllegalActionException { 307 if (attribute instanceof ExecutionAttributes) { 308 Decorator decorator = ((ExecutionAttributes) attribute) 309 .getDecorator(); 310 if (decorator != null && decorator instanceof CommunicationAspect) { 311 if (isOpaque()) { 312 createReceivers(); 313 } 314 } 315 } else if (attribute == defaultValue) { 316 Token value = defaultValue.getToken(); 317 if (value instanceof ArrayToken) { 318 _persistentTokens = ((ArrayToken) value).arrayValue(); 319 _persistentTokensInside = ((ArrayToken) value).arrayValue(); 320 _persistentToken = null; 321 } else { 322 _persistentToken = value; 323 _persistentTokens = null; 324 _persistentTokensInside = null; 325 } 326 } 327 super.attributeChanged(attribute); 328 } 329 330 /** Send a token to all connected receivers. 331 * Tokens are in general immutable, so each receiver is given a 332 * reference to the same token and no clones are made. 333 * The transfer is accomplished by calling getRemoteReceivers() 334 * to determine the number of channels with valid receivers and 335 * then putting the token into the receivers. 336 * If there are no destination receivers, then nothing is sent. 337 * If the port is not connected to anything, or receivers have not been 338 * created in the remote port, then just return. 339 * <p> 340 * Some of this method is read-synchronized on the workspace. 341 * Since it is possible for a thread to block while executing a put(), 342 * it is important that the thread does not hold read access on 343 * the workspace when it is blocked. Thus this method releases 344 * read access on the workspace before calling put(). 345 * 346 * @param token The token to send 347 * @exception IllegalActionException Not thrown in this base class. 348 * @exception NoRoomException If a send to one of the channels throws 349 * it. 350 */ 351 public void broadcast(Token token) 352 throws IllegalActionException, NoRoomException { 353 Receiver[][] farReceivers; 354 355 if (_debugging) { 356 _debug("broadcast " + token); 357 } 358 359 if (_hasPortEventListeners) { 360 _notifyPortEventListeners( 361 new IOPortEvent(this, IOPortEvent.SEND_BEGIN, 362 IOPortEvent.ALLCHANNELS, true, token)); 363 } 364 365 try { 366 try { 367 _workspace.getReadAccess(); 368 farReceivers = getRemoteReceivers(); 369 370 if (farReceivers == null) { 371 return; 372 } 373 } finally { 374 _workspace.doneReading(); 375 } 376 377 // NOTE: This does not call send() here, because send() 378 // repeats the above on each call. 379 for (Receiver[] farReceiver : farReceivers) { 380 if (farReceiver == null) { 381 continue; 382 } 383 384 if (farReceiver.length > 0) { 385 // Delegate to the receiver to handle putting to all 386 // receivers, since domain-specific techniques might be relevant. 387 farReceiver[0].putToAll(token, farReceiver); 388 } 389 } 390 } finally { 391 if (_hasPortEventListeners) { 392 _notifyPortEventListeners( 393 new IOPortEvent(this, IOPortEvent.SEND_END, 394 IOPortEvent.ALLCHANNELS, true, token)); 395 } 396 } 397 } 398 399 /** Send the specified portion of a token array to all receivers connected 400 * to this port. The first <i>vectorLength</i> tokens 401 * of the token array are sent. 402 * <p> 403 * Tokens are in general immutable, so each receiver 404 * is given a reference to the same token and no clones are made. 405 * If the port is not connected to anything, or receivers have not been 406 * created in the remote port, or the channel index is out of 407 * range, or the port is not an output port, 408 * then just silently return. This behavior makes it 409 * easy to leave output ports unconnected when you are not interested 410 * in the output. The transfer is accomplished 411 * by calling the vectorized put() method of the remote receivers. 412 * If the port is not connected to anything, or receivers have not been 413 * created in the remote port, then just return. 414 * <p> 415 * Some of this method is read-synchronized on the workspace. 416 * Since it is possible for a thread to block while executing a put, 417 * it is important that the thread does not hold read access on 418 * the workspace when it is blocked. Thus this method releases 419 * read access on the workspace before calling put(). 420 * 421 * @param tokenArray The token array to send 422 * @param vectorLength The number of elements of the token 423 * array to send. 424 * @exception NoRoomException If there is no room in the receiver. 425 * @exception IllegalActionException Not thrown in this base class. 426 */ 427 public void broadcast(Token[] tokenArray, int vectorLength) 428 throws IllegalActionException, NoRoomException { 429 Receiver[][] farReceivers; 430 431 if (_debugging) { 432 _debug("broadcast token array of length " + vectorLength); 433 } 434 435 if (_hasPortEventListeners) { 436 _notifyPortEventListeners(new IOPortEvent(this, 437 IOPortEvent.SEND_BEGIN, IOPortEvent.ALLCHANNELS, true, 438 tokenArray, vectorLength)); 439 } 440 441 try { 442 try { 443 _workspace.getReadAccess(); 444 farReceivers = getRemoteReceivers(); 445 446 if (farReceivers == null) { 447 return; 448 } 449 } finally { 450 _workspace.doneReading(); 451 } 452 453 // NOTE: This does not call send() here, because send() 454 // repeats the above on each call. 455 for (Receiver[] farReceiver : farReceivers) { 456 if (farReceiver == null) { 457 continue; 458 } 459 460 if (farReceiver.length > 0) { 461 // Delegate to the receiver to handle putting to all 462 // receivers, since domain-specific techniques might be relevant. 463 farReceiver[0].putArrayToAll(tokenArray, vectorLength, 464 farReceiver); 465 } 466 } 467 } finally { 468 if (_hasPortEventListeners) { 469 _notifyPortEventListeners(new IOPortEvent(this, 470 IOPortEvent.SEND_END, IOPortEvent.ALLCHANNELS, true, 471 tokenArray, vectorLength)); 472 } 473 } 474 } 475 476 /** Set all receivers connected on the outside to have no 477 * tokens. The transfer is accomplished by calling clear() on the 478 * appropriate receivers. If there are no destination receivers, 479 * or if this is not an output port, then do nothing. Some of 480 * this method is read-synchronized on the workspace. 481 * @see #sendClear(int ) 482 * @exception IllegalActionException If a receiver does not support 483 * clear(). 484 */ 485 public void broadcastClear() throws IllegalActionException { 486 Receiver[][] farReceivers; 487 488 if (_debugging) { 489 _debug("broadcast clear."); 490 } 491 492 try { 493 _workspace.getReadAccess(); 494 farReceivers = getRemoteReceivers(); 495 496 if (farReceivers == null) { 497 return; 498 } 499 } finally { 500 _workspace.doneReading(); 501 } 502 503 // NOTE: Conceivably, clear() in some domains may block, 504 // so we make sure to release read access above before calling 505 // clear(). 506 for (Receiver[] farReceiver : farReceivers) { 507 if (farReceiver == null) { 508 continue; 509 } 510 511 for (int j = 0; j < farReceiver.length; j++) { 512 farReceiver[j].clear(); 513 } 514 } 515 } 516 517 /** 518 * Check whether the widths constraints are met. 519 * @exception IllegalActionException If the width constraints or not met. 520 */ 521 public void checkWidthConstraints() throws IllegalActionException { 522 int width = _getOutsideWidth(null); 523 for (Parameter parameter : _widthEqualToParameter) { 524 IntToken t = (IntToken) parameter.getToken(); 525 526 if (t != null) { 527 if (width != t.intValue()) { 528 throw new IllegalActionException(this, "The outside width (" 529 + width + ") of port " + getFullName() 530 + " is different from the width constrain specified by parameter " 531 + parameter + " with container " 532 + parameter.getContainer() + ". A" 533 + " possible fix is to right clicking on the" 534 + " outside relation(s) and set the width -1."); 535 } 536 } 537 } 538 539 for (IOPort port : _widthEqualToPort) { 540 int otherWidth = port._getOutsideWidth(null); 541 if (width != otherWidth) { 542 throw new IllegalActionException(this, "The outside width (" 543 + width + ") of port " + getFullName() 544 + " is different from the outside width (" + otherWidth 545 + ") of port " + port.getFullName() 546 + ". A possible fix is to right clicking on the" 547 + " connected relation(s) and setting the width AUTO" 548 + " or some fixed number."); 549 } 550 } 551 } 552 553 /** Clone this port into the specified workspace. The new port is 554 * <i>not</i> added to the directory of that workspace (you must do this 555 * yourself if you want it there). 556 * The result is a new port with no connections and no container. 557 * 558 * @param workspace The workspace for the cloned object. 559 * @exception CloneNotSupportedException If one or more of the attributes 560 * cannot be cloned. 561 * @return A new IOPort. 562 */ 563 @Override 564 public Object clone(Workspace workspace) throws CloneNotSupportedException { 565 IOPort newObject = (IOPort) super.clone(workspace); 566 newObject._localReceiversTable = null; 567 newObject._insideInputVersion = -1; 568 newObject._insideOutputVersion = -1; 569 newObject._width = 0; 570 newObject._widthVersion = -1; 571 newObject._insideWidth = 0; 572 newObject._insideWidthVersion = -1; 573 newObject._farReceivers = null; 574 newObject._farReceiversVersion = -1; 575 newObject._localReceivers = null; 576 newObject._localReceiversVersion = -1; 577 newObject._localInsideReceivers = null; 578 newObject._localInsideReceiversVersion = -1; 579 newObject._insideReceivers = null; 580 newObject._insideReceiversVersion = -1; 581 newObject._numberOfSinksVersion = -1; 582 newObject._numberOfSourcesVersion = -1; 583 584 newObject._hasPortEventListeners = false; 585 newObject._portEventListeners = null; 586 587 newObject._widthEqualToParameter = new HashSet<Parameter>(); 588 newObject._widthEqualToPort = new HashSet<IOPort>(); 589 newObject._defaultWidth = -1; 590 newObject._communicationAspects = new ArrayList(); 591 592 newObject._persistentToken = null; 593 newObject._persistentTokens = null; 594 newObject._persistentTokensInside = null; 595 try { 596 newObject.attributeChanged(newObject.defaultValue); 597 } catch (IllegalActionException e) { 598 throw new CloneNotSupportedException( 599 "Unexpected cloning error: " + e); 600 } 601 602 return newObject; 603 } 604 605 /** Convert the specified token into a token acceptable to 606 * this port. In this base class, this method simply returns 607 * the same token passed to it, performing no conversion. 608 * @param token The token to convert. 609 * @return The converted token. 610 * @exception IllegalActionException If the conversion is 611 * invalid (not thrown in this base class). 612 */ 613 public Token convert(Token token) throws IllegalActionException { 614 return token; 615 } 616 617 /** Create new receivers for this port, replacing any that may 618 * previously exist, and validate any instances of Settable that 619 * this port may contain. This method should only be called on 620 * opaque ports. 621 * <p> 622 * If the port is an input port, receivers are created as necessary 623 * for each relation connecting to the port from the outside. 624 * If the port is an output port, receivers are created as necessary 625 * for each relation connected to the port from the inside. Note that 626 * only composite entities will have relations connecting to ports 627 * from the inside. 628 * <p> 629 * Note that it is perfectly allowable for a zero width output port to 630 * have insideReceivers. This can be used to allow a model to be 631 * embedded in a container that does not connect the port to anything. 632 * <p> 633 * This method is <i>not</i> write-synchronized on the workspace, so the 634 * caller should be. 635 * @exception IllegalActionException If this port is not 636 * an opaque input port or if there is no director. 637 */ 638 public void createReceivers() throws IllegalActionException { 639 //System.out.println("IOPort.createReceivers() " + getFullName()); 640 if (!isOpaque()) { 641 throw new IllegalActionException(this, 642 "createReceivers: Can only create " 643 + "receivers on opaque ports. Perhaps there is no " 644 + "top level director?"); 645 } 646 647 if (_debugging) { 648 _debug("create receivers"); 649 } 650 651 // Create the hashtable of lists of receivers in this port, keyed by 652 // relation. This replaces any previous table, so we first remove 653 // the receivers that are currently in the table. 654 if (_localReceiversTable != null) { 655 for (IORelation relation : _localReceiversTable.keySet()) { 656 _removeReceivers(relation); 657 } 658 } 659 _localReceiversTable = new HashMap<IORelation, List<Receiver[][]>>(); 660 // Make sure _localReceivers is updated next time it is accessed. 661 _localReceiversVersion = -1; 662 _insideReceiversVersion = -1; 663 664 boolean input = isInput(); 665 boolean output = isOutput(); 666 667 if (input) { 668 Iterator<?> outsideRelations = linkedRelationList().iterator(); 669 670 // Avoid an infinite loop because getWidth() calls createReceivers(). 671 boolean createReceivers = false; 672 int myWidth = _getWidth(createReceivers); 673 int channel = 0; 674 boolean madeOne = false; 675 676 while (outsideRelations.hasNext()) { 677 IORelation relation = (IORelation) outsideRelations.next(); 678 679 // A null link which can be created using insertLink() 680 // with an index might result in an null relation. 681 if (relation != null) { 682 int width = relation.getWidth(); 683 684 if (!madeOne && myWidth == 1 && width > 1) { 685 width = 1; 686 } 687 if (width == -1) { 688 throw new IllegalActionException(this, 689 "Width of relation \"" + relation.getName() 690 + "\" was -1?"); 691 } 692 693 Receiver[][] result = new Receiver[width][1]; 694 695 for (int i = 0; i < width; i++) { 696 // This throws an exception if there is no director. 697 result[i][0] = _newReceiver(channel); 698 madeOne = true; 699 } 700 701 // Save it. If we have previously seen this relation, 702 // then we simply add the new array to the list 703 // of occurrences for this relation. Otherwise, 704 // we create a new list with one element. 705 // EAL 7/30/99. 706 if (_localReceiversTable.containsKey(relation)) { 707 List<Receiver[][]> occurrences = _localReceiversTable 708 .get(relation); 709 occurrences.add(result); 710 } else { 711 List<Receiver[][]> occurrences = new LinkedList<Receiver[][]>(); 712 occurrences.add(result); 713 _localReceiversTable.put(relation, occurrences); 714 } 715 716 if (myWidth == 1 && madeOne) { 717 // Made the one receiver we need. Nothing more to do. 718 break; 719 } 720 } 721 } 722 //channel++; 723 } 724 725 if (output) { 726 Iterator<?> insideRelations = insideRelationList().iterator(); 727 728 while (insideRelations.hasNext()) { 729 IORelation relation = (IORelation) insideRelations.next(); 730 int width = relation.getWidth(); 731 732 if (width > 0) { 733 Receiver[][] result = new Receiver[width][1]; 734 735 // Inside links need to have receivers compatible 736 // with the local director. We need to create those 737 // receivers here. 738 for (int i = 0; i < width; i++) { 739 // This throws an exception if there is no director. 740 result[i][0] = _newInsideReceiver(i); 741 } 742 743 // Save it. If we have previously seen this relation, 744 // then we simply add the new array to the list 745 // of occurrences for this relation. Otherwise, 746 // we create a new list with one element. 747 // EAL 7/30/99. 748 if (_localReceiversTable.containsKey(relation)) { 749 List<Receiver[][]> occurrences = _localReceiversTable 750 .get(relation); 751 occurrences.add(result); 752 } else { 753 List<Receiver[][]> occurrences = new LinkedList<Receiver[][]>(); 754 occurrences.add(result); 755 _localReceiversTable.put(relation, occurrences); 756 } 757 } 758 } 759 } 760 } 761 762 /** Return a list of input ports connected to this port on the 763 * outside. NOTE: This method is not as useful as it might seem. 764 * In particular, it includes in the returned list input ports that 765 * are higher in the hierarchy to which this port is connected 766 * on the <i>inside</i>. This can be confusing because such ports 767 * cannot receive data produced by this port. To get a list of 768 * the ports that can receive data from this port, use the 769 * sinkPortList() method. 770 * 771 * @see ptolemy.kernel.ComponentPort#deepConnectedPortList 772 * @return A list of IOPort objects. 773 */ 774 public List<IOPort> deepConnectedInPortList() { 775 try { 776 _workspace.getReadAccess(); 777 778 LinkedList<IOPort> result = new LinkedList<IOPort>(); 779 780 Iterator<?> ports = deepConnectedPortList().iterator(); 781 782 while (ports.hasNext()) { 783 IOPort port = (IOPort) ports.next(); 784 785 if (port.isInput()) { 786 result.addLast(port); 787 } 788 } 789 790 return result; 791 } finally { 792 _workspace.doneReading(); 793 } 794 } 795 796 /** Deeply enumerate the ports connected to this port on the 797 * outside that are input ports. This method is deprecated and calls 798 * deepConnectedInPortList(). It is read-synchronized on the 799 * workspace. 800 * 801 * @see ptolemy.kernel.ComponentPort#deepConnectedPorts 802 * @deprecated Use deepConnectedInPortList() instead. 803 * @return An enumeration of input IOPort objects. 804 */ 805 @Deprecated 806 @SuppressWarnings("unchecked") 807 public Enumeration deepConnectedInPorts() { 808 return Collections.enumeration(deepConnectedInPortList()); 809 } 810 811 /** Return a list of output ports connected to this port on the 812 * outside. NOTE: This method is not as useful as it might seem. 813 * In particular, it includes in the returned list output ports that 814 * are higher in the hierarchy to which this port is connected 815 * on the <i>inside</i>. This can be confusing because such ports 816 * cannot send data to this port. To get a list of 817 * the ports that can send data to this port, use the 818 * sourcePortList() method. 819 * 820 * @see ptolemy.kernel.ComponentPort#deepConnectedPorts 821 * @return An enumeration of IOPort objects. 822 */ 823 public List<IOPort> deepConnectedOutPortList() { 824 try { 825 _workspace.getReadAccess(); 826 827 LinkedList<IOPort> result = new LinkedList<IOPort>(); 828 829 Iterator<?> ports = deepConnectedPortList().iterator(); 830 831 while (ports.hasNext()) { 832 IOPort port = (IOPort) ports.next(); 833 834 if (port.isOutput()) { 835 result.addLast(port); 836 } 837 } 838 839 return result; 840 } finally { 841 _workspace.doneReading(); 842 } 843 } 844 845 /** Deeply enumerate the ports connected to this port on the 846 * outside that are output ports. This method is deprecated and calls 847 * deepConnectedInPortList(). It is read-synchronized on the 848 * workspace. 849 * 850 * @see ptolemy.kernel.ComponentPort#deepConnectedPorts 851 * @deprecated Use deepConnectedInPortList() instead. 852 * @return An enumeration of output IOPort objects. 853 */ 854 @Deprecated 855 @SuppressWarnings("unchecked") 856 public Enumeration deepConnectedOutPorts() { 857 return Collections.enumeration(deepConnectedOutPortList()); 858 } 859 860 /** If the port is an input, return the receivers deeply linked on 861 * the inside. This method is used to obtain the receivers that 862 * are to receive data at this input port. The returned value is 863 * an array of arrays in the same format as that returned by 864 * getReceivers(). The difference between this method and 865 * getReceivers() is that this method treats the port as a 866 * transparent port regardless of whether it is one. That is, 867 * the returned receivers are contained by ports connected on the 868 * inside to this port. The number of channels is the inside 869 * width of this port. If there are no relations linked on the 870 * inside, it returns an empty array. This method is used for opaque, 871 * non-atomic entities. It "sees through" the boundary of opaque 872 * ports and actors. This method is <i>not</i> read-synchronized 873 * on the workspace, so the caller should be. 874 * @return The inside receivers, or an empty receiver array if there 875 * are none. 876 * @exception IllegalActionException If thrown while getting the 877 * deep receivers of the relation or while getting the inside 878 * width. 879 * @exception InvalidStateException Not thrown in this base class 880 */ 881 public Receiver[][] deepGetReceivers() 882 throws InvalidStateException, IllegalActionException { 883 if (!isInput()) { 884 return _EMPTY_RECEIVER_ARRAY; 885 } 886 887 // Note that this is the inside width, which may not be equal to the 888 // outside width of the port. 889 int width = getWidthInside(); 890 891 if (width <= 0) { 892 return _EMPTY_RECEIVER_ARRAY; 893 } 894 895 if (_insideReceiversVersion != _workspace.getVersion()) { 896 // Cache is invalid. Update it. 897 _insideReceivers = new Receiver[width][0]; 898 899 int index = 0; 900 Iterator<?> insideRelations = insideRelationList().iterator(); 901 902 while (insideRelations.hasNext()) { 903 IORelation relation = (IORelation) insideRelations.next(); 904 Receiver[][] deepReceiver = relation.deepReceivers(this); 905 906 if (deepReceiver != null && deepReceiver.length > 0) { 907 int size = java.lang.Math.min(deepReceiver.length, 908 width - index); 909 910 for (int i = 0; i < size; i++) { 911 _insideReceivers[index++] = deepReceiver[i]; 912 } 913 } else { 914 // Skip over the width of the relation. 915 index += relation.getWidth(); 916 if (index >= _insideReceivers.length) { 917 // No place to put any further receivers (paranoid coding). 918 break; 919 } 920 } 921 } 922 923 _insideReceiversVersion = _workspace.getVersion(); 924 } 925 926 return _insideReceivers; 927 } 928 929 /** Get a token from the specified channel. 930 * If the channel has a group with more than one receiver (something 931 * that is possible if this is a transparent port), then this method 932 * calls get() on all receivers, but returns only the first non-null 933 * token returned by these calls. 934 * Normally this method is not used on transparent ports. 935 * If there is no token to return, then throw an exception. 936 * <p> 937 * Some of this method is read-synchronized on the workspace. 938 * Since it is possible for a thread to block while executing a get, 939 * it is important that the thread does not hold read access on 940 * the workspace when it is blocked. Thus this method releases 941 * read access on the workspace before calling get(). 942 * 943 * @param channelIndex The channel index. 944 * @return A token from the specified channel. 945 * @exception NoTokenException If there is no token. 946 * @exception IllegalActionException If there is no director, and hence 947 * no receivers have been created, if the port is not an input port, or 948 * if the channel index is out of range. 949 */ 950 public Token get(int channelIndex) 951 throws NoTokenException, IllegalActionException { 952 Receiver[][] localReceivers; 953 954 if (_hasPortEventListeners) { 955 _notifyPortEventListeners(new IOPortEvent(this, 956 IOPortEvent.GET_BEGIN, channelIndex, true, null)); 957 } 958 959 try { 960 _workspace.getReadAccess(); 961 962 // Note that the getReceivers() method might throw an 963 // IllegalActionException if there's no director. 964 localReceivers = getReceivers(); 965 966 if (channelIndex >= localReceivers.length) { 967 if (!isInput()) { 968 throw new IllegalActionException(this, 969 "Port is not an input port!"); 970 } else { 971 throw new IllegalActionException(this, 972 "Channel index " + channelIndex 973 + " is out of range, because width is only " 974 + getWidth() + "."); 975 } 976 } 977 978 if (localReceivers[channelIndex] == null) { 979 throw new NoTokenException(this, 980 "No receiver at index: " + channelIndex + "."); 981 } 982 } finally { 983 _workspace.doneReading(); 984 } 985 986 // Find the first non-null token in the receiver group. 987 Token token = null; 988 for (int j = 0; j < localReceivers[channelIndex].length; j++) { 989 Token localToken = null; 990 if (localReceivers[channelIndex][j].hasToken()) { 991 localToken = localReceivers[channelIndex][j].get(); 992 } 993 if (token == null) { 994 token = localToken; 995 if (_hasPortEventListeners) { 996 _notifyPortEventListeners(new IOPortEvent(this, 997 IOPortEvent.GET_END, channelIndex, true, token)); 998 } 999 } 1000 } 1001 1002 if (token == null) { 1003 token = _getPersistentValue(channelIndex, false); 1004 if (token == null) { 1005 throw new NoTokenException(this, "No token to return."); 1006 } 1007 } 1008 1009 if (_debugging) { 1010 _debug("get from channel " + channelIndex + ": " + token); 1011 } 1012 // If this port is persistent or the input is a smooth token, 1013 // then remember the value of this input. 1014 _recordPersistentValueIfNecessary(channelIndex, token, false); 1015 1016 return token; 1017 } 1018 1019 /** Get an array of tokens from the specified channel. The 1020 * parameter <i>channelIndex</i> specifies the channel and 1021 * the parameter <i>vectorLength</i> specifies the number of 1022 * valid tokens to get in the returned array. The length of 1023 * the returned array will be equal to <i>vectorLength</i>. 1024 * <p> 1025 * If the channel has a group with more than one receiver (something 1026 * that is possible if this is a transparent port), then this method 1027 * calls get() on all receivers, but returns only the result from 1028 * the first in the group. 1029 * Normally this method is not used on transparent ports. 1030 * If there are not enough tokens to fill the array, then throw 1031 * an exception. 1032 * <p> 1033 * Some of this method is read-synchronized on the workspace. 1034 * Since it is possible for a thread to block while executing a get, 1035 * it is important that the thread does not hold read access on 1036 * the workspace when it is blocked. Thus this method releases 1037 * read access on the workspace before calling get. 1038 * 1039 * @param channelIndex The channel index. 1040 * @param vectorLength The number of valid tokens to get in the 1041 * returned array. 1042 * @return A token array from the specified channel containing 1043 * <i>vectorLength</i> valid tokens. 1044 * @exception NoTokenException If there is no array of tokens. 1045 * @exception IllegalActionException If there is no director, and hence 1046 * no receivers have been created, if the port is not an input port, or 1047 * if the channel index is out of range. 1048 */ 1049 public Token[] get(int channelIndex, int vectorLength) 1050 throws NoTokenException, IllegalActionException { 1051 Receiver[][] localReceivers; 1052 1053 if (_hasPortEventListeners) { 1054 _notifyPortEventListeners( 1055 new IOPortEvent(this, IOPortEvent.GET_BEGIN, channelIndex, 1056 true, null, vectorLength)); 1057 } 1058 1059 try { 1060 _workspace.getReadAccess(); 1061 1062 // Note that the getReceivers() method might throw an 1063 // IllegalActionException if there's no director. 1064 localReceivers = getReceivers(); 1065 } finally { 1066 _workspace.doneReading(); 1067 } 1068 1069 if (channelIndex >= localReceivers.length) { 1070 // NOTE: This may be thrown if the port is not an input port. 1071 throw new IllegalActionException(this, 1072 "get: channel index is out of range."); 1073 } 1074 1075 if (localReceivers[channelIndex] == null) { 1076 throw new NoTokenException(this, 1077 "get: no receiver at index: " + channelIndex + "."); 1078 } 1079 1080 Token[] result = null; 1081 // First try the efficient block read. 1082 try { 1083 result = localReceivers[channelIndex][0].getArray(vectorLength); 1084 // Record the last of these received tokens if there is persistence. 1085 _recordPersistentValueIfNecessary(channelIndex, 1086 result[result.length - 1], false); 1087 } catch (NoTokenException ex) { 1088 // There are not enough tokens. If persistence has been specified, 1089 // or if a SmoothToken is available, then we can fill in the array. 1090 // But we have to do it one by one. 1091 result = new Token[vectorLength]; 1092 for (int i = 0; i < vectorLength; i++) { 1093 if (localReceivers[channelIndex][0].hasToken()) { 1094 result[i] = localReceivers[channelIndex][0].get(); 1095 _recordPersistentValueIfNecessary(channelIndex, result[i], 1096 false); 1097 } else { 1098 result[i] = _getPersistentValue(channelIndex, false); 1099 if (result[i] == null) { 1100 throw new NoTokenException(this, 1101 "get: Requested " + vectorLength 1102 + " tokens, but only " + i 1103 + " were available."); 1104 } 1105 } 1106 } 1107 } 1108 1109 if (_hasPortEventListeners) { 1110 _notifyPortEventListeners(new IOPortEvent(this, IOPortEvent.GET_END, 1111 channelIndex, true, result, vectorLength)); 1112 } 1113 1114 int index = 1; 1115 1116 while (index < localReceivers[channelIndex].length) { 1117 // Read and discard data from other channels in the group. 1118 localReceivers[channelIndex][index].getArray(vectorLength); 1119 index++; 1120 } 1121 1122 if (_debugging) { 1123 _debug("get vector from channel " + channelIndex + " of length " 1124 + vectorLength); 1125 } 1126 1127 return result; 1128 } 1129 1130 /** Return the corresponding channel in this port for the given receiver. 1131 * The given receiver may be contained by this port or a port that is 1132 * connected to this port. 1133 * @param receiver A receiver that is contained in this port or 1134 * connected to another receiver contained in this port. 1135 * @return The corresponding channel for the receiver. 1136 * @exception IllegalActionException If the given receiver does not 1137 * take part in any connections pertaining to this port. 1138 */ 1139 public int getChannelForReceiver(Receiver receiver) 1140 throws IllegalActionException { 1141 1142 // Get the channel number for the receiver. 1143 Receiver[][] receivers; 1144 if (isInput()) { 1145 receivers = getReceivers(); 1146 } else { 1147 receivers = getInsideReceivers(); 1148 } 1149 1150 for (int channel = 0; channel < receivers.length; channel++) { 1151 if (receivers[channel] != null) { 1152 for (int copy = 0; copy < receivers[channel].length; copy++) { 1153 if (receivers[channel][copy] == receiver) { 1154 return channel; 1155 } 1156 } 1157 } 1158 } 1159 1160 // FIXME: this is for backwards compatibility, but really should go 1161 // in a getChannelForRemoteReceiver() 1162 if (!isInput()) { 1163 receivers = getRemoteReceivers(); 1164 1165 for (int channel = 0; channel < receivers.length; channel++) { 1166 if (receivers[channel] != null) { 1167 for (int copy = 0; copy < receivers[channel].length; copy++) { 1168 if (receivers[channel][copy] == receiver) { 1169 return channel; 1170 } 1171 } 1172 } 1173 } 1174 } 1175 1176 throw new IllegalActionException(this, 1177 "Attempt to get a channel for a receiver that" 1178 + " is not related to this port."); 1179 } 1180 1181 /** Call the {@link #getModelTime} method and return a double 1182 * representation of the model time. 1183 * @param channelIndex The channel index. 1184 * @return The current time associated with a certain channel. 1185 * @exception IllegalActionException If the channel index 1186 * is out of range or if the port is not an input port. 1187 * @deprecated As Ptolemy II 4.1, 1188 * replaced by {@link #getModelTime}. 1189 */ 1190 @Deprecated 1191 public double getCurrentTime(int channelIndex) 1192 throws IllegalActionException { 1193 return getModelTime(channelIndex).getDoubleValue(); 1194 } 1195 1196 /** Get the default width. In case there is no unique solution for a relation 1197 * connected to this port the default width will be used. 1198 * @return The default width. 1199 * @see #setDefaultWidth(int) 1200 */ 1201 public int getDefaultWidth() { 1202 return _defaultWidth; 1203 } 1204 1205 /** Get a token from the specified inside channel of this port. 1206 * This method is usually called on the output port of a 1207 * composite actor. 1208 * 1209 * <p>If the channel has a group with more than one receiver 1210 * (something that is possible if this is a transparent port), 1211 * then this method calls get() on all receivers, but returns 1212 * only the first non-null token returned by these calls. 1213 * Normally this method is not used on transparent ports. If 1214 * there is no token to return, then throw an exception. This 1215 * method is usually called only by the director of a composite 1216 * actor during transferOutputs(), as atomic actors do not normally 1217 * have relations connected on the inside of their ports. 1218 * 1219 * <p> Some of this method is read-synchronized on the workspace. 1220 * Since it is possible for a thread to block while executing a 1221 * get(), it is important that the thread does not hold read access 1222 * on the workspace when it is blocked. Thus this method releases 1223 * read access on the workspace before calling get(). 1224 * 1225 * @param channelIndex The channel index. 1226 * @return A token from the specified channel. 1227 * @exception NoTokenException If there is no token. 1228 * @exception IllegalActionException If there is no director, and hence 1229 * no receivers have been created, if the port is not an output port, or 1230 * if the channel index is out of range. 1231 */ 1232 public Token getInside(int channelIndex) 1233 throws NoTokenException, IllegalActionException { 1234 // NOTE: This code is very similar to get(int). 1235 // It would be better to delegate to a shared private method. 1236 Receiver[][] localReceivers; 1237 1238 if (_hasPortEventListeners) { 1239 _notifyPortEventListeners(new IOPortEvent(this, 1240 IOPortEvent.GET_BEGIN, channelIndex, false, null)); 1241 } 1242 1243 try { 1244 _workspace.getReadAccess(); 1245 1246 // Note that the getInsideReceivers() method might throw an 1247 // IllegalActionException if there's no director. 1248 localReceivers = getInsideReceivers(); 1249 1250 if (channelIndex >= localReceivers.length) { 1251 if (!isOutput()) { 1252 throw new IllegalActionException(this, 1253 "Port is not an output port!"); 1254 } else { 1255 throw new IllegalActionException(this, "Channel index " 1256 + channelIndex 1257 + " is out of range, because inside width is only " 1258 + localReceivers.length + "."); 1259 } 1260 } 1261 1262 if (localReceivers[channelIndex] == null) { 1263 throw new IllegalActionException(this, 1264 "No receiver at inside index: " + channelIndex + "."); 1265 } 1266 } finally { 1267 _workspace.doneReading(); 1268 } 1269 1270 // Find the first non-null token in the receiver group. 1271 Token token = null; 1272 for (int j = 0; j < localReceivers[channelIndex].length; j++) { 1273 Token localToken = null; 1274 if (localReceivers[channelIndex][j].hasToken()) { 1275 localToken = localReceivers[channelIndex][j].get(); 1276 } 1277 if (token == null) { 1278 token = localToken; 1279 if (_hasPortEventListeners) { 1280 _notifyPortEventListeners(new IOPortEvent(this, 1281 IOPortEvent.GET_END, channelIndex, true, token)); 1282 } 1283 } 1284 } 1285 1286 if (token == null) { 1287 token = _getPersistentValue(channelIndex, true); 1288 if (token == null) { 1289 throw new NoTokenException(this, "No token to return."); 1290 } 1291 } 1292 1293 if (_debugging) { 1294 _debug("get from inside channel " + channelIndex + ": " + token); 1295 } 1296 // If this port is persistent or the input is a smooth token, 1297 // then remember the value of this input. 1298 _recordPersistentValueIfNecessary(channelIndex, token, true); 1299 1300 return token; 1301 } 1302 1303 /** If the port is an opaque output port, return the receivers that 1304 * receive data from all inside linked relations. 1305 * This method is used for opaque, non-atomic entities, which have 1306 * opaque ports with inside links. Normally, those inside links 1307 * are not visible. 1308 * This method permits a director to transfer data across an opaque 1309 * boundary by transferring it from the inside receivers to whatever 1310 * receivers this might be connected to on the outside. 1311 * The returned value is an an array of arrays in the same format as 1312 * that returned by getReceivers(). 1313 * This method is read-synchronized on the workspace. 1314 * 1315 * @return The local inside receivers, or an empty array if there are 1316 * none. 1317 * @see #getInside(int) 1318 */ 1319 public Receiver[][] getInsideReceivers() { 1320 try { 1321 _workspace.getReadAccess(); 1322 1323 if (!isOutput() || !isOpaque()) { 1324 return _EMPTY_RECEIVER_ARRAY; 1325 } 1326 1327 // Check to see whether cache is valid. 1328 if (_localInsideReceiversVersion == _workspace.getVersion()) { 1329 return _localInsideReceivers; 1330 } 1331 1332 // Have to compute the _inside_ width. 1333 int width = getWidthInside(); 1334 1335 if (width <= 0) { 1336 return _EMPTY_RECEIVER_ARRAY; 1337 } 1338 1339 // Cache not valid. Reconstruct it. 1340 _localInsideReceivers = new Receiver[width][0]; 1341 1342 int index = 0; 1343 Iterator<?> relations = insideRelationList().iterator(); 1344 1345 // NOTE: Have to be careful here to keep track of the 1346 // occurrence number of the receiver. 1347 // EAL 7/30/00. 1348 HashMap<IORelation, Integer> seen = new HashMap<IORelation, Integer>(); 1349 1350 while (relations.hasNext()) { 1351 IORelation relation = (IORelation) relations.next(); 1352 1353 int occurrence = 0; 1354 1355 if (seen.containsKey(relation)) { 1356 // Have seen this relation before. Increment 1357 // the occurrence number. 1358 occurrence = seen.get(relation).intValue(); 1359 occurrence++; 1360 } 1361 1362 seen.put(relation, Integer.valueOf(occurrence)); 1363 1364 Receiver[][] receivers = getReceivers(relation, occurrence); 1365 1366 if (receivers != null) { 1367 for (Receiver[] receiver : receivers) { 1368 _localInsideReceivers[index++] = receiver; 1369 } 1370 } 1371 } 1372 1373 _localInsideReceiversVersion = _workspace.getVersion(); 1374 return _localInsideReceivers; 1375 } catch (IllegalActionException ex) { 1376 // This would be thrown only if the above call to 1377 // getReceivers(IORelation, int) throws. This should not 1378 // occur because we are sure the IORelation is connected. 1379 throw new InternalErrorException(this, ex, 1380 "Expected relation to be connected!"); 1381 } finally { 1382 _workspace.doneReading(); 1383 } 1384 } 1385 1386 /** Get the listeners for IOPortEvents. 1387 * @return The a copy of the list of listeners for IOPortEvents, 1388 * if any. Otherwise an empty list. 1389 */ 1390 public List<IOPortEventListener> getIOPortEventListeners() { 1391 List<IOPortEventListener> listeners = new LinkedList<IOPortEventListener>(); 1392 if (_hasPortEventListeners) { 1393 listeners.addAll(_portEventListeners); 1394 } 1395 return listeners; 1396 } 1397 1398 /** 1399 * Retrieve the index of the relation at the port. 1400 * In case the relation is not connected with this port -1 1401 * will be returned. 1402 * @param port The port. 1403 * @param relation The relation. 1404 * @param isOutsideRelation A flag that specifies that the 1405 * relation is an outside relation of the port. 1406 * @return The index of the relation at the port. 1407 */ 1408 @SuppressWarnings("unchecked") 1409 static public int getRelationIndex(IOPort port, Relation relation, 1410 boolean isOutsideRelation) { 1411 List<Relation> relations = isOutsideRelation ? port.linkedRelationList() 1412 : port.insideRelationList(); 1413 int i = 0; 1414 for (Relation relation2 : relations) { 1415 if (relation == relation2) { 1416 return i; 1417 } 1418 ++i; 1419 } 1420 return -1; 1421 } 1422 1423 /** Return the current time associated with a certain channel. 1424 * In most domains, this is just the current time of the director. 1425 * However, in some domains, the current time is a per-channel 1426 * concept. If the channel has a token to be read (i.e. hasToken() 1427 * returns true), then the current time is the time associated with 1428 * that token. If there is no token to be read, then the current 1429 * time is the time of most recently read token. If no token has been 1430 * previously read, then the current time is 0.0. Notice that this 1431 * means that an actor accessing time should do things in the 1432 * following order: 1433 * <pre> 1434 * if (hasToken(n)) { 1435 * double time = port.getCurrentTime(n); 1436 * Token token = port.get(n); 1437 * } 1438 * </pre> 1439 * I.e., getCurrentTime() is called before get(). 1440 * Currently, only the DT domain uses this per-channel time feature. 1441 * 1442 * @param channelIndex The channel index. 1443 * @return The current time associated with a certain channel. 1444 * @exception IllegalActionException If the channel index 1445 * is out of range or if the port is not an input port. 1446 */ 1447 public Time getModelTime(int channelIndex) throws IllegalActionException { 1448 return getModelTime(channelIndex, false); 1449 } 1450 1451 /** Return the current time associated with a certain channel. 1452 * In most domains, this is just the current time of the director. 1453 * However, in some domains, the current time is a per-channel 1454 * concept. If the channel has a token to be read (i.e. hasToken() 1455 * returns true), then the current time is the time associated with 1456 * that token. If there is no token to be read, then the current 1457 * time is the time of most recently read token. If no token has been 1458 * previously read, then the current time is 0.0. Notice that this 1459 * means that an actor accessing time should do things in the 1460 * following order: 1461 * <pre> 1462 * if (hasToken(n)) { 1463 * double time = port.getCurrentTime(n); 1464 * Token token = port.get(n); 1465 * } 1466 * </pre> 1467 * I.e., getCurrentTime() is called before get(). 1468 * Currently, only the DT domain uses this per-channel time feature. 1469 * 1470 * @param channelIndex The channel index. 1471 * @param inside True for an inside channel. 1472 * @return The current time associated with a certain channel. 1473 * @exception IllegalActionException If the channel index 1474 * is out of range or if the port is not an input port. 1475 */ 1476 public Time getModelTime(int channelIndex, boolean inside) 1477 throws IllegalActionException { 1478 Receiver[][] localReceivers; 1479 1480 try { 1481 try { 1482 _workspace.getReadAccess(); 1483 1484 // Note that the getReceivers() method might throw an 1485 // IllegalActionException if there's no director. 1486 if (inside) { 1487 localReceivers = getInsideReceivers(); 1488 } else { 1489 localReceivers = getReceivers(); 1490 } 1491 1492 if (localReceivers[channelIndex] == null) { 1493 throw new IllegalActionException(this, 1494 "no receiver at index: " + channelIndex + "."); 1495 } 1496 } finally { 1497 _workspace.doneReading(); 1498 } 1499 1500 AbstractReceiver receiver = (AbstractReceiver) localReceivers[channelIndex][0]; 1501 return receiver.getModelTime(); 1502 } catch (ArrayIndexOutOfBoundsException ex) { 1503 // NOTE: This may be thrown if the port is not an input port. 1504 throw new IllegalActionException(this, 1505 "getCurrentTime: channel index is out of range."); 1506 } 1507 } 1508 1509 /** Return the list of communication aspects in this port. 1510 * A communication aspect is a {@link Parameter} whose value is an 1511 * {@link ObjectToken} that references an object that implements 1512 * the {@link CommunicationAspect} interface. 1513 * Update the sequence number of communication aspects. 1514 * @return The list of communication aspects. 1515 * @exception IllegalActionException Thrown if the token of the parameter 1516 * containing the communication aspect object cannot be retrieved. 1517 */ 1518 public List<CommunicationAspect> getCommunicationAspects() 1519 throws IllegalActionException { 1520 if (_communicationAspects == null) { 1521 _communicationAspects = new ArrayList<CommunicationAspect>(); 1522 } 1523 1524 HashMap<Integer, CommunicationAspectAttributes> _communicationAspectMap = new HashMap<Integer, CommunicationAspectAttributes>(); 1525 int sequenceNumber = 1; 1526 List<CommunicationAspectAttributes> communicationAspectList = this 1527 .attributeList(CommunicationAspectAttributes.class); 1528 if (communicationAspectList.size() > 0) { 1529 try { 1530 _workspace.getWriteAccess(); 1531 1532 List<CommunicationAspectAttributes> enabledAttributes = new ArrayList(); 1533 1534 for (int i = 0; i < communicationAspectList.size(); i++) { 1535 CommunicationAspectAttributes attribute = communicationAspectList 1536 .get(i); 1537 if (((BooleanToken) attribute.enable.getToken()) 1538 .booleanValue()) { 1539 enabledAttributes.add(attribute); 1540 int s = ((IntToken) attribute.sequenceNumber.getToken()) 1541 .intValue(); 1542 if (s > sequenceNumber) { 1543 sequenceNumber = s; 1544 } 1545 } else { 1546 attribute.sequenceNumber.setToken(new IntToken(-1)); 1547 } 1548 } 1549 sequenceNumber = sequenceNumber + 1; 1550 for (int i = 0; i < enabledAttributes.size(); i++) { 1551 final CommunicationAspectAttributes attribute = enabledAttributes 1552 .get(i); 1553 final int seqNum = sequenceNumber; 1554 CommunicationAspect communicationAspect = (CommunicationAspect) attribute 1555 .getDecorator(); 1556 int oldSeqNum = ((IntToken) attribute.sequenceNumber 1557 .getToken()).intValue(); 1558 if (oldSeqNum == -1 && communicationAspect != null 1559 && !_communicationAspects 1560 .contains(communicationAspect)) { 1561 attribute.sequenceNumber.setToken(new IntToken(seqNum)); 1562 _communicationAspectMap.put(seqNum, attribute); 1563 sequenceNumber = sequenceNumber + 1; 1564 } else { 1565 _communicationAspectMap.put(oldSeqNum, attribute); 1566 1567 } 1568 } 1569 _communicationAspects.clear(); 1570 Iterator<Integer> iterator = _communicationAspectMap.keySet() 1571 .iterator(); 1572 int i = 1; 1573 while (iterator.hasNext()) { 1574 1575 CommunicationAspectAttributes attribute = _communicationAspectMap 1576 .get(iterator.next()); 1577 attribute.sequenceNumber.setToken(new IntToken(i)); 1578 i = i + 1; 1579 Decorator decorator = attribute.getDecorator(); 1580 if (decorator != null) { 1581 _communicationAspects 1582 .add((CommunicationAspect) decorator); 1583 } 1584 } 1585 } catch (Exception e) { 1586 e.printStackTrace(); 1587 } finally { 1588 _workspace.doneWriting(); 1589 } 1590 } 1591 return _communicationAspects; 1592 } 1593 1594 /** If the port is an input, return the receivers that receive data 1595 * from all linked relations. For an input 1596 * port, the returned value is an array of arrays. The first index 1597 * specifies the channel number. The second index specifies the 1598 * receiver number within the group of receivers that get copies from 1599 * the same channel. 1600 * <p> 1601 * For a transparent port (a port of a non-opaque entity), this method 1602 * returns receivers in ports connected to this port on the inside. 1603 * For an opaque port, the receivers returned are contained directly by 1604 * this port. 1605 * <p> 1606 * The number of channels (number of groups) is the width of the port. 1607 * <p> 1608 * For each channel, there may be any number of receivers in the group. 1609 * The individual receivers are selected using the second index of the 1610 * returned array of arrays. If there are no receivers in the group, 1611 * then the channel is represented by null. I.e., if the returned 1612 * array of arrays is <i>x</i> and the channel number is <i>c</i>, 1613 * then <i>x</i>[<i>c</i>] is null. Otherwise, it is an array, where 1614 * the size of the array is the number of receivers in the group. 1615 * If the port is opaque, then the group size is one, so only 1616 * <i>x</i>[<i>c</i>][0] is defined. If the port is transparent, 1617 * the group size is arbitrary. 1618 * <p> 1619 * For an opaque port, this method creates receivers by calling 1620 * _newReceiver() if there are no receivers or the number of receivers 1621 * does not match the width of the port. In the latter case, 1622 * previous receivers are lost, together with any data they may contain. 1623 * <p> 1624 * This method is read-synchronized on the workspace. If its cached 1625 * list of local receivers is not valid, however, then it acquires 1626 * write synchronization on the workspace to reconstruct it. 1627 * 1628 * @return The local receivers, or an empty array if there are none. 1629 */ 1630 public Receiver[][] getReceivers() { 1631 try { 1632 _workspace.getReadAccess(); 1633 1634 if (!isInput()) { 1635 return _EMPTY_RECEIVER_ARRAY; 1636 } 1637 1638 if (isOpaque()) { 1639 // Check to see whether cache is valid. 1640 if (_localReceiversVersion == _workspace.getVersion()) { 1641 return _localReceivers; 1642 } 1643 1644 // Cache not valid. Reconstruct it. 1645 int width = getWidth(); 1646 1647 if (width <= 0) { 1648 return _EMPTY_RECEIVER_ARRAY; 1649 } 1650 1651 _localReceivers = new Receiver[width][0]; 1652 1653 int index = 0; 1654 Iterator<?> relations = linkedRelationList().iterator(); 1655 1656 // NOTE: Have to be careful here to keep track of the 1657 // occurrence number of the receiver. 1658 // EAL 7/30/00. 1659 HashMap<IORelation, Integer> seen = new HashMap<IORelation, Integer>(); 1660 1661 while (relations.hasNext()) { 1662 IORelation relation = (IORelation) relations.next(); 1663 1664 // A null link (supported since indexed links) might 1665 // yield a null relation here. EAL 7/19/00. 1666 if (relation != null) { 1667 int occurrence = 0; 1668 1669 if (seen.containsKey(relation)) { 1670 // Have seen this relation before. Increment 1671 // the occurrence number. 1672 occurrence = seen.get(relation).intValue(); 1673 occurrence++; 1674 } 1675 seen.put(relation, Integer.valueOf(occurrence)); 1676 1677 Receiver[][] receiverRelation = getReceivers(relation, 1678 occurrence); 1679 1680 if (receiverRelation != null) { 1681 for (Receiver[] element : receiverRelation) { 1682 _localReceivers[index++] = element; 1683 } 1684 } 1685 } 1686 } 1687 1688 _localReceiversVersion = _workspace.getVersion(); 1689 return _localReceivers; 1690 } else { 1691 // Transparent port. 1692 return deepGetReceivers(); 1693 } 1694 } catch (IllegalActionException ex) { 1695 // This would be thrown only if the above call to 1696 // getReceivers(IORelation, int) throws. This should not 1697 // occur because we are sure the IORelation is connected. 1698 throw new InternalErrorException(this, ex, 1699 "Expected relation to be connected!"); 1700 } finally { 1701 _workspace.doneReading(); 1702 } 1703 } 1704 1705 /** If the port is an input, return receivers that handle incoming 1706 * channels from the specified relation. If the port is an opaque output 1707 * and the relation is inside linked, return the receivers that handle 1708 * incoming channels from the inside. Since the port may be linked 1709 * multiple times to the specified relation, this method only returns 1710 * the relations correspond to the first occurrence. 1711 * The returned value is an array of arrays of the same form 1712 * as that returned by getReceivers() with no arguments. Note that a 1713 * single occurrence of a relation may represent multiple channels 1714 * because it may be a bus. If there are no matching receivers, 1715 * then return an empty array. 1716 * <p> 1717 * This method is read-synchronized on the workspace. 1718 * 1719 * @param relation Relations that are linked on the outside or inside. 1720 * @return The local receivers. 1721 * @exception IllegalActionException If the relation is not linked 1722 * from the outside, or if there is no director. 1723 */ 1724 public Receiver[][] getReceivers(IORelation relation) 1725 throws IllegalActionException { 1726 return getReceivers(relation, 0); 1727 } 1728 1729 /** If the port is an input, return receivers that handle incoming 1730 * channels from the specified relation. If the port is an opaque output 1731 * and the relation is inside linked, return the receivers that handle 1732 * incoming channels from the inside. Since the port may be linked 1733 * multiple times to the specified relation, the <i>occurrences</i> 1734 * argument specifies which of the links we wish to examine. 1735 * The returned value is an array of arrays of the same form 1736 * as that returned by getReceivers() with no arguments. Note that a 1737 * single occurrence of a relation may represent multiple channels 1738 * because it may be a bus. If there are no matching receivers, 1739 * then return an empty array. 1740 * <p> 1741 * This method is read-synchronized on the workspace. 1742 * 1743 * @param relation Relations that are linked on the outside or inside. 1744 * @param occurrence The occurrence number that we are interested in, 1745 * starting at 0. 1746 * @return The local receivers, or an empty array if there are none. 1747 * @exception IllegalActionException If the relation is not linked 1748 * from the outside. 1749 */ 1750 public Receiver[][] getReceivers(IORelation relation, int occurrence) 1751 throws IllegalActionException { 1752 try { 1753 _workspace.getReadAccess(); 1754 1755 // Allow inside relations also to support opaque, 1756 // non-atomic entities. 1757 boolean insideLink = isInsideLinked(relation); 1758 1759 if (!isLinked(relation) && !insideLink) { 1760 throw new IllegalActionException(this, relation, 1761 "getReceivers: Relation argument is not linked " 1762 + "to me."); 1763 } 1764 1765 return _getReceivers(relation, occurrence, insideLink); 1766 } finally { 1767 _workspace.doneReading(); 1768 } 1769 } 1770 1771 /** If the port is an output, return the remote receivers that can 1772 * receive from the port. For an output 1773 * port, the returned value is an array of arrays of the same form 1774 * as that returned by getReceivers() with no arguments. The length 1775 * of the array is the width of the port (the number of channels). 1776 * It is an array of arrays, each of which represents a group of 1777 * receivers that receive data from the same channel. 1778 * <p> 1779 * This method may have the effect of creating new receivers in the 1780 * remote input ports, if they do not already have the right number of 1781 * receivers. In this case, previous receivers are lost, together 1782 * with any data they may contain. 1783 * <p> 1784 * This method is read-synchronized on the workspace. 1785 * @return The receivers for output data, or an empty array if there 1786 * are none. 1787 * @exception IllegalActionException If thrown while getting the 1788 * width of this port, getting the deep receives of a relation or getting the 1789 * width of a relation. 1790 */ 1791 public Receiver[][] getRemoteReceivers() throws IllegalActionException { 1792 try { 1793 _workspace.getReadAccess(); 1794 1795 if (!isOutput()) { 1796 return _EMPTY_RECEIVER_ARRAY; 1797 } 1798 1799 int width = getWidth(); 1800 1801 if (width <= 0) { 1802 return _EMPTY_RECEIVER_ARRAY; 1803 } 1804 1805 // For opaque port, try the cached _farReceivers 1806 // Check validity of cached version 1807 if (isOpaque() && _farReceiversVersion == _workspace.getVersion()) { 1808 return _farReceivers; 1809 } 1810 1811 if (_intermediateFarReceiver != null) { 1812 _farReceivers = new Receiver[width][]; 1813 for (int i = 0; i < width; i++) { 1814 _farReceivers[i] = new Receiver[1]; 1815 _farReceivers[i][0] = _intermediateFarReceiver; 1816 } 1817 return _farReceivers; 1818 } 1819 1820 // If not an opaque port or Cache is not valid. Reconstruct it. 1821 Receiver[][] farReceivers = new Receiver[width][0]; 1822 Iterator<?> relations = linkedRelationList().iterator(); 1823 int index = 0; 1824 1825 while (relations.hasNext()) { 1826 IORelation relation = (IORelation) relations.next(); 1827 1828 // A null link (supported since indexed links) might 1829 // yield a null relation here. EAL 7/19/00. 1830 if (relation != null) { 1831 Receiver[][] deepReceivers = relation.deepReceivers(this); 1832 1833 if (deepReceivers != null && deepReceivers.length > 0) { 1834 for (int i = 0; i < deepReceivers.length; i++) { 1835 try { 1836 farReceivers[index] = deepReceivers[i]; 1837 } catch (ArrayIndexOutOfBoundsException ex) { 1838 // ArrayIndexOutOfBounds was thrown by a bug in code generation. 1839 // The previous error message was meaningless. 1840 throw new InternalErrorException(this, ex, 1841 "Failed to set farReceivers[" + index 1842 + "] = deepReceivers[" + i 1843 + "]. " 1844 + "farReceivers.length = " 1845 + farReceivers.length 1846 + " deepReceivers.length = " 1847 + deepReceivers.length + "."); 1848 } 1849 index++; 1850 } 1851 } else { 1852 // create a number of null entries in farReceivers 1853 // corresponding to the width of relation r 1854 index += relation.getWidth(); 1855 if (index >= farReceivers.length) { 1856 // No place to put any further receivers (paranoid coding). 1857 break; 1858 } 1859 } 1860 } 1861 } 1862 1863 // For an opaque port, cache the result. 1864 if (isOpaque()) { 1865 _farReceiversVersion = _workspace.getVersion(); 1866 _farReceivers = farReceivers; 1867 } 1868 1869 return farReceivers; 1870 } finally { 1871 _workspace.doneReading(); 1872 } 1873 } 1874 1875 /** If this port is an output, return the remote receivers that can 1876 * receive data from this port through the specified relation or 1877 * any relation in its relation group. 1878 * The relation or one in its relation group should be linked to the port 1879 * from the inside, otherwise an exception is thrown. For an output 1880 * port, the returned value is an array of arrays of the same form 1881 * as that returned by getReceivers() with no arguments. 1882 * <p> 1883 * This method may have the effect of creating new receivers in the 1884 * remote input ports, if they do not already have the right number of 1885 * receivers. In this case, previous receivers are lost, together 1886 * with any data they may contain. 1887 * <p> 1888 * This method is read-synchronized on the workspace. 1889 * @param relation The specified relation from which the remote 1890 * receivers can receive data. 1891 * @return The receivers for output data, or an empty array if there 1892 * are none. 1893 * @exception IllegalActionException If the IORelation is not linked 1894 * to the port from the inside. 1895 */ 1896 public Receiver[][] getRemoteReceivers(IORelation relation) 1897 throws IllegalActionException { 1898 try { 1899 _workspace.getReadAccess(); 1900 1901 if (!isInsideGroupLinked(relation)) { 1902 throw new IllegalActionException(this, relation, 1903 "not linked from the inside."); 1904 } 1905 1906 if (!isOutput()) { 1907 return _EMPTY_RECEIVER_ARRAY; 1908 } 1909 1910 int width = relation.getWidth(); 1911 1912 if (width <= 0) { 1913 return _EMPTY_RECEIVER_ARRAY; 1914 } 1915 1916 // no cache used. 1917 Receiver[][] outsideReceivers = getRemoteReceivers(); 1918 1919 if (outsideReceivers == null) { 1920 return _EMPTY_RECEIVER_ARRAY; 1921 } 1922 1923 Receiver[][] result = new Receiver[width][]; 1924 Iterator<?> insideRelations = insideRelationList().iterator(); 1925 int index = 0; 1926 1927 while (insideRelations.hasNext()) { 1928 IORelation insideRelation = (IORelation) insideRelations.next(); 1929 1930 if (insideRelation.relationGroupList().contains(relation)) { 1931 int size = java.lang.Math.min(width, 1932 outsideReceivers.length - index); 1933 1934 //NOTE: if size = 0, the for loop is skipped. 1935 for (int i = 0; i < size; i++) { 1936 result[i] = outsideReceivers[i + index]; 1937 } 1938 1939 break; 1940 } 1941 // Calling getWidth on a relation for which the width has to be 1942 // inferred will trigger the width inference algorithm here. 1943 index += insideRelation.getWidth(); 1944 } 1945 1946 return result; 1947 } finally { 1948 _workspace.doneReading(); 1949 } 1950 } 1951 1952 /** Return the width of the port. The width is the sum of the 1953 * widths of the relations that the port is linked to (on the outside). 1954 * Note that this method cannot be used to determine whether a port 1955 * is connected (deeply) to another port that can either supply it with 1956 * data or consume data it produces. The correct methods to use to 1957 * determine that are numberOfSinks() and numberOfSources(). 1958 * This method is read-synchronized on the workspace. 1959 * This method will trigger the width inference algorithm if necessary. 1960 * @see #numberOfSinks() 1961 * @see #numberOfSources() 1962 * @return The width of the port. 1963 * @exception IllegalActionException If thrown while calling _getWidth() 1964 */ 1965 public int getWidth() throws IllegalActionException { 1966 boolean createReceivers = true; 1967 return _getWidth(createReceivers); 1968 } 1969 1970 /** Get the width from the constraints put on the width 1971 * of this port if the width is fully determined. 1972 * If it is not possible to determine the width yet 1973 * (for example because dependent relations also don't have 1974 * their width inferred), -1 is returned 1975 * @return The width. 1976 */ 1977 public int getWidthFromConstraints() { 1978 for (Parameter parameter : _widthEqualToParameter) { 1979 try { 1980 IntToken t = (IntToken) parameter.getToken(); 1981 1982 if (t != null) { 1983 return t.intValue(); 1984 } 1985 } catch (Exception e) { 1986 // It it throws, it means we can't evaluate the 1987 // parameter yet. 1988 continue; 1989 } 1990 } 1991 1992 for (IOPort port : _widthEqualToPort) { 1993 try { 1994 Set<IORelation> outsideUnspecifiedWidths = RelationWidthInference 1995 ._relationsWithUnspecifiedWidths( 1996 port.linkedRelationList()); 1997 // It there is still a outsideUnspecifiedWidths, the width 1998 // is not yet completely specified. 1999 if (outsideUnspecifiedWidths.isEmpty()) { 2000 int outsideWidth = port._getOutsideWidth(null); 2001 return outsideWidth; 2002 } 2003 } catch (Exception e) { 2004 // It it throws, it means we can't evaluate the 2005 // width yet. 2006 continue; 2007 } 2008 } 2009 2010 // No information available yet 2011 return -1; 2012 } 2013 2014 /** Return the inside width of this port. The inside width is the 2015 * sum of the widths of the relations that the port is linked to 2016 * on the inside. This method is read-synchronized on the 2017 * workspace. 2018 * This method will trigger the width inference algorithm if necessary. 2019 * 2020 * @return The width of the inside of the port. 2021 * @exception IllegalActionException If thrown while getting the 2022 * width of the relations or while creating receivers. 2023 */ 2024 public int getWidthInside() throws IllegalActionException { 2025 try { 2026 _workspace.getReadAccess(); 2027 2028 long version = _workspace.getVersion(); 2029 2030 if (_insideWidthVersion != version) { 2031 2032 int sum = 0; 2033 Iterator<?> relations = insideRelationList().iterator(); 2034 2035 while (relations.hasNext()) { 2036 IORelation relation = (IORelation) relations.next(); 2037 2038 // A null link (supported since indexed links) might 2039 // yield a null relation here. EAL 7/19/00. 2040 if (relation != null) { 2041 // Calling getWidth on a relation for which the width has to be 2042 // inferred will trigger the width inference algorithm here. 2043 sum += relation.getWidth(); 2044 } 2045 } 2046 if (_insideWidth != sum) { 2047 // Need to re-create receivers for Pub/Sub in Opaques. 2048 // See _getWidth() for a similar piece of code. 2049 // If we don't check to see that sum < _insideWidth, 2050 // then actor/process/test/Branch.tcl has tests that 2051 // fail because we end up creating new receivers that 2052 // are not producer receivers. 2053 if (sum < _insideWidth && isOpaque()) { 2054 createReceivers(); 2055 } 2056 _insideWidth = sum; 2057 _insideWidthVersion = version; 2058 } 2059 } 2060 2061 return _insideWidth; 2062 } finally { 2063 _workspace.doneReading(); 2064 } 2065 } 2066 2067 /** Return true if the specified channel has a new token to deliver 2068 * via the get() method. This differs from {@link #hasToken(int)} 2069 * in that it does not return true just because the port is persistent 2070 * or the most recently received input was a {@link SmoothToken}. 2071 * If this port is not an input, or if the 2072 * channel index is out of range, then throw an exception. 2073 * Note that this does not report any tokens in inside receivers 2074 * of an output port. Those are accessible only through 2075 * getInsideReceivers(). 2076 * 2077 * @param channelIndex The channel index. 2078 * @return True if there is a token in the channel. 2079 * @exception IllegalActionException If the receivers do not support 2080 * this query, if there is no director, and hence no receivers, 2081 * if the port is not an input port, or if the channel index is out 2082 * of range. 2083 */ 2084 public boolean hasNewToken(int channelIndex) throws IllegalActionException { 2085 // The getReceivers() method throws an IllegalActionException if 2086 // there's no director. 2087 Receiver[][] receivers = getReceivers(); 2088 boolean result = false; 2089 2090 if (receivers != null && channelIndex >= receivers.length) { 2091 if (!isInput()) { 2092 throw new IllegalActionException(this, 2093 "Port is not an input port!"); 2094 } else { 2095 throw new IllegalActionException(this, 2096 "Channel index " + channelIndex 2097 + " is out of range, because width is only " 2098 + getWidth() + "."); 2099 } 2100 } 2101 2102 if (receivers != null && receivers[channelIndex] != null) { 2103 for (int j = 0; j < receivers[channelIndex].length; j++) { 2104 if (receivers[channelIndex][j].hasToken()) { 2105 result = true; 2106 break; 2107 } 2108 } 2109 } 2110 2111 if (_debugging) { 2112 _debug("hasToken on channel " + channelIndex + " returns " 2113 + result); 2114 } 2115 2116 return result; 2117 } 2118 2119 /** Return true if the specified channel has a token to deliver 2120 * via the getInside() method. This differs from {@link #hasTokenInside(int)} 2121 * in that it does not return true just because the port is persistent 2122 * or the most recently received input was a {@link SmoothToken}. 2123 * If this port is not an output, or 2124 * if the channel index is out of range, then throw an exception. 2125 * Note that this does not report any tokens in receivers of an 2126 * input port. 2127 * 2128 * @param channelIndex The channel index. 2129 * @return True if there is a token in the channel. 2130 * @exception IllegalActionException If the receivers do not support 2131 * this query, if there is no director, and hence no receivers, 2132 * if the port is not an output port, or if the channel index is out 2133 * of range. 2134 */ 2135 public boolean hasNewTokenInside(int channelIndex) 2136 throws IllegalActionException { 2137 // The getInsideReceivers() method throws an 2138 // IllegalActionException if there's no director. 2139 Receiver[][] receivers = getInsideReceivers(); 2140 boolean result = false; 2141 2142 if (channelIndex >= receivers.length) { 2143 if (!isOutput()) { 2144 throw new IllegalActionException(this, 2145 "Port is not an output port!"); 2146 } else { 2147 throw new IllegalActionException(this, "Channel index " 2148 + channelIndex 2149 + " is out of range, because inside width is only " 2150 + getWidthInside() + "."); 2151 } 2152 } 2153 2154 if (receivers[channelIndex] != null) { 2155 for (int j = 0; j < receivers[channelIndex].length; j++) { 2156 if (receivers[channelIndex][j].hasToken()) { 2157 result = true; 2158 break; 2159 } 2160 } 2161 } 2162 2163 if (_debugging) { 2164 _debug("hasTokenInside on channel " + channelIndex + " returns " 2165 + result); 2166 } 2167 2168 return result; 2169 } 2170 2171 /** Return true if the specified channel can accept a token via the 2172 * put() method. If this port is not an output, or the channel index 2173 * is out of range, then throw IllegalActionException. If there 2174 * are multiple receivers in the group associated with the channel, 2175 * then return true only if all the receivers can accept a token. 2176 * 2177 * @param channelIndex The channel index. 2178 * @return True if there is room for a token in the channel. 2179 * @exception IllegalActionException If the receivers do not support 2180 * this query, if this is not an output port, or if the channel index 2181 * is out of range. 2182 */ 2183 public boolean hasRoom(int channelIndex) throws IllegalActionException { 2184 boolean result = true; 2185 2186 try { 2187 Receiver[][] farReceivers = getRemoteReceivers(); 2188 2189 if (farReceivers == null || farReceivers[channelIndex] == null) { 2190 result = false; 2191 } else { 2192 for (int j = 0; j < farReceivers[channelIndex].length; j++) { 2193 if (!farReceivers[channelIndex][j].hasRoom()) { 2194 result = false; 2195 break; 2196 } 2197 } 2198 } 2199 } catch (ArrayIndexOutOfBoundsException ex) { 2200 // NOTE: This might be thrown if the port is not an output port. 2201 throw new IllegalActionException(this, 2202 "hasRoom: channel index is out of range."); 2203 } 2204 2205 if (_debugging) { 2206 _debug("hasRoom on channel " + channelIndex + " returns " + result); 2207 } 2208 2209 return result; 2210 } 2211 2212 /** Return true if the specified channel can accept a token via 2213 * the putInside() method. If this port is not an input, or the 2214 * channel index is out of range, then throw 2215 * IllegalActionException. If there are multiple receivers in 2216 * the group associated with the channel, then return true only 2217 * if all the receivers can accept a token. 2218 * 2219 * @param channelIndex The channel index. 2220 * @return True if there is room for a token in the channel. 2221 * @exception IllegalActionException If the receivers do not 2222 * support this query, if this is not an input port, or if the 2223 * channel index is out of range. 2224 */ 2225 public boolean hasRoomInside(int channelIndex) 2226 throws IllegalActionException { 2227 boolean result = true; 2228 2229 try { 2230 Receiver[][] farReceivers = getInsideReceivers(); 2231 2232 if (farReceivers == null || farReceivers[channelIndex] == null) { 2233 result = false; 2234 } else { 2235 for (int j = 0; j < farReceivers[channelIndex].length; j++) { 2236 if (!farReceivers[channelIndex][j].hasRoom()) { 2237 result = false; 2238 break; 2239 } 2240 } 2241 } 2242 } catch (ArrayIndexOutOfBoundsException ex) { 2243 // NOTE: This might be thrown if the port is not an output port. 2244 throw new IllegalActionException(this, 2245 "hasRoom: channel index is out of range."); 2246 } 2247 2248 if (_debugging) { 2249 _debug("hasRoomInside on channel " + channelIndex + " returns " 2250 + result); 2251 } 2252 2253 return result; 2254 } 2255 2256 /** Return true if the port is persistent (see {@link #defaultValue}), 2257 * or if the most recent input was an {@link SmoothToken}, or 2258 * if the specified channel has a token to deliver 2259 * via the get() method. If this port is not an input, or if the 2260 * channel index is out of range, then throw an exception. 2261 * Note that this does not report any tokens in inside receivers 2262 * of an output port. Those are accessible only through 2263 * getInsideReceivers(). 2264 * 2265 * @param channelIndex The channel index. 2266 * @return True if there is a token in the channel. 2267 * @exception IllegalActionException If the receivers do not support 2268 * this query, if there is no director, and hence no receivers, 2269 * if the port is not an input port, or if the channel index is out 2270 * of range. 2271 */ 2272 public boolean hasToken(int channelIndex) throws IllegalActionException { 2273 if (_getPersistentValue(channelIndex, false) != null) { 2274 return true; 2275 } 2276 return hasNewToken(channelIndex); 2277 } 2278 2279 /** Return true if the specified channel has the specified number 2280 * of tokens to deliver via the get() method. 2281 * If this port is not an input, or if the 2282 * channel index is out of range, then throw an exception. 2283 * Note that this does not report any tokens in inside receivers 2284 * of an output port. Those are accessible only through 2285 * getInsideReceivers(). 2286 * 2287 * @param channelIndex The channel index. 2288 * @param tokens The number of tokens to query the channel for. 2289 * @return True if there is a token in the channel. 2290 * @exception IllegalActionException If the receivers do not support 2291 * this query, if there is no director, and hence no receivers, 2292 * if the port is not an input port, or if the channel index is out 2293 * of range. 2294 */ 2295 public boolean hasToken(int channelIndex, int tokens) 2296 throws IllegalActionException { 2297 if (_getPersistentValue(channelIndex, false) != null) { 2298 // FIXME: There is a corner case where this isn't quite right. 2299 // If a SmoothToken is received during retrieval of the vector of 2300 // inputs, then the port becomes persistent. But it may not be 2301 // persistent yet, in which case, this method will return false, 2302 // when it actually should have returned true. But there is no 2303 // way to determine this without actually reading the tokens! 2304 // Fortunately, it makes little sense to use vector reads with 2305 // SmoothTokens, so this is extremely unlikely to come up. 2306 // And it can be worked around by setting a defaultValue. 2307 return true; 2308 } 2309 2310 boolean result = false; 2311 2312 try { 2313 // The getReceivers() method throws an IllegalActionException if 2314 // there's no director. 2315 Receiver[][] receivers = getReceivers(); 2316 2317 if (receivers != null && receivers[channelIndex] != null) { 2318 for (int j = 0; j < receivers[channelIndex].length; j++) { 2319 if (receivers[channelIndex][j].hasToken(tokens)) { 2320 result = true; 2321 break; 2322 } 2323 } 2324 } 2325 } catch (ArrayIndexOutOfBoundsException ex) { 2326 // NOTE: This might be thrown if the port is not an output port. 2327 throw new IllegalActionException(this, 2328 "hasToken: channel index is out of range."); 2329 } 2330 2331 if (_debugging) { 2332 _debug("hasToken on channel " + channelIndex + " returns " + result 2333 + ", with " + tokens + " tokens requested"); 2334 } 2335 2336 return result; 2337 } 2338 2339 /** Return true if the port is persisent or the specified channel 2340 * has a token to deliver 2341 * via the getInside() method. If this port is not an output, or 2342 * if the channel index is out of range, then throw an exception. 2343 * Note that this does not report any tokens in receivers of an 2344 * input port. 2345 * 2346 * @param channelIndex The channel index. 2347 * @return True if there is a token in the channel. 2348 * @exception IllegalActionException If the receivers do not support 2349 * this query, if there is no director, and hence no receivers, 2350 * if the port is not an output port, or if the channel index is out 2351 * of range. 2352 */ 2353 public boolean hasTokenInside(int channelIndex) 2354 throws IllegalActionException { 2355 if (_getPersistentValue(channelIndex, true) != null) { 2356 // FIXME: See limitation documented in hasToken(int). 2357 return true; 2358 } 2359 2360 return hasNewTokenInside(channelIndex); 2361 } 2362 2363 /** Return whether there are constraints on the width of 2364 * this port. There are constraints in case the method 2365 * setWidthEquals has been called. 2366 * @return True when there are constraints on the width. 2367 * @see #setWidthEquals(Parameter) 2368 * @see #setWidthEquals(IOPort, boolean) 2369 */ 2370 public boolean hasWidthConstraints() { 2371 return !_widthEqualToParameter.isEmpty() 2372 || !_widthEqualToPort.isEmpty(); 2373 } 2374 2375 /** Override the base class to invalidate the schedule and resolved 2376 * types of the director of the container, if there is one, in addition 2377 * to what the base class does. 2378 * @param index The index at which to insert the link. 2379 * @param relation The relation to link to this port. 2380 * @exception IllegalActionException If the link would cross levels of 2381 * the hierarchy, or the relation is incompatible, 2382 * or the port has no container, or the port is not in the 2383 * same workspace as the relation, or if this port is not a multiport 2384 * and the index is greater than zero or if another link already exists. 2385 */ 2386 @Override 2387 public void insertLink(int index, Relation relation) 2388 throws IllegalActionException { 2389 if (!isMultiport()) { 2390 if (index > 0) { 2391 throw new IllegalActionException(this, 2392 "Cannot insert link at an index greater than " 2393 + "zero in a port that is not a multiport."); 2394 } else if (_isInsideLinkable(relation)) { 2395 if (numInsideLinks() > 0) { 2396 throw new IllegalActionException(this, 2397 "Cannot insert a second inside link in a " 2398 + "port that is not a multiport."); 2399 } 2400 } else if (numLinks() > 0) { 2401 throw new IllegalActionException(this, 2402 "Cannot insert a second link in a port that is not a " 2403 + "multiport."); 2404 } 2405 } 2406 2407 super.insertLink(index, relation); 2408 _invalidate(); 2409 } 2410 2411 /** Return a list of the ports that may accept data from this port 2412 * when it sends on the inside. In this base class, this includes 2413 * both input ports and opaque output ports that are 2414 * connected on the inside to this port, which are the ports that 2415 * will receive data from this one if data is sent on the inside. 2416 * However, derived classes are free to return ports on this list 2417 * that <i>may</i> receive data from this port, even if they are 2418 * not actually currently connected. The wireless domain, for 2419 * example, takes advantage of this and includes ports on the 2420 * inside that share the same channel. This port must 2421 * be an opaque input port, otherwise return an empty list. 2422 * @see #deepGetReceivers() 2423 * @return A list of IOPort objects. 2424 */ 2425 public List<IOPort> insideSinkPortList() { 2426 // NOTE: This doesn't just get the containers of the 2427 // receivers returned by deepGetReceivers() 2428 // because the receivers may not have yet been created. 2429 // FIXME: This should cache the result. 2430 try { 2431 _workspace.getReadAccess(); 2432 2433 Nameable container = getContainer(); 2434 2435 if (!(container instanceof CompositeActor && isInput() 2436 && isOpaque())) { 2437 // Return an empty list, since this port cannot send data 2438 // to the inside. 2439 return new LinkedList<IOPort>(); 2440 } 2441 2442 Director dir = ((CompositeActor) container).getDirector(); 2443 int depthOfDirector = dir.depthInHierarchy(); 2444 LinkedList<IOPort> result = new LinkedList<IOPort>(); 2445 Iterator<?> ports = deepInsidePortList().iterator(); 2446 2447 while (ports.hasNext()) { 2448 IOPort port = (IOPort) ports.next(); 2449 int depth = port.getContainer().depthInHierarchy(); 2450 2451 if (port.isInput() && depth >= depthOfDirector) { 2452 result.addLast(port); 2453 } else if (port.isOutput() && depth < depthOfDirector) { 2454 result.addLast(port); 2455 } 2456 } 2457 2458 return result; 2459 } finally { 2460 _workspace.doneReading(); 2461 } 2462 } 2463 2464 /** Return a list of the ports that can send data to this port 2465 * from the inside. This includes both output ports and opaque 2466 * input ports that are connected on the inside to this port. 2467 * These are the ports that will send data from this one from the inside. 2468 * However, derived classes are free to return ports on this list 2469 * that <i>may</i> send data to this port, even if they are 2470 * not actually currently connected. The wireless domain, for 2471 * example, takes advantage of this and includes ports on the 2472 * inside that share the same channel. This port must 2473 * be an opaque output port, otherwise return an empty list. 2474 * @return A list of IOPort objects. 2475 */ 2476 public List<IOPort> insideSourcePortList() { 2477 try { 2478 _workspace.getReadAccess(); 2479 2480 Nameable container = getContainer(); 2481 2482 if (!(container instanceof CompositeActor && isOutput() 2483 && isOpaque())) { 2484 // Return an empty list, since this port cannot receive data 2485 // from the inside. 2486 return new LinkedList<IOPort>(); 2487 } 2488 2489 Director dir = ((CompositeActor) container).getDirector(); 2490 int depthOfDirector = dir.depthInHierarchy(); 2491 LinkedList<IOPort> result = new LinkedList<IOPort>(); 2492 Iterator<?> ports = deepInsidePortList().iterator(); 2493 2494 while (ports.hasNext()) { 2495 IOPort port = (IOPort) ports.next(); 2496 int depth = port.getContainer().depthInHierarchy(); 2497 2498 if (port.isInput() && depth < depthOfDirector) { 2499 result.addLast(port); 2500 } else if (port.isOutput() && depth >= depthOfDirector) { 2501 result.addLast(port); 2502 } 2503 } 2504 2505 return result; 2506 } finally { 2507 _workspace.doneReading(); 2508 } 2509 } 2510 2511 /** Invalidate the communication aspect list. */ 2512 public void invalidateCommunicationAspects() { 2513 } 2514 2515 /** Return true if the port is an input. The port is an input 2516 * if either setInput() has been called with a <i>true</i> argument, or 2517 * it is connected on the inside to an input port, or if it is 2518 * connected on the inside to the inside of an output port. 2519 * In other words, it is an input if data can be put directly into 2520 * it or sent through it to an input. 2521 * This method is read-synchronized on the workspace. 2522 * 2523 * @return True if the port is an input. 2524 */ 2525 public boolean isInput() { 2526 if (_isInputOutputStatusSet) { 2527 return _isInput; 2528 } 2529 2530 // Status has not been set. Try to infer it. 2531 long version = _workspace.getVersion(); 2532 2533 if (_insideInputVersion != version) { 2534 try { 2535 _workspace.getReadAccess(); 2536 2537 // Check to see whether any port linked on the inside 2538 // is an input. 2539 _isInput = false; // By default we are not an input port. 2540 2541 Iterator<?> ports = deepInsidePortList().iterator(); 2542 2543 while (ports.hasNext()) { 2544 IOPort p = (IOPort) ports.next(); 2545 2546 // Rule out case where this port itself is listed... 2547 if (p != this && p.isInput()) { 2548 _isInput = true; 2549 } 2550 } 2551 2552 _insideInputVersion = version; 2553 } finally { 2554 _workspace.doneReading(); 2555 } 2556 } 2557 2558 return _isInput; 2559 } 2560 2561 /** Return whether the port has relations connected on the inside. 2562 * @return True when a relation != null is connected on the inside. 2563 */ 2564 public boolean isInsideConnected() { 2565 for (Object relationObject : insideRelationList()) { 2566 if (relationObject != null) { 2567 return true; 2568 } 2569 } 2570 return false; 2571 } 2572 2573 /** Return true if all channels of this port have known state; that is, 2574 * the tokens on each channel are known, or each channel is known not to 2575 * have any tokens. 2576 * <p> 2577 * This method supports domains, such as SR, which have fixed-point 2578 * semantics. In such domains, an iteration of a model starts with 2579 * the state of all channels unknown, and the iteration concludes when 2580 * the state of all channels is known. 2581 * @see #isKnown(int) 2582 * @see #isKnownInside(int) 2583 * @return True if it is known whether there is a token in each channel. 2584 * @exception IllegalActionException If the receivers do not support 2585 * this query, or if there is no director, and hence no receivers. 2586 */ 2587 public boolean isKnown() throws IllegalActionException { 2588 boolean result = true; 2589 2590 for (int j = 0; j < getWidth(); j++) { 2591 if (!isKnown(j)) { 2592 result = false; 2593 break; 2594 } 2595 } 2596 2597 if (_debugging) { 2598 _debug("isKnown returns " + result); 2599 } 2600 2601 return result; 2602 } 2603 2604 /** Return <i>true</i> if the specified channel has known state; 2605 * that is, the tokens on this channel are known, or this channel 2606 * is known not to have any tokens. 2607 * If the channel index is out of range, then throw 2608 * an exception. If the port is an input and an output, then both 2609 * the receivers in this port (for the input) and the remote 2610 * receivers (for the output) must be known to return true. 2611 * If the port is neither an input nor an output, then return true. 2612 * <p> 2613 * This method supports domains, such as SR, which have fixed-point 2614 * semantics. In such domains, an iteration of a model starts with 2615 * the state of all channels unknown, and the iteration concludes when 2616 * the state of all channels is known. 2617 * @see #isKnown() 2618 * @see #isKnownInside(int) * 2619 * @param channelIndex The channel index. 2620 * @return True if it is known whether there is a token in the channel. 2621 * @exception IllegalActionException If the receivers do not support 2622 * this query, if there is no director, and hence no receivers, 2623 * if the port is not an input port, or if the channel index is out 2624 * of range. 2625 */ 2626 public boolean isKnown(int channelIndex) throws IllegalActionException { 2627 boolean result = true; 2628 2629 try { 2630 if (isInput()) { 2631 Receiver[][] receivers = getReceivers(); 2632 2633 if (receivers.length <= channelIndex) { 2634 throw new IllegalActionException(this, 2635 "Channel index is out of range: " + channelIndex); 2636 } 2637 2638 if (receivers[channelIndex] != null) { 2639 for (int j = 0; j < receivers[channelIndex].length; j++) { 2640 if (!receivers[channelIndex][j].isKnown()) { 2641 result = false; 2642 break; 2643 } 2644 } 2645 } 2646 } 2647 2648 if (result && isOutput()) { 2649 Receiver[][] receivers = getRemoteReceivers(); 2650 2651 if (receivers.length <= channelIndex) { 2652 throw new IllegalActionException(this, 2653 "Channel index is out of range: " + channelIndex); 2654 } 2655 2656 if (receivers[channelIndex] != null) { 2657 for (int j = 0; j < receivers[channelIndex].length; j++) { 2658 if (!receivers[channelIndex][j].isKnown()) { 2659 result = false; 2660 break; 2661 } 2662 } 2663 } 2664 } 2665 } catch (ArrayIndexOutOfBoundsException ex) { 2666 throw new IllegalActionException(this, 2667 "isKnown: channel index is out of range."); 2668 } 2669 2670 if (_debugging) { 2671 _debug("isKnown on channel " + channelIndex + " returns " + result); 2672 } 2673 2674 return result; 2675 } 2676 2677 /** Return <i>true</i> if the specified inside channel has known state; 2678 * that is, the tokens on this channel are known, or this channel 2679 * is known not to have any tokens. If the channel index is out 2680 * of range, then throw an exception. 2681 * If the port is an input and an output, then both 2682 * the receivers in this port (for the input) and the remote 2683 * receivers (for the output) must be known to return true. 2684 * If the port is neither an input nor an output, then return true. 2685 * <p> 2686 * This method supports domains, such as SR, which have fixed-point 2687 * semantics. In such domains, an iteration of a model starts with 2688 * the state of all channels unknown, and the iteration concludes when 2689 * the state of all channels is known. 2690 * 2691 * @param channelIndex The channel index. 2692 * @return True if it is known whether there is a token in the channel. 2693 * @exception IllegalActionException If the receivers do not 2694 * support this query, if there is no director, and hence no 2695 * receivers, or if the inside channel index is out of range. 2696 */ 2697 public boolean isKnownInside(int channelIndex) 2698 throws IllegalActionException { 2699 boolean result = true; 2700 2701 try { 2702 if (isOutput()) { 2703 Receiver[][] receivers = getInsideReceivers(); 2704 2705 if (receivers != null && receivers[channelIndex] != null) { 2706 for (int j = 0; j < receivers[channelIndex].length; j++) { 2707 if (!receivers[channelIndex][j].isKnown()) { 2708 result = false; 2709 break; 2710 } 2711 } 2712 } 2713 } 2714 2715 if (result && isInput()) { 2716 Receiver[][] receivers = deepGetReceivers(); 2717 2718 if (receivers != null && receivers[channelIndex] != null) { 2719 for (int j = 0; j < receivers[channelIndex].length; j++) { 2720 if (!receivers[channelIndex][j].isKnown()) { 2721 result = false; 2722 break; 2723 } 2724 } 2725 } 2726 } 2727 } catch (ArrayIndexOutOfBoundsException ex) { 2728 throw new IllegalActionException(this, 2729 "isKnownInside: channel index is out of range."); 2730 } 2731 2732 if (_debugging) { 2733 _debug("isKnownInside on channel " + channelIndex + " returns " 2734 + result); 2735 } 2736 2737 return result; 2738 } 2739 2740 /** Return true if the port is a multiport. The port is a multiport 2741 * if setMultiport() has been called with a true argument. 2742 * 2743 * @return True if the port is a multiport. 2744 */ 2745 public boolean isMultiport() { 2746 // No need to synchronize this because the action is atomic 2747 // and synchronization would just ensure that no write action 2748 // is in progress. 2749 return _isMultiport; 2750 } 2751 2752 /** Return true if the port is an output. The port is an output 2753 * if either setOutput() has been called with a true argument, or 2754 * it is connected on the inside to an output port, or it is 2755 * connected on the inside to the inside of an input port. 2756 * This method is read-synchronized on the workspace. 2757 * 2758 * @return True if the port is an output. 2759 */ 2760 public boolean isOutput() { 2761 if (_isInputOutputStatusSet) { 2762 return _isOutput; 2763 } 2764 2765 // Status has not been set. Try to infer it. 2766 long version = _workspace.getVersion(); 2767 2768 if (_insideOutputVersion != version) { 2769 try { 2770 _workspace.getReadAccess(); 2771 2772 // Check to see whether any port linked on the 2773 // inside is an output. 2774 _isOutput = false; // By default we are not an output port. 2775 2776 Iterator<?> ports = deepInsidePortList().iterator(); 2777 2778 while (ports.hasNext()) { 2779 IOPort p = (IOPort) ports.next(); 2780 2781 // Rule out case where this port itself is listed... 2782 if (p != this && p.isOutput()) { 2783 _isOutput = true; 2784 } 2785 } 2786 2787 _insideOutputVersion = version; 2788 } finally { 2789 _workspace.doneReading(); 2790 } 2791 } 2792 2793 return _isOutput; 2794 } 2795 2796 /** Return whether the port has relations connected on the outside. 2797 * @return True when a relation != null is connected on the outside. 2798 */ 2799 public boolean isOutsideConnected() { 2800 for (Object relationObject : linkedRelationList()) { 2801 if (relationObject != null) { 2802 return true; 2803 } 2804 } 2805 return false; 2806 } 2807 2808 /** Override the base class to invalidate the schedule and resolved 2809 * types of the director of the container, if there is one, in addition 2810 * to what the base class does. 2811 * @param relation The relation to link to. 2812 * @exception IllegalActionException If the relation does not share 2813 * the same workspace, or the port has no container. 2814 */ 2815 @Override 2816 public void liberalLink(ComponentRelation relation) 2817 throws IllegalActionException { 2818 super.liberalLink(relation); 2819 _invalidate(); 2820 } 2821 2822 /** Override the base class to invalidate the schedule and resolved 2823 * types of the director of the container, if there is one, in addition 2824 * to what the base class does. 2825 * @param relation The relation to link to. 2826 * @exception IllegalActionException If the link crosses levels of 2827 * the hierarchy, or the port has no container, or the relation 2828 * is not an instance of IORelation. 2829 */ 2830 @Override 2831 public void link(Relation relation) throws IllegalActionException { 2832 super.link(relation); 2833 _invalidate(); 2834 } 2835 2836 /** Return the number of sink ports that may receive data from this one. 2837 * This is the number of ports returned by sinkPortList(), but 2838 * this method is more efficient to call than that one if you only 2839 * need to know how many ports there are (because the result is cached). 2840 * This method is typically used to determine whether an output port 2841 * is connected (deeply) to any input port that can consume its 2842 * data. Note that it is not sufficient to call getWidth() to determine 2843 * this; it is possible for getWidth() to return a number greater than 2844 * zero when this method returns zero. In particular, if this port 2845 * is connected to the inside of an opaque output port, but that opaque 2846 * output port is not connected on the outside, then this method will 2847 * return zero, but getWidth() will return the width of the relation 2848 * mediating the connection. 2849 * @see #sinkPortList() 2850 * @see #numberOfSources() 2851 * @see #getWidth() 2852 * @return The number of ports that can receive data from this one. 2853 */ 2854 public int numberOfSinks() { 2855 try { 2856 _workspace.getReadAccess(); 2857 if (_numberOfSinksVersion != _workspace.getVersion()) { 2858 _numberOfSinks = 0; 2859 Nameable container = getContainer(); 2860 // Do not use getExecutiveDirector() here because some 2861 // actors fool with that (like RunCompositeActor). 2862 Nameable containersContainer = container.getContainer(); 2863 if (containersContainer instanceof Actor) { 2864 Director excDirector = ((Actor) containersContainer) 2865 .getDirector(); 2866 int depthOfDirector = excDirector.depthInHierarchy(); 2867 LinkedList<IOPort> result = new LinkedList<IOPort>(); 2868 Iterator<?> ports = deepConnectedPortList().iterator(); 2869 2870 while (ports.hasNext()) { 2871 IOPort port = (IOPort) ports.next(); 2872 int depth = port.getContainer().depthInHierarchy(); 2873 2874 if (port.isInput() && depth >= depthOfDirector) { 2875 result.addLast(port); 2876 } else if (port.isOutput() && depth < depthOfDirector 2877 && port.numberOfSinks() > 0) { 2878 result.addLast(port); 2879 } 2880 } 2881 _numberOfSinks = result.size(); 2882 } 2883 _numberOfSinksVersion = _workspace.getVersion(); 2884 } 2885 return _numberOfSinks; 2886 } finally { 2887 _workspace.doneReading(); 2888 } 2889 } 2890 2891 /** Return the number of source ports that may send data to this one. 2892 * This is no greater than number of ports returned by sourcePortList(). 2893 * This method is typically used to determine whether an input port 2894 * is connected (deeply) to any output port that can supply it with 2895 * data. Note that it is not sufficient to call getWidth() to determine 2896 * this; it is possible for getWidth() to return a number greater than 2897 * zero when this method returns zero. In particular, if this port 2898 * is connected to the inside of an opaque input port, but that opaque 2899 * input port is not connected on the outside, then this method will 2900 * return zero, but getWidth() will return the width of the relation 2901 * mediating the connection. 2902 * @see #sourcePortList() 2903 * @see #numberOfSinks() 2904 * @see #getWidth() 2905 * @return The number of ports that can send data to this one. 2906 */ 2907 public int numberOfSources() { 2908 // The following code is similar to sourcePortList(), 2909 // but handles the special case of opaque ports as in 2910 // the method comment. 2911 try { 2912 _workspace.getReadAccess(); 2913 if (_numberOfSourcesVersion != _workspace.getVersion()) { 2914 Nameable container = getContainer(); 2915 int depthOfDirector = -1; 2916 2917 if (container != null) { 2918 // Do not use getExecutiveDirector() here because some 2919 // actors fool with that (like RunCompositeActor). 2920 Nameable containersContainer = container.getContainer(); 2921 if (containersContainer instanceof Actor) { 2922 Director director = ((Actor) containersContainer) 2923 .getDirector(); 2924 if (director != null) { 2925 depthOfDirector = director.depthInHierarchy(); 2926 } 2927 } 2928 } 2929 2930 LinkedList<IOPort> result = new LinkedList<IOPort>(); 2931 Iterator<?> ports = deepConnectedPortList().iterator(); 2932 2933 while (ports.hasNext()) { 2934 IOPort port = (IOPort) ports.next(); 2935 int depth = port.depthInHierarchy(); 2936 2937 if (port.isInput() && depth <= depthOfDirector 2938 && port.numberOfSources() > 0) { 2939 result.addLast(port); 2940 } else if (port.isOutput() && depth > depthOfDirector) { 2941 result.addLast(port); 2942 } 2943 } 2944 _numberOfSources = result.size(); 2945 _numberOfSourcesVersion = _workspace.getVersion(); 2946 } 2947 return _numberOfSources; 2948 } finally { 2949 _workspace.doneReading(); 2950 } 2951 } 2952 2953 /** Unregister a token sent listener. If the specified listener has not 2954 * been previously registered, then do nothing. Note that this method 2955 * is basically the same as removeDebugListener in the class NamedObj. 2956 * @param listener The listener to remove from the list of listeners 2957 * to which token sent messages are sent. 2958 * @see #addIOPortEventListener(IOPortEventListener) 2959 */ 2960 public void removeIOPortEventListener(IOPortEventListener listener) { 2961 if (_portEventListeners == null) { 2962 return; 2963 } 2964 2965 // NOTE: This has to be synchronized to prevent 2966 // concurrent modification exceptions. 2967 synchronized (_portEventListeners) { 2968 _portEventListeners.remove(listener); 2969 2970 if (_portEventListeners.size() == 0) { 2971 _hasPortEventListeners = false; 2972 } 2973 2974 return; 2975 } 2976 } 2977 2978 /** If port has default value reset the saved persistent value. 2979 * @exception IllegalActionException If defaultValue cannot be retrieved. 2980 */ 2981 public void reset() throws IllegalActionException { 2982 Token value = defaultValue.getToken(); 2983 if (value instanceof ArrayToken) { 2984 _persistentTokens = ((ArrayToken) value).arrayValue(); 2985 _persistentTokensInside = ((ArrayToken) value).arrayValue(); 2986 _persistentToken = null; 2987 } else { 2988 _persistentToken = value; 2989 _persistentTokens = null; 2990 _persistentTokensInside = null; 2991 } 2992 } 2993 2994 /** Send the specified token to all receivers connected to the 2995 * specified channel. Tokens are in general immutable, so each receiver 2996 * is given a reference to the same token and no clones are made. 2997 * If the port is not connected to anything, or receivers have not been 2998 * created in the remote port, or the channel index is out of 2999 * range, or the port is not an output port, 3000 * then just silently return. This behavior makes it 3001 * easy to leave output ports unconnected when you are not interested 3002 * in the output. The transfer is 3003 * accomplished by calling the put() method of the remote receivers. 3004 * If the port is not connected to anything, or receivers have not been 3005 * created in the remote port, then just return. 3006 * <p> 3007 * If a null token is specified, this is interpreted as an assertion 3008 * that no token is being sent. For some domains, specifically those 3009 * that queue tokens such as PN and SDF, this has no effect. For 3010 * others, specifically those that have a well-defined notion of 3011 * "absent" inputs such as SR, modal, and Continuous, sending a null 3012 * token corresponds to asserting that the inputs of destination 3013 * actors will be absent in this round. 3014 * <p> 3015 * Some of this method is read-synchronized on the workspace. 3016 * Since it is possible for a thread to block while executing a put, 3017 * it is important that the thread does not hold read access on 3018 * the workspace when it is blocked. Thus this method releases 3019 * read access on the workspace before calling put. 3020 * 3021 * @param channelIndex The index of the channel, from 0 to width-1 3022 * @param token The token to send, or null to send no token. 3023 * @exception NoRoomException If there is no room in the receiver. 3024 * @exception IllegalActionException Not thrown in this base class. 3025 */ 3026 public void send(int channelIndex, Token token) 3027 throws IllegalActionException, NoRoomException { 3028 /* Effective 11/27/09, null tokens are accepted. 3029 if (token == null) { 3030 throw new IllegalActionException(this, "Cannot send a null token."); 3031 } 3032 */ 3033 3034 Receiver[][] farReceivers; 3035 3036 if (_debugging) { 3037 _debug("send to channel " + channelIndex + ": " + token); 3038 } 3039 3040 if (_hasPortEventListeners) { 3041 _notifyPortEventListeners(new IOPortEvent(this, 3042 IOPortEvent.SEND_BEGIN, channelIndex, true, token)); 3043 } 3044 3045 try { 3046 try { 3047 _workspace.getReadAccess(); 3048 3049 // Note that the getRemoteReceivers() method doesn't throw 3050 // any non-runtime exception. 3051 farReceivers = getRemoteReceivers(); 3052 3053 if (farReceivers == null || farReceivers.length <= channelIndex 3054 || farReceivers[channelIndex] == null) { 3055 return; 3056 } 3057 } finally { 3058 _workspace.doneReading(); 3059 } 3060 3061 if (farReceivers[channelIndex].length > 0) { 3062 // Delegate to the receiver to handle putting to all 3063 // receivers, since domain-specific techniques might be relevant. 3064 farReceivers[channelIndex][0].putToAll(token, 3065 farReceivers[channelIndex]); 3066 } 3067 } finally { 3068 if (_hasPortEventListeners) { 3069 _notifyPortEventListeners(new IOPortEvent(this, 3070 IOPortEvent.SEND_END, channelIndex, true, token)); 3071 } 3072 } 3073 } 3074 3075 /** Send the specified portion of a token array to all receivers connected 3076 * to the specified channel. The first <i>vectorLength</i> tokens 3077 * of the token array are sent. 3078 * <p> 3079 * Tokens are in general immutable, so each receiver 3080 * is given a reference to the same token and no clones are made. 3081 * If the port is not connected to anything, or receivers have not been 3082 * created in the remote port, or the channel index is out of 3083 * range, or the port is not an output port, 3084 * then just silently return. This behavior makes it 3085 * easy to leave output ports unconnected when you are not interested 3086 * in the output. The transfer is accomplished 3087 * by calling the vectorized put() method of the remote receivers. 3088 * If the port is not connected to anything, or receivers have not been 3089 * created in the remote port, then just return. 3090 * <p> 3091 * Some of this method is read-synchronized on the workspace. 3092 * Since it is possible for a thread to block while executing a put, 3093 * it is important that the thread does not hold read access on 3094 * the workspace when it is blocked. Thus this method releases 3095 * read access on the workspace before calling put. 3096 * 3097 * @param channelIndex The index of the channel, from 0 to width-1 3098 * @param tokenArray The token array to send 3099 * @param vectorLength The number of elements of of the token 3100 * array to send. 3101 * @exception NoRoomException If there is no room in the receiver. 3102 * @exception IllegalActionException Not thrown in this base class. 3103 */ 3104 public void send(int channelIndex, Token[] tokenArray, int vectorLength) 3105 throws IllegalActionException, NoRoomException { 3106 Receiver[][] farReceivers; 3107 3108 if (_debugging) { 3109 _debug("send to channel " + channelIndex + " token array of length " 3110 + vectorLength); 3111 } 3112 3113 if (_hasPortEventListeners) { 3114 _notifyPortEventListeners( 3115 new IOPortEvent(this, IOPortEvent.SEND_BEGIN, channelIndex, 3116 true, tokenArray, vectorLength)); 3117 } 3118 3119 try { 3120 try { 3121 _workspace.getReadAccess(); 3122 3123 // Note that the getRemoteReceivers() method doesn't throw 3124 // any non-runtime exception. 3125 farReceivers = getRemoteReceivers(); 3126 3127 if (farReceivers == null || farReceivers.length <= channelIndex 3128 || farReceivers[channelIndex] == null) { 3129 return; 3130 } 3131 } finally { 3132 _workspace.doneReading(); 3133 } 3134 3135 if (farReceivers[channelIndex].length > 0) { 3136 // Delegate to the receiver to handle putting to all 3137 // receivers, since domain-specific techniques might be relevant. 3138 farReceivers[channelIndex][0].putArrayToAll(tokenArray, 3139 vectorLength, farReceivers[channelIndex]); 3140 } 3141 } finally { 3142 if (_hasPortEventListeners) { 3143 _notifyPortEventListeners( 3144 new IOPortEvent(this, IOPortEvent.SEND_END, 3145 channelIndex, true, tokenArray, vectorLength)); 3146 } 3147 } 3148 } 3149 3150 /** Set all destination receivers connected via the specified to channel 3151 * to have no token. The transfer is accomplished by calling 3152 * clear() on the appropriate receivers. If there are no 3153 * destination receivers on the specified channel, or if this is not 3154 * an output port, or if the array index is out of bounds, 3155 * then do nothing. 3156 * Some of this method is read-synchronized on the workspace. 3157 * @see #broadcastClear() 3158 * @see #sendClearInside(int) 3159 * @param channelIndex The index of the channel, from 0 to width-1 3160 * @exception IllegalActionException If a receiver does not support 3161 * clear(). 3162 */ 3163 public void sendClear(int channelIndex) throws IllegalActionException { 3164 Receiver[][] farReceivers; 3165 3166 if (_debugging) { 3167 _debug("sendClear to channel " + channelIndex); 3168 } 3169 3170 try { 3171 _workspace.getReadAccess(); 3172 3173 // Note that the getRemoteReceivers() method doesn't throw 3174 // any non-runtime exception. 3175 farReceivers = getRemoteReceivers(); 3176 3177 if (farReceivers == null || farReceivers.length <= channelIndex 3178 || farReceivers[channelIndex] == null) { 3179 return; 3180 } 3181 } finally { 3182 _workspace.doneReading(); 3183 } 3184 3185 // NOTE: Conceivably, clear() in some domains may block, 3186 // so we make sure to release read access above before calling 3187 // clear(). 3188 for (int j = 0; j < farReceivers[channelIndex].length; j++) { 3189 farReceivers[channelIndex][j].clear(); 3190 } 3191 } 3192 3193 /** Set all destination receivers connected on the inside via the specified 3194 * to channel to have no token. This is accomplished by calling 3195 * clear() on the appropriate receivers. If there are no 3196 * destination inside receivers on the specified channel, 3197 * or if the channel index is out of bounds, then do nothing. 3198 * Some of this method is read-synchronized on the workspace. 3199 * @see #sendClear(int) 3200 * @param channelIndex The index of the channel, from 0 to insideWidth-1. 3201 * @exception IllegalActionException If a receiver does not support 3202 * clear(). 3203 */ 3204 public void sendClearInside(int channelIndex) 3205 throws IllegalActionException { 3206 Receiver[][] farReceivers; 3207 3208 if (_debugging) { 3209 _debug("sendClearInside to channel " + channelIndex); 3210 } 3211 3212 try { 3213 _workspace.getReadAccess(); 3214 farReceivers = deepGetReceivers(); 3215 3216 if (farReceivers == null || farReceivers.length <= channelIndex 3217 || farReceivers[channelIndex] == null) { 3218 return; 3219 } 3220 } finally { 3221 _workspace.doneReading(); 3222 } 3223 3224 for (int j = 0; j < farReceivers[channelIndex].length; j++) { 3225 farReceivers[channelIndex][j].clear(); 3226 } 3227 } 3228 3229 /** Send the specified token to all receivers connected to the 3230 * specified inside channel of this port. Tokens are in general 3231 * immutable, so each receiver is given a reference to the same 3232 * token and no clones are made. If the port is not connected to 3233 * anything on the inside, or receivers have not been created in 3234 * the remote port, or the channel index is out of range, or the 3235 * port is not an input port, then just silently return. This 3236 * behavior makes it easy to leave external input ports of a 3237 * composite unconnected when you are not interested in the 3238 * received values. The transfer is accomplished by calling the 3239 * put() method of the inside remote receivers. If the port is 3240 * not connected to anything, or receivers have not been created 3241 * in the remote port, then just return. This method is normally 3242 * called only by the transferInputs method of directors of 3243 * composite actors, as AtomicActors do not usually have any 3244 * relations on the inside of their ports. 3245 * <p> 3246 * If a null token is specified, this is interpreted as an assertion 3247 * that no token is being sent. For some domains, specifically those 3248 * that queue tokens such as PN and SDF, this has no effect. For 3249 * others, specifically those that have a well-defined notion of 3250 * "absent" inputs such as SR, modal, and Continuous, sending a null 3251 * token corresponds to asserting that the inputs of destination 3252 * actors will be absent in this round. 3253 * 3254 * <p> Some of this method is read-synchronized on the workspace. 3255 * Since it is possible for a thread to block while executing a 3256 * put, it is important that the thread does not hold read access 3257 * on the workspace when it is blocked. Thus this method releases 3258 * read access on the workspace before calling put. 3259 * 3260 * @param channelIndex The index of the channel, from 0 to width-1 3261 * @param token The token to send, or null to send no token. 3262 * @exception NoRoomException If there is no room in the receiver. 3263 * @exception IllegalActionException Not thrown in this base class. 3264 */ 3265 public void sendInside(int channelIndex, Token token) 3266 throws IllegalActionException, NoRoomException { 3267 Receiver[][] farReceivers; 3268 3269 if (_debugging) { 3270 _debug("send inside to channel " + channelIndex + ": " + token); 3271 } 3272 3273 if (_hasPortEventListeners) { 3274 _notifyPortEventListeners(new IOPortEvent(this, 3275 IOPortEvent.SEND_BEGIN, channelIndex, false, token)); 3276 } 3277 3278 try { 3279 try { 3280 _workspace.getReadAccess(); 3281 3282 // Note that the getRemoteReceivers() method doesn't throw 3283 // any non-runtime exception. 3284 farReceivers = deepGetReceivers(); 3285 3286 if (farReceivers == null || farReceivers.length <= channelIndex 3287 || farReceivers[channelIndex] == null) { 3288 return; 3289 } 3290 } finally { 3291 _workspace.doneReading(); 3292 } 3293 3294 if (farReceivers[channelIndex].length > 0) { 3295 // Delegate to the receiver to handle putting to all 3296 // receivers, since domain-specific techniques might be relevant. 3297 farReceivers[channelIndex][0].putToAll(token, 3298 farReceivers[channelIndex]); 3299 } 3300 } finally { 3301 if (_hasPortEventListeners) { 3302 _notifyPortEventListeners(new IOPortEvent(this, 3303 IOPortEvent.SEND_END, channelIndex, false, token)); 3304 } 3305 } 3306 } 3307 3308 /** Override the base class to ensure that the proposed container 3309 * implements the Actor interface (the base class ensures that the 3310 * container is an instance of ComponentEntity) or null. A null 3311 * argument will remove the port from the container. This method 3312 * invalidates the schedule and type resolution of the director 3313 * of the container, if there is one. 3314 * 3315 * @param container The proposed container. 3316 * @exception IllegalActionException If the proposed container is not a 3317 * ComponentEntity, doesn't implement Actor, or has no name, 3318 * or the port and container are not in the same workspace. Or 3319 * it's not null 3320 * @exception NameDuplicationException If the container already has 3321 * a port with the name of this port. 3322 */ 3323 @Override 3324 public void setContainer(Entity container) 3325 throws IllegalActionException, NameDuplicationException { 3326 // Invalidate schedule and type resolution of the old container. 3327 Actor oldContainer = (Actor) getContainer(); 3328 3329 if (oldContainer != null) { 3330 Director director = oldContainer.getDirector(); 3331 3332 if (director != null) { 3333 director.invalidateSchedule(); 3334 director.invalidateResolvedTypes(); 3335 } 3336 } 3337 3338 // Invalidate schedule and type resolution of the new container. 3339 if (container instanceof Actor) { 3340 Director director = ((Actor) container).getDirector(); 3341 3342 if (director != null) { 3343 director.invalidateSchedule(); 3344 director.invalidateResolvedTypes(); 3345 } 3346 } 3347 3348 super.setContainer(container); 3349 } 3350 3351 /** Set the default width. In case there is no unique solution for a relation 3352 * connected to this port the default width will be used. 3353 * If the default width is not set, the value will be -1 3354 * which corresponds to no default width. 3355 * @param defaultWidth The default width. 3356 * @see #getDefaultWidth() 3357 */ 3358 public void setDefaultWidth(int defaultWidth) { 3359 _defaultWidth = defaultWidth; 3360 } 3361 3362 /** If the argument is true, make the port an input port. 3363 * If the argument is false, make the port not an input port. 3364 * If this is never called, and setOutput() is never called, 3365 * and the port is a transparent port of a composite actor, 3366 * then the input/output status will be inferred from the connection. 3367 * This method invalidates the schedule and resolved types of the 3368 * director of the container, if there is one. 3369 * It is write-synchronized on the workspace, and increments 3370 * the version of the workspace. 3371 * @param isInput True to make the port an input. 3372 * @exception IllegalActionException If changing the port status is 3373 * not permitted (not thrown in this base class). 3374 */ 3375 public void setInput(boolean isInput) throws IllegalActionException { 3376 // No need for the try ... finally construct here because no 3377 // exception can occur. Note that although the action here is 3378 // atomic, we still need to obtain write access to be sure that 3379 // the change is not made in the middle of another read in another 3380 // thread. 3381 _workspace.getWriteAccess(); 3382 _isInput = isInput; 3383 3384 // Flag that the input status has been set, 3385 // and therefore should not be inferred. 3386 _isInputOutputStatusSet = true; 3387 _invalidate(); 3388 _workspace.doneWriting(); 3389 } 3390 3391 /** If the argument is true, make the port a multiport. 3392 * That is, make it capable of linking with multiple IORelations, 3393 * or with IORelations that have width greater than one. 3394 * If the argument is false, allow only links with a single 3395 * IORelation of width one. 3396 * This method invalidates the schedule and resolved types of the 3397 * director of the container, if there is one. 3398 * It is write-synchronized on the workspace. 3399 * @param isMultiport True to make the port a multiport. 3400 * @exception IllegalActionException If changing the port status is 3401 * not permitted (not thrown in this base class). 3402 */ 3403 public void setMultiport(boolean isMultiport) 3404 throws IllegalActionException { 3405 // No need for the try ... finally construct here because no 3406 // exception can occur. Note that although the action here is 3407 // atomic, we still need to obtain write access to be sure that 3408 // the change is not made in the middle of another read in another 3409 // thread. 3410 _workspace.getWriteAccess(); 3411 _isMultiport = isMultiport; 3412 _invalidate(); 3413 _workspace.doneWriting(); 3414 } 3415 3416 /** If the argument is true, make the port an output port. 3417 * If the argument is false, make the port not an output port. 3418 * If this is never called, and setInput() is never called, 3419 * and the port is a transparent port of a composite actor, 3420 * then the input/output status will be inferred from the connection. 3421 * This method invalidates the schedule and resolved types of the 3422 * director of the container, if there is one. 3423 * It is write-synchronized on the workspace, and increments 3424 * the version of the workspace. 3425 * @param isOutput True to make the port an output. 3426 * @exception IllegalActionException If changing the port status is 3427 * not permitted (not thrown in this base class). 3428 */ 3429 public void setOutput(boolean isOutput) throws IllegalActionException { 3430 // No need for the try ... finally construct here because no 3431 // exception can occur. Note that although the action here is 3432 // atomic, we still need to obtain write access to be sure that 3433 // the change is not made in the middle of another read in another 3434 // thread. 3435 _workspace.getWriteAccess(); 3436 _isOutput = isOutput; 3437 3438 // Flag that the output status has been set, 3439 // and therefore should not be inferred. 3440 _isInputOutputStatusSet = true; 3441 _invalidate(); 3442 _workspace.doneWriting(); 3443 } 3444 3445 /** Constrain the width of this port to be equal to the parameter. 3446 * <p>Actors that call this method should have a clone() method that 3447 * repeats the width constraints that were specified in 3448 * the constructor. 3449 * @param parameter A parameter. 3450 */ 3451 public void setWidthEquals(Parameter parameter) { 3452 _widthEqualToParameter.add(parameter); 3453 } 3454 3455 /** Constrain the width of this port to be equal to the width of 3456 * the IOPort port. If bidirectional equals true, the width of port 3457 * can also be inferred from the width of this. 3458 * <p>Actors that call this method should have a clone() method that 3459 * repeats the width constraints that were specified in 3460 * the constructor. 3461 * @param port A port. 3462 * @param bidirectional A flag that specifies whether the constraint 3463 * work in two directions. 3464 */ 3465 public void setWidthEquals(IOPort port, boolean bidirectional) { 3466 if (!_widthEqualToPort.contains(port)) { 3467 _widthEqualToPort.add(port); 3468 if (bidirectional) { 3469 port.setWidthEquals(this, false); 3470 } 3471 } 3472 } 3473 3474 /** Return a list of the ports that may accept data from this port when 3475 * it sends on the outside. This includes 3476 * opaque input ports that are connected on the outside to this port 3477 * and opaque output ports that are connected on the inside to this one. 3478 * These are the ports that 3479 * will receive data from this one if data is sent on the outside. 3480 * However, derived classes are free to return ports on this list 3481 * that <i>may</i> receive data from this port, even if they are 3482 * not actually currently connected. The wireless domain, for 3483 * example, takes advantage of this and includes ports on the 3484 * outside that share the same channel. 3485 * @see #getRemoteReceivers() 3486 * @return A list of IOPort objects. 3487 */ 3488 public List<IOPort> sinkPortList() { 3489 // NOTE: This doesn't just get the containers of the 3490 // receivers returned by deepGetReceivers() 3491 // because the receivers may not have yet been created. 3492 try { 3493 _workspace.getReadAccess(); 3494 if (_sinkPortListVersion != _workspace.getVersion()) { 3495 Nameable container = getContainer(); 3496 // Do not use getExecutiveDirector() here because some 3497 // actors fool with that (like RunCompositeActor). 3498 Nameable containersContainer = container.getContainer(); 3499 _sinkPortList = new LinkedList<IOPort>(); 3500 if (containersContainer instanceof Actor) { 3501 Director excDirector = ((Actor) containersContainer) 3502 .getDirector(); 3503 int depthOfDirector = excDirector.depthInHierarchy(); 3504 Iterator<?> ports = deepConnectedPortList().iterator(); 3505 3506 while (ports.hasNext()) { 3507 IOPort port = (IOPort) ports.next(); 3508 int depth = port.getContainer().depthInHierarchy(); 3509 3510 if (port.isInput() && depth >= depthOfDirector) { 3511 _sinkPortList.addLast(port); 3512 } else if (port.isOutput() && depth < depthOfDirector) { 3513 _sinkPortList.addLast(port); 3514 } 3515 } 3516 } 3517 _sinkPortListVersion = _workspace.getVersion(); 3518 } 3519 return _sinkPortList; 3520 } finally { 3521 _workspace.doneReading(); 3522 } 3523 } 3524 3525 /** Return a list of ports that may send data to this port from the 3526 * outside. This includes all opaque output ports that are 3527 * connected on the outside to this port, and opaque input ports 3528 * that are connected on the inside to this port. These are the ports that 3529 * will send data to this one. 3530 * However, derived classes are free to return ports on this list 3531 * that <i>may</i> send data to this port, even if they are 3532 * not actually currently connected. The wireless domain, for 3533 * example, takes advantage of this and includes ports on the 3534 * outside that share the same channel. 3535 * @return A list of IOPort objects, or an empty list if there are none. 3536 */ 3537 public List<IOPort> sourcePortList() { 3538 try { 3539 _workspace.getReadAccess(); 3540 if (_sourcePortListVersion != _workspace.getVersion()) { 3541 Nameable container = getContainer(); 3542 int depthOfDirector = -1; 3543 3544 if (container != null) { 3545 // Do not use getExecutiveDirector() here because some 3546 // actors fool with that (like RunCompositeActor). 3547 Nameable containersContainer = container.getContainer(); 3548 if (containersContainer instanceof Actor) { 3549 Director director = ((Actor) containersContainer) 3550 .getDirector(); 3551 if (director != null) { 3552 depthOfDirector = director.depthInHierarchy(); 3553 } 3554 } 3555 } 3556 3557 _sourcePortList = new LinkedList<IOPort>(); 3558 Iterator<?> ports = deepConnectedPortList().iterator(); 3559 3560 while (ports.hasNext()) { 3561 IOPort port = (IOPort) ports.next(); 3562 int depth = port.depthInHierarchy(); 3563 3564 if (port.isInput() && depth <= depthOfDirector) { 3565 _sourcePortList.addLast(port); 3566 } else if (port.isOutput() && depth > depthOfDirector) { 3567 _sourcePortList.addLast(port); 3568 } 3569 } 3570 _sourcePortListVersion = _workspace.getVersion(); 3571 } 3572 return _sourcePortList; 3573 } finally { 3574 _workspace.doneReading(); 3575 } 3576 } 3577 3578 /** Return a list of ports connected to this port on the 3579 * outside that can send data to this port such that the data 3580 * is received by the specified receiver. This includes all 3581 * opaque output ports that are connected on the outside to this port, 3582 * and opaque input ports that are connected on the inside to this port. 3583 * If there are multiple paths from a source port to the specified 3584 * channel, then the source port will appear more than once in the 3585 * resulting list. 3586 * If the channel is out of bounds, then return an empty list. 3587 * @see #sourcePortList() 3588 * @param receiver The receiver. 3589 * @return A list of IOPort objects, or an empty list if there are none. 3590 */ 3591 3592 /* NOTE: This method is commented out because it is untested, 3593 * extremely complicated, and appears to be not needed (yet). 3594 public List sourcePortList(Receiver receiver) { 3595 // This is a surprisingly difficult thing to do... 3596 List result = new LinkedList(); 3597 3598 // Next, we iterate over ports in the sourcePortList() to find 3599 // those that have one of these receivers in their remote 3600 // receiver list. 3601 Iterator sourcePorts = sourcePortList().iterator(); 3602 while (sourcePorts.hasNext()) { 3603 IOPort sourcePort = (IOPort)sourcePorts.next(); 3604 Receiver[][] sourcesRemoteReceivers 3605 = sourcePort.getRemoteReceivers(); 3606 if (sourcesRemoteReceivers == null) continue; 3607 for (int i = 0; i < sourcesRemoteReceivers.length; i++) { 3608 if (sourcesRemoteReceivers[i] == null) continue; 3609 for (int j = 0; j < sourcesRemoteReceivers[i].length; j++) { 3610 if (sourcesRemoteReceivers[i][j] == null) continue; 3611 if (sourcesRemoteReceivers[i][j] == receiver) { 3612 result.add(sourcesRemoteReceivers[i][j].getContainer()); 3613 } 3614 } 3615 } 3616 } 3617 return result; 3618 } 3619 */ 3620 3621 /** Transfer data from this port to the ports it is connected to 3622 * on the inside. 3623 * This port must be an opaque input port. If any 3624 * channel of the this port has no data, then that channel is 3625 * ignored. This method will transfer exactly one token on 3626 * each input channel that has at least one token available. 3627 * 3628 * @exception IllegalActionException If this port is not an 3629 * opaque input port. 3630 * @return True if at least one data token is transferred. 3631 * @deprecated Domains should use sendInside directly to 3632 * implement their transferInputs method. 3633 */ 3634 @Deprecated 3635 public boolean transferInputs() throws IllegalActionException { 3636 if (!isInput() || !isOpaque()) { 3637 throw new IllegalActionException(this, 3638 "transferInputs: this port is not an opaque" 3639 + "input port."); 3640 } 3641 3642 boolean wasTransferred = false; 3643 3644 for (int i = 0; i < getWidth(); i++) { 3645 // NOTE: This is not compatible with certain cases in PN, 3646 // where we don't want to block on a port if nothing is connected 3647 // to the port on the inside. 3648 try { 3649 if (isKnown(i)) { 3650 if (hasToken(i)) { 3651 Token t = get(i); 3652 3653 if (_debugging) { 3654 _debug(getName(), 3655 "transferring input from " + getName()); 3656 } 3657 3658 sendInside(i, t); 3659 wasTransferred = true; 3660 } 3661 3662 // NOTE: Used to call sendClear() here, but most 3663 // domains now throw an exception if this is called. 3664 // Presumably this was called to support SR, but 3665 // SR needs to handle this itself. EAL 10/31/02. 3666 // else: sendClearInside(i); 3667 } 3668 } catch (NoTokenException ex) { 3669 // this shouldn't happen. 3670 throw new InternalErrorException(this, ex, null); 3671 } 3672 } 3673 3674 return wasTransferred; 3675 } 3676 3677 /** Transfer data from this port to the ports it is connected to on 3678 * the outside. 3679 * This port must be an opaque output port. If any 3680 * channel of this port has no data, then that channel is 3681 * ignored. This method will transfer exactly one token on 3682 * each output channel that has at least one token available. 3683 * 3684 * @exception IllegalActionException If the port is not an opaque 3685 * output port. 3686 * @return True if at least one data token is transferred. 3687 * @deprecated domains should use getInside directly to implement their 3688 * transferOutputs method. 3689 */ 3690 @Deprecated 3691 public boolean transferOutputs() throws IllegalActionException { 3692 if (_debugging) { 3693 _debug("calling transferOutputs."); 3694 } 3695 3696 if (!this.isOutput() || !this.isOpaque()) { 3697 throw new IllegalActionException(this, 3698 "transferOutputs: this port is not " 3699 + "an opaque output port."); 3700 } 3701 3702 boolean wasTransferred = false; 3703 3704 for (int i = 0; i < getWidthInside(); i++) { 3705 try { 3706 if (isKnownInside(i)) { 3707 if (hasTokenInside(i)) { 3708 Token t = getInside(i); 3709 send(i, t); 3710 wasTransferred = true; 3711 } 3712 3713 // NOTE: Used to call sendClear() here, but most 3714 // domains now throw an exception if this is called. 3715 // Why was it called? EAL 10/31/02. 3716 // else: sendClear(i); 3717 } 3718 } catch (NoTokenException ex) { 3719 throw new InternalErrorException(this, ex, null); 3720 } 3721 } 3722 3723 return wasTransferred; 3724 } 3725 3726 /** Unlink whatever relation is currently linked at the specified index 3727 * number. If there is no such relation, do nothing. 3728 * If a link is removed, then any links at higher index numbers 3729 * will have their index numbers decremented by one. 3730 * If there is a container, notify it by calling connectionsChanged(). 3731 * Invalidate the schedule and resolved types of the director of the 3732 * container, if there is one. 3733 * This method is write-synchronized on the 3734 * workspace and increments its version number. 3735 * @param index The index number of the link to remove. 3736 */ 3737 @Override 3738 public void unlink(int index) { 3739 // Override the base class to update localReceiversTable. 3740 try { 3741 _workspace.getWriteAccess(); 3742 3743 Relation toDelete = (Relation) _relationsList.get(index); 3744 3745 if (toDelete != null && _localReceiversTable != null) { 3746 _removeReceivers(toDelete); 3747 _localReceiversTable.remove(toDelete); 3748 _localReceiversVersion = -1; 3749 } 3750 3751 super.unlink(index); 3752 _invalidate(); 3753 } finally { 3754 _workspace.doneWriting(); 3755 } 3756 } 3757 3758 /** Unlink the specified Relation. The receivers associated with 3759 * this relation, and any data they contain, are lost. If the Relation 3760 * is not linked to this port, do nothing. If the relation is linked 3761 * more than once, then unlink all occurrences. 3762 * Invalidate the schedule and resolved types of the director of the 3763 * container, if there is one. 3764 * Invalidate the schedule and resolved types of the director of the 3765 * container, if there is one. 3766 * This method is write-synchronized on the workspace. 3767 * 3768 * @param relation The relation to unlink. 3769 */ 3770 @Override 3771 public void unlink(Relation relation) { 3772 try { 3773 _workspace.getWriteAccess(); 3774 super.unlink(relation); 3775 3776 if (_localReceiversTable != null) { 3777 _removeReceivers(relation); 3778 _localReceiversTable.remove(relation); 3779 _localReceiversVersion = -1; 3780 _insideReceiversVersion = -1; 3781 } 3782 3783 _invalidate(); 3784 } finally { 3785 _workspace.doneWriting(); 3786 } 3787 } 3788 3789 /** Unlink all relations that are linked on the outside. 3790 * This method is write-synchronized on the 3791 * workspace. 3792 */ 3793 @Override 3794 @SuppressWarnings("unchecked") 3795 public void unlinkAll() { 3796 try { 3797 _workspace.getWriteAccess(); 3798 3799 // NOTE: Can't just clear the localReceiversTable because 3800 // that would unlink inside relations as well. 3801 if (_localReceiversTable != null) { 3802 // Have to clone the local receivers table to avoid 3803 // a ConcurrentModificationException. 3804 HashMap<IORelation, List<Receiver[][]>> clonedMap = (HashMap<IORelation, List<Receiver[][]>>) _localReceiversTable 3805 .clone(); 3806 3807 for (IORelation relation : clonedMap.keySet()) { 3808 if (!isInsideLinked(relation)) { 3809 _removeReceivers(relation); 3810 _localReceiversTable.remove(relation); 3811 _localReceiversVersion = -1; 3812 _insideReceiversVersion = -1; 3813 } 3814 } 3815 } 3816 3817 super.unlinkAll(); 3818 _invalidate(); 3819 } finally { 3820 _workspace.doneWriting(); 3821 } 3822 } 3823 3824 /** Unlink all relations that are linked on the inside. 3825 * This method is write-synchronized on the 3826 * workspace. 3827 */ 3828 @Override 3829 @SuppressWarnings("unchecked") 3830 public void unlinkAllInside() { 3831 try { 3832 _workspace.getWriteAccess(); 3833 3834 // NOTE: Can't just clear the localReceiversTable because 3835 // that would unlink inside relations as well. 3836 if (_localReceiversTable != null) { 3837 // Have to clone the local receivers table to avoid 3838 // a ConcurrentModificationException. 3839 HashMap<IORelation, List<Receiver[][]>> clonedMap = (HashMap<IORelation, List<Receiver[][]>>) _localReceiversTable 3840 .clone(); 3841 3842 for (IORelation relation : clonedMap.keySet()) { 3843 3844 if (isInsideLinked(relation)) { 3845 _removeReceivers(relation); 3846 _localReceiversTable.remove(relation); 3847 _localReceiversVersion = -1; 3848 _insideReceiversVersion = -1; 3849 } 3850 } 3851 } 3852 3853 super.unlinkAllInside(); 3854 _invalidate(); 3855 } finally { 3856 _workspace.doneWriting(); 3857 } 3858 } 3859 3860 /** Unlink whatever relation is currently linked on the inside 3861 * with the specified index number. If the relation 3862 * is not linked to this port on the inside, do nothing. 3863 * If a link is removed, then any links at higher index numbers 3864 * will have their index numbers decremented by one. 3865 * If there is a container, notify it by calling connectionsChanged(). 3866 * This method is write-synchronized on the workspace 3867 * and increments its version number. 3868 * @param index The index number of the link to remove. 3869 */ 3870 @Override 3871 public void unlinkInside(int index) { 3872 // Override the base class to update localReceiversTable. 3873 try { 3874 _workspace.getWriteAccess(); 3875 3876 Relation toDelete = (Relation) _insideLinks.get(index); 3877 3878 if (toDelete != null) { 3879 if (_localReceiversTable != null) { 3880 _removeReceivers(toDelete); 3881 _localReceiversTable.remove(toDelete); 3882 _insideReceiversVersion = -1; 3883 } 3884 } 3885 3886 super.unlinkInside(index); 3887 _invalidate(); 3888 } finally { 3889 _workspace.doneWriting(); 3890 } 3891 } 3892 3893 /** Unlink the specified Relation on the inside. The receivers associated 3894 * with this relation, and any data they contain, are lost. If the Relation 3895 * is not linked to this port, do nothing. If the relation is linked 3896 * more than once, then unlink all occurrences. 3897 * This method is write-synchronized on the workspace. 3898 * 3899 * @param relation The relation to unlink. 3900 */ 3901 @Override 3902 public void unlinkInside(Relation relation) { 3903 try { 3904 _workspace.getWriteAccess(); 3905 super.unlinkInside(relation); 3906 3907 if (_localReceiversTable != null) { 3908 _removeReceivers(relation); 3909 _localReceiversTable.remove(relation); 3910 _insideReceiversVersion = -1; 3911 } 3912 3913 _invalidate(); 3914 } finally { 3915 _workspace.doneWriting(); 3916 } 3917 } 3918 3919 /////////////////////////////////////////////////////////////////// 3920 //// public variables //// 3921 3922 /** Indicate that the description(int) method should include information 3923 * about whether the port is an input, output, or multiport, whether it 3924 * is opaque, and what is its width. 3925 */ 3926 public static final int CONFIGURATION = 512; 3927 3928 /** Indicate that the description(int) method should include receivers 3929 * contained by this port (if any). 3930 */ 3931 public static final int RECEIVERS = 1024; 3932 3933 /** Indicate that the description(int) method should include receivers 3934 * remotely connected to this port (if any). 3935 */ 3936 public static final int REMOTERECEIVERS = 2048; 3937 3938 /////////////////////////////////////////////////////////////////// 3939 //// protected methods //// 3940 3941 /** Check that the specified container implements the Actor interface 3942 * (or is null). 3943 * @param container The proposed container. 3944 * @exception IllegalActionException If the container is not of 3945 * an acceptable class. 3946 */ 3947 @Override 3948 protected void _checkContainer(Entity container) 3949 throws IllegalActionException { 3950 if (!(container instanceof Actor) && container != null) { 3951 throw new IllegalActionException(container, this, 3952 "IOPort can only be contained by objects implementing " 3953 + "the Actor interface."); 3954 } 3955 } 3956 3957 /** Override parent method to ensure compatibility of the relation 3958 * and validity of the width of the port. 3959 * If this port is not a multiport, then the width of the 3960 * relation is required to be specified to be one. This method 3961 * allows level-crossing links. 3962 * This method is <i>not</i> synchronized on the 3963 * workspace, so the caller should be. 3964 * 3965 * @param relation The relation to link to on the inside. 3966 * @exception IllegalActionException If this port has no container or 3967 * the relation is not an IORelation, or the port already linked to a 3968 * relation and is not a multiport, or the relation has width 3969 * not exactly one and the port is not a multiport, or the 3970 * relation is incompatible with this port, or the port is not 3971 * in the same workspace as the relation. 3972 */ 3973 @Override 3974 protected void _checkLiberalLink(Relation relation) 3975 throws IllegalActionException { 3976 if (!(relation instanceof IORelation)) { 3977 throw new IllegalActionException(this, relation, 3978 "Attempt to link to an incompatible relation." 3979 + " IOPort requires IORelation."); 3980 } 3981 3982 _checkMultiportLink((IORelation) relation); 3983 super._checkLiberalLink(relation); 3984 } 3985 3986 /** Override parent method to ensure compatibility of the relation 3987 * and validity of the width of the port. 3988 * If this port is not a multiport, then the width of the 3989 * relation is required to be specified to be one. 3990 * This method is <i>not</i> synchronized on the 3991 * workspace, so the caller should be. 3992 * 3993 * @param relation The relation to link to. 3994 * @exception IllegalActionException If this port has no container or 3995 * the relation is not an IORelation, or the port already linked to a 3996 * relation and is not a multiport, or if the relation has width 3997 * not exactly one and the port is not a multiport, or the port is 3998 * not in the same workspace as the relation. 3999 */ 4000 @Override 4001 protected void _checkLink(Relation relation) throws IllegalActionException { 4002 if (!(relation instanceof IORelation)) { 4003 throw new IllegalActionException(this, relation, 4004 "Attempt to link to an incompatible relation." 4005 + " IOPort requires IORelation."); 4006 } 4007 4008 _checkMultiportLink((IORelation) relation); 4009 super._checkLink(relation); 4010 } 4011 4012 /** Return a description of the object. The level of detail depends 4013 * on the argument, which is an or-ing of the static final constants 4014 * defined in the NamedObj class and in this class. 4015 * Lines are indented according to 4016 * to the level argument using the protected method _getIndentPrefix(). 4017 * Zero, one or two brackets can be specified to surround the returned 4018 * description. If one is specified it is the leading bracket. 4019 * This is used by derived classes that will append to the description. 4020 * Those derived classes are responsible for the closing bracket. 4021 * An argument other than 0, 1, or 2 is taken to be equivalent to 0. 4022 * <p> 4023 * If the detail argument sets the bit defined by the constant 4024 * CONFIGURATION, then append to the description a field containing 4025 * any subset of the words "input", "output", "multiport", and "opaque", 4026 * separated by spaces, plus a subfield of the form "{width 4027 * <i>integer</i>}", where the integer is the width of the port. 4028 * The field keyword is "configuration". 4029 * <p> 4030 * If the detail argument sets the bit defined by the constant 4031 * RECEIVERS, then append to the description a field containing 4032 * the receivers contained by this port. The keyword is "receivers" 4033 * and the format is like the Receivers array, an array of groups, with 4034 * each group receiving from a channel. 4035 * Each group is a list of receiver descriptions (it may also be empty). 4036 * If the detail argument sets the bit defined by the constant 4037 * REMOTERECEIVERS, then also append to the description a field containing 4038 * the remote receivers connected to this port. 4039 * 4040 * This method is read-synchronized on the workspace. 4041 * @param detail The level of detail. 4042 * @param indent The amount of indenting. 4043 * @param bracket The number of surrounding brackets (0, 1, or 2). 4044 * @return A description of the object. 4045 * @exception IllegalActionException If thrown while getting the 4046 * description of subcomponents. 4047 */ 4048 @Override 4049 protected String _description(int detail, int indent, int bracket) 4050 throws IllegalActionException { 4051 try { 4052 _workspace.getReadAccess(); 4053 4054 StringBuffer result = new StringBuffer(); 4055 4056 if (bracket == 1 || bracket == 2) { 4057 result.append(super._description(detail, indent, 1)); 4058 } else { 4059 result.append(super._description(detail, indent, 0)); 4060 } 4061 4062 if ((detail & CONFIGURATION) != 0) { 4063 if (result.toString().trim().length() > 0) { 4064 result.append(" "); 4065 } 4066 4067 result.append("configuration {"); 4068 4069 boolean space = false; 4070 4071 if (isInput()) { 4072 space = true; 4073 result.append("input"); 4074 } 4075 4076 if (isOutput()) { 4077 if (space) { 4078 result.append(" "); 4079 } 4080 4081 space = true; 4082 result.append("output"); 4083 } 4084 4085 if (isMultiport()) { 4086 if (space) { 4087 result.append(" "); 4088 } 4089 4090 space = true; 4091 result.append("multiport"); 4092 } 4093 4094 if (isOpaque()) { 4095 if (space) { 4096 result.append(" "); 4097 } 4098 4099 space = true; 4100 result.append("opaque"); 4101 } 4102 4103 if (space) { 4104 result.append(" "); 4105 } 4106 4107 result.append("{width " + getWidth() + "}}"); 4108 } 4109 4110 if ((detail & RECEIVERS) != 0) { 4111 if (result.toString().trim().length() > 0) { 4112 result.append(" "); 4113 } 4114 4115 result.append("receivers {\n"); 4116 4117 Receiver[][] receivers = getReceivers(); 4118 4119 for (Receiver[] receiver : receivers) { 4120 // One list item per group 4121 result.append(_getIndentPrefix(indent + 1) + "{\n"); 4122 4123 // cd $PTII/ptolemy/configs/test; ptjacl allConfigs.tcl 4124 // was failing with NPE here because of the 4125 // RendezvousSynchrony design pattern has a null 4126 // receiver. This can probably be ignored, it was 4127 // uncovered by recent changes to IOPort. 4128 // 4129 // To replicate, uncomment the receiver == null clause and then do: 4130 // 4131 // bash-3.2$ $PTII/bin/ptjacl 4132 // % java::new ptolemy.moml.MoMLParser 4133 // java0x1 4134 // % set parser [java::new ptolemy.moml.MoMLParser] 4135 // java0x2 4136 // % set t [$parser parseFile /Users/cxh/ptII/ptolemy/actor/designs/RendezvousSynchrony.xml] 4137 // java0x3 4138 // % $t description 4139 // java.lang.NullPointerException: _description had a null reciever?.RendezvousSynchrony._A1_.input 4140 // 4141 //if (receiver == null) { 4142 // throw new NullPointerException("_description had a null receiver?" + getFullName()); 4143 //} 4144 4145 if (receiver != null) { 4146 for (int j = 0; j < receiver.length; j++) { 4147 result.append(_getIndentPrefix(indent + 2)); 4148 result.append("{"); 4149 4150 if (receiver[j] != null) { 4151 result.append(receiver[j].getClass().getName()); 4152 } 4153 4154 result.append("}\n"); 4155 } 4156 } 4157 result.append(_getIndentPrefix(indent + 1) + "}\n"); 4158 } 4159 4160 result.append(_getIndentPrefix(indent) + "}"); 4161 } 4162 4163 if ((detail & REMOTERECEIVERS) != 0) { 4164 if (result.toString().trim().length() > 0) { 4165 result.append(" "); 4166 } 4167 4168 result.append("remotereceivers {\n"); 4169 4170 Receiver[][] receivers = getRemoteReceivers(); 4171 4172 if (receivers != null) { 4173 for (Receiver[] receiver : receivers) { 4174 // One list item per group 4175 result.append(_getIndentPrefix(indent + 1) + "{\n"); 4176 4177 if (receiver != null) { 4178 for (int j = 0; j < receiver.length; j++) { 4179 result.append(_getIndentPrefix(indent + 2)); 4180 result.append("{"); 4181 4182 if (receiver[j] != null) { 4183 result.append( 4184 receiver[j].getClass().getName()); 4185 result.append(" in "); 4186 result.append(receiver[j].getContainer() 4187 .getFullName()); 4188 } 4189 4190 result.append("}\n"); 4191 } 4192 } 4193 4194 result.append(_getIndentPrefix(indent + 1) + "}\n"); 4195 } 4196 } 4197 4198 result.append(_getIndentPrefix(indent) + "}"); 4199 } 4200 4201 if (bracket == 2) { 4202 result.append("}"); 4203 } 4204 4205 return result.toString(); 4206 } finally { 4207 _workspace.doneReading(); 4208 } 4209 } 4210 4211 /** Write a MoML description of the contents of this object, which 4212 * in this class is the attributes plus possibly a special attribute 4213 * to indicate whether the port is a multiport. This method is called 4214 * by _exportMoML(). If there are attributes, then 4215 * each attribute description is indented according to the specified 4216 * depth and terminated with a newline character. 4217 * @param output The output stream to write to. 4218 * @param depth The depth in the hierarchy, to determine indenting. 4219 * @exception IOException If an I/O error occurs. 4220 */ 4221 @Override 4222 protected void _exportMoMLContents(Writer output, int depth) 4223 throws IOException { 4224 if (_isInput) { 4225 output.write( 4226 _getIndentPrefix(depth) + "<property name=\"input\"/>\n"); 4227 } 4228 4229 if (_isOutput) { 4230 output.write( 4231 _getIndentPrefix(depth) + "<property name=\"output\"/>\n"); 4232 } 4233 4234 if (_isMultiport) { 4235 output.write(_getIndentPrefix(depth) 4236 + "<property name=\"multiport\"/>\n"); 4237 } 4238 4239 super._exportMoMLContents(output, depth); 4240 } 4241 4242 /** Return the sums of the widths of the relations linked on the 4243 * inside, except the specified relation. If any of these relations 4244 * has not had its width specified, throw an exception. This is 4245 * used by IORelation to infer the width of a bus with 4246 * unspecified width and to determine whether more than one 4247 * relation with unspecified width is linked on the inside, and 4248 * by the liberalLink() method to check validity of the link. If 4249 * the argument is null, all relations linked on the inside are 4250 * checked. This method is not read-synchronized on the 4251 * workspace, so the caller should be. 4252 * This method ignores the relations for which the width still has 4253 * to be inferred. 4254 * 4255 * @param except The relation to exclude. 4256 * @return The sums of the width of the relations linked on the inside, 4257 * except for the specified port. 4258 * @exception IllegalActionException If thrown while checking if a 4259 * relation needs width inference, while getting the width of the 4260 * relation, while checking if the width or a relation is fixed. 4261 */ 4262 protected int _getInsideWidth(IORelation except) 4263 throws IllegalActionException { 4264 if (IORelation._USE_NEW_WIDTH_INFERENCE_ALGO) { 4265 int result = 0; 4266 Iterator<?> relations = insideRelationList().iterator(); 4267 4268 while (relations.hasNext()) { 4269 IORelation relation = (IORelation) relations.next(); 4270 4271 if (relation != except) { 4272 if (!relation.needsWidthInference()) { 4273 result += relation.getWidth(); 4274 } 4275 } 4276 } 4277 4278 return result; 4279 } else { 4280 int result = 0; 4281 Iterator<?> relations = insideRelationList().iterator(); 4282 4283 while (relations.hasNext()) { 4284 IORelation relation = (IORelation) relations.next(); 4285 4286 if (relation != except) { 4287 if (!relation.isWidthFixed()) { 4288 throw new InvalidStateException(this, 4289 "Width of inside relations cannot " 4290 + "be determined."); 4291 } 4292 4293 result += relation.getWidth(); 4294 } 4295 } 4296 4297 return result; 4298 } 4299 } 4300 4301 /** Return the sums of the widths of the relations linked on the 4302 * outside, except the specified relation. If any of these relations 4303 * has not had its width specified, throw an exception. This is 4304 * used by IORelation to infer the width of a bus with 4305 * unspecified width and to determine whether more than one 4306 * relation with unspecified width is linked on the outside, and 4307 * by the liberalLink() method to check validity of the link. If 4308 * the argument is null, all relations linked on the inside are 4309 * checked. This method is not read-synchronized on the 4310 * workspace, so the caller should be. 4311 * This method ignores the relations for which the width still has 4312 * to be inferred. 4313 4314 * @param except The relation to exclude. 4315 * @return The sums of the width of the relations linked on the outside, 4316 * except for the specified port. 4317 * @exception IllegalActionException If thrown while checking if a 4318 * relation needs width inference, while getting the width of the 4319 * relation, while checking if the width or a relation is fixed. 4320 */ 4321 protected int _getOutsideWidth(IORelation except) 4322 throws IllegalActionException { 4323 if (IORelation._USE_NEW_WIDTH_INFERENCE_ALGO) { 4324 int result = 0; 4325 Iterator<?> relations = linkedRelationList().iterator(); 4326 4327 while (relations.hasNext()) { 4328 IORelation relation = (IORelation) relations.next(); 4329 4330 if (relation != except) { 4331 if (!relation.needsWidthInference()) { 4332 result += relation.getWidth(); 4333 } 4334 } 4335 } 4336 4337 return result; 4338 } else { 4339 int result = 0; 4340 Iterator<?> relations = linkedRelationList().iterator(); 4341 4342 while (relations.hasNext()) { 4343 IORelation relation = (IORelation) relations.next(); 4344 4345 if (relation != except) { 4346 if (!relation.isWidthFixed()) { 4347 throw new InvalidStateException(this, 4348 "Width of outside relations cannot " 4349 + "be determined."); 4350 } 4351 4352 result += relation.getWidth(); 4353 } 4354 } 4355 4356 return result; 4357 } 4358 } 4359 4360 /** If the port is an input, return receivers that handle incoming 4361 * channels from the specified relation or any relation in its 4362 * relation group. If the port is an opaque output 4363 * and the relation is inside linked, return the receivers that handle 4364 * incoming channels from the inside. Since the port may be linked 4365 * multiple times to the specified relation, the <i>occurrences</i> 4366 * argument specifies which of the links we wish to examine. 4367 * The returned value is an array of arrays of the same form 4368 * as that returned by getReceivers() with no arguments. Note that a 4369 * single occurrence of a relation may represent multiple channels 4370 * because it may be a bus. If there are no matching receivers, 4371 * then return an empty array. 4372 * <p> 4373 * This method handles relation groups. That is, given any relation 4374 * in a relation group, it returns the combined receivers of all 4375 * the relations in the relation group, in the order as returned 4376 * by the getRelationGroup() method of Receiver. 4377 * <p> 4378 * This method is read-synchronized on the workspace. 4379 * 4380 * @param relation Relations that are linked on the outside or inside. 4381 * @param occurrence The occurrence number that we are interested in, 4382 * starting at 0. 4383 * @return The local receivers, or an empty array if there are none. 4384 * @exception IllegalActionException If the relation is not linked 4385 * from the outside. 4386 */ 4387 protected Receiver[][] _getReceiversLinkedToGroup(IORelation relation, 4388 int occurrence) throws IllegalActionException { 4389 try { 4390 _workspace.getReadAccess(); 4391 4392 // Allow inside relations also to support opaque, 4393 // non-atomic entities. 4394 boolean insideLink = isInsideGroupLinked(relation); 4395 4396 if (!isGroupLinked(relation) && !insideLink) { 4397 throw new IllegalActionException(this, relation, 4398 "getReceivers: Relation argument is not linked " 4399 + "to me on the inside."); 4400 } 4401 4402 List<?> groupRelationsList = relation.relationGroupList(); 4403 4404 // For efficiency, if there is only one element, then just 4405 // return the results of the private method. 4406 if (groupRelationsList.size() == 1) { 4407 return _getReceivers(relation, occurrence, insideLink); 4408 } 4409 4410 // Create a linked list of the results to be merged. 4411 Receiver[][][] results = new Receiver[groupRelationsList 4412 .size()][][]; 4413 int index = 0; 4414 int width = 0; 4415 Iterator<?> groupRelations = groupRelationsList.iterator(); 4416 4417 while (groupRelations.hasNext()) { 4418 IORelation groupRelation = (IORelation) groupRelations.next(); 4419 Receiver[][] oneResult = _getReceivers(groupRelation, 4420 occurrence, insideLink); 4421 results[index] = oneResult; 4422 index++; 4423 4424 if (oneResult != null && oneResult.length > width) { 4425 width = oneResult.length; 4426 } 4427 } 4428 4429 Receiver[][] result = new Receiver[width][]; 4430 4431 // Now fill in the result for each channel i. 4432 for (int i = 0; i < width; i++) { 4433 // First find out how many replicas there are 4434 // for each channel. 4435 int numberOfReplicas = 0; 4436 4437 for (Receiver[][] result2 : results) { 4438 if (result2 == null || i >= result2.length 4439 || result2[i] == null) { 4440 // This result has no more replicas to contribute. 4441 continue; 4442 } 4443 4444 numberOfReplicas += result2[i].length; 4445 } 4446 4447 result[i] = new Receiver[numberOfReplicas]; 4448 4449 // Next, copy the replicas into the result. 4450 index = 0; 4451 4452 for (Receiver[][] result2 : results) { 4453 if (result2 == null || i >= result2.length 4454 || result2[i] == null) { 4455 // This result has no more replicas to contribute. 4456 continue; 4457 } 4458 4459 for (int k = 0; k < result2[i].length; k++) { 4460 result[i][index] = result2[i][k]; 4461 index++; 4462 } 4463 } 4464 } 4465 4466 return result; 4467 } finally { 4468 _workspace.doneReading(); 4469 } 4470 } 4471 4472 /** Create a new receiver compatible with the local director. 4473 * This is done by asking the local director of the container for 4474 * a new receiver, and then setting its 4475 * container to this port. This allows actors to work across 4476 * several domains, since often the only domain-specific part of 4477 * of an actor is its receivers. Derived classes may choose to 4478 * handle this directly, creating whatever specific type of receiver 4479 * they want. This method is not read-synchronized 4480 * on the workspace, so the caller should be. 4481 * <p> 4482 * The returned receiver is either the new receiver, or another 4483 * receiver wrapping it as specified in {@link #_wrapReceiver(Receiver, int)}. 4484 * @return A new receiver. 4485 * @exception IllegalActionException If the port has no container, 4486 * or the container is unable to return a new receiver (for example 4487 * if it has no local director). 4488 */ 4489 protected Receiver _newInsideReceiver() throws IllegalActionException { 4490 return _newInsideReceiver(0); 4491 } 4492 4493 /** Create a new receiver compatible with the local director. 4494 * This is done by asking the local director of the container for 4495 * a new receiver, and then setting its 4496 * container to this port. This allows actors to work across 4497 * several domains, since often the only domain-specific part of 4498 * of an actor is its receivers. Derived classes may choose to 4499 * handle this directly, creating whatever specific type of receiver 4500 * they want. This method is not read-synchronized 4501 * on the workspace, so the caller should be. 4502 * <p> 4503 * The returned receiver is either the new receiver, or another 4504 * receiver wrapping it as specified in {@link #_wrapReceiver(Receiver, int)}. 4505 * @param channel Used to determine source port. 4506 * @return A new receiver. 4507 * @exception IllegalActionException If the port has no container, 4508 * or the container is unable to return a new receiver (for example 4509 * if it has no local director). 4510 */ 4511 protected Receiver _newInsideReceiver(int channel) 4512 throws IllegalActionException { 4513 Nameable container = getContainer(); 4514 4515 if (container instanceof CompositeActor) { 4516 CompositeActor castContainer = (CompositeActor) container; 4517 4518 if (castContainer.isOpaque() && !castContainer.isAtomic()) { 4519 Receiver receiver = castContainer.newInsideReceiver(); 4520 receiver.setContainer(this); 4521 // return receiver; 4522 return _wrapReceiver(receiver, channel); 4523 } 4524 } 4525 4526 throw new IllegalActionException(this, 4527 "Can only create inside receivers for a port of a non-atomic, " 4528 + "opaque entity."); 4529 } 4530 4531 /** Create a new receiver compatible with the executive director. 4532 * This is done by asking the 4533 * containing actor for a new receiver, and then setting its 4534 * container to this port. This allows actors to work across 4535 * several domains, since often the only domain-specific part of 4536 * of an actor is its receivers. Derived classes may choose to 4537 * handle this directly, creating whatever specific type of receiver 4538 * they want. This method is not write-synchronized 4539 * on the workspace, so the caller should be. 4540 * <p> 4541 * The returned receiver is either the new receiver, or another 4542 * receiver wrapping it as specified in {@link #_wrapReceiver(Receiver, int)}. 4543 * @return A new receiver. 4544 * @exception IllegalActionException If the port has no container, 4545 * or the container is unable to return a new receiver (for example 4546 * if it has no executive director). 4547 */ 4548 protected Receiver _newReceiver() throws IllegalActionException { 4549 return _newReceiver(0); 4550 } 4551 4552 /** Create a new receiver compatible with the executive director. 4553 * This is done by asking the 4554 * containing actor for a new receiver, and then setting its 4555 * container to this port. This allows actors to work across 4556 * several domains, since often the only domain-specific part of 4557 * of an actor is its receivers. Derived classes may choose to 4558 * handle this directly, creating whatever specific type of receiver 4559 * they want. This method is not write-synchronized 4560 * on the workspace, so the caller should be. 4561 * <p> 4562 * The returned receiver is either the new receiver, or another 4563 * receiver wrapping it as specified in {@link #_wrapReceiver(Receiver, int)}. 4564 * @param channel Channel id used to determine the source port. 4565 * @return A new receiver. 4566 * @exception IllegalActionException If the port has no container, 4567 * or the container is unable to return a new receiver (for example 4568 * if it has no executive director). 4569 */ 4570 protected Receiver _newReceiver(int channel) throws IllegalActionException { 4571 Actor container = (Actor) getContainer(); 4572 4573 if (container == null) { 4574 throw new IllegalActionException(this, 4575 "Cannot create a receiver without a container."); 4576 } 4577 Receiver receiver = container.newReceiver(); 4578 receiver.setContainer(this); 4579 // return receiver; 4580 return _wrapReceiver(receiver, channel); 4581 } 4582 4583 /** Remove the receivers associated with the specified 4584 * relation, if there are any. This sets the container 4585 * of each receiver to null. 4586 * @param relation The relation. 4587 */ 4588 protected void _removeReceivers(Relation relation) { 4589 boolean removed = false; 4590 if (_localReceiversTable != null) { 4591 List<Receiver[][]> receivers = _localReceiversTable.get(relation); 4592 if (receivers != null) { 4593 Iterator<Receiver[][]> iterator = receivers.iterator(); 4594 while (iterator.hasNext()) { 4595 Receiver[][] receiverArray = iterator.next(); 4596 for (Receiver[] element : receiverArray) { 4597 if (element != null) { 4598 for (int j = 0; j < element.length; j++) { 4599 if (element[j] != null) { 4600 try { 4601 removed = true; 4602 element[j].setContainer(null); 4603 } catch (IllegalActionException e) { 4604 // This should not happen because we are setting 4605 // the container to null. 4606 throw new InternalErrorException(e); 4607 } 4608 } 4609 } 4610 } 4611 } 4612 } 4613 } 4614 } 4615 if (removed) { 4616 // Must increment the workspace version if receivers have 4617 // been removed. Otherwise, there will be lists of receivers 4618 // cached where the receivers have no containers. 4619 _workspace.incrVersion(); 4620 } 4621 } 4622 4623 /** Send a PortEvent to all port event listeners that 4624 * have registered with this IOPort. 4625 * @param event The event. 4626 * @exception IllegalActionException If thrown by portEvent(). 4627 */ 4628 protected final void _notifyPortEventListeners(IOPortEvent event) 4629 throws IllegalActionException { 4630 if (_hasPortEventListeners) { 4631 for (IOPortEventListener listener : _portEventListeners) { 4632 listener.portEvent(event); 4633 } 4634 } 4635 } 4636 4637 /** Set a constant token so that every call to {@link #get(int)} 4638 * or {@link #get(int,int)} replaces the returned token(s) with 4639 * this specified token. This is a rather specialized piece 4640 * of functionality added to be able to support 4641 * {@link ConstantPublisherPort}. 4642 * @param token The token to return instead of received tokens, 4643 * or null to cancel this functionality. 4644 * @param limit If a non-negative number is given here, then 4645 * limit the number of constant tokens provided. 4646 */ 4647 protected void _setConstant(Token token, int limit) { 4648 _constantToken = token; 4649 _constantLimit = limit; 4650 _constantTokensSent = 0; 4651 } 4652 4653 /** If this port has parameters whose values are tokens that contain 4654 * an object implementing {@link CommunicationAspect}, then wrap the 4655 * receiver specified in the argument using those communication aspects. 4656 * If there are no such parameters, then simply return the specified 4657 * receiver. If there is one such parameter, then use the quantity 4658 * manager to wrap the specified receiver in a new receiver, and return 4659 * that receiver. If there are two such parameters, then use the second 4660 * communication aspect to create a receiver that wraps that created by the 4661 * first communication aspect. Etc. 4662 * @see CommunicationAspect 4663 * @param receiver The receiver to wrap. 4664 * @param channel Channel id used to determine the source port. 4665 * @return Either a new receiver wrapping the specified receiver, 4666 * or the specified receiver. 4667 * @exception IllegalActionException If any parameter of the port 4668 * cannot be evaluated. 4669 */ 4670 protected Receiver _wrapReceiver(Receiver receiver, int channel) 4671 throws IllegalActionException { 4672 Receiver result = receiver; 4673 List<CommunicationAspect> communicationAspects = getCommunicationAspects(); 4674 if (isInput()) { 4675 for (int i = communicationAspects.size() - 1; i >= 0; i--) { 4676 CommunicationAspect communicationAspect = communicationAspects 4677 .get(i); 4678 if (communicationAspect != null) { 4679 result = communicationAspect 4680 .createIntermediateReceiver(result); 4681 } 4682 } 4683 if (result instanceof IntermediateReceiver) { 4684 IntermediateReceiver intermediateReceiver = (IntermediateReceiver) result; 4685 intermediateReceiver.source = (Actor) this.sourcePortList() 4686 .get(channel).getContainer(); 4687 } 4688 } else { 4689 for (int i = 0; i < communicationAspects.size(); i++) { 4690 CommunicationAspect communicationAspect = communicationAspects 4691 .get(i); 4692 result = communicationAspect.createIntermediateReceiver(result); 4693 } 4694 } 4695 // FIXME what if isInput && isOutput?? 4696 4697 return result; 4698 } 4699 4700 /////////////////////////////////////////////////////////////////// 4701 //// protected variables //// 4702 4703 /** The limit of the number of constant values to return instead 4704 * of the received tokens. This is protected so that AbstractReceiver 4705 * can access it. 4706 */ 4707 protected int _constantLimit = -1; 4708 4709 /** The constant value to return instead of the received tokens. 4710 * This is protected so that AbstractReceiver can access it. 4711 */ 4712 protected Token _constantToken; 4713 4714 /** The number of constant tokens that have been sent since the last 4715 * call to _setConstant(). This is protected so that AbstractReceiver 4716 * can access it. 4717 */ 4718 protected int _constantTokensSent = 0; 4719 4720 /** Flag that is true if there are port event listeners. */ 4721 protected boolean _hasPortEventListeners = false; 4722 4723 /** The list of IOPortEventListeners registered with this object. 4724 * NOTE: Because of the way we synchronize on this object, it should 4725 * never be reset to null after the first list is created. 4726 */ 4727 protected List<IOPortEventListener> _portEventListeners = null; 4728 4729 /////////////////////////////////////////////////////////////////// 4730 //// private methods //// 4731 4732 /** Check that a port that is not a multiport will not have too many 4733 * links if a link is established with the specified relation. 4734 * @exception IllegalActionException If the port will have too many 4735 * links. 4736 */ 4737 private void _checkMultiportLink(IORelation relation) 4738 throws IllegalActionException { 4739 if (IORelation._USE_NEW_WIDTH_INFERENCE_ALGO) { 4740 if (_isInsideLinkable(relation.getContainer())) { 4741 // An inside link 4742 // Check for existing inside links 4743 if (!isMultiport() && numInsideLinks() >= 1) { 4744 throw new IllegalActionException(this, relation, 4745 "Attempt to link more than one relation " 4746 + "to a single port."); 4747 } 4748 4749 if (relation.isWidthFixed() && relation.getWidth() > 1) { 4750 // Relation is a bus. 4751 if (!isMultiport()) { 4752 throw new IllegalActionException(this, relation, 4753 "Attempt to link a bus relation " 4754 + "to a single port."); 4755 } 4756 } 4757 } else { 4758 // An outside link 4759 // Check for existing outside links 4760 if (!isMultiport() && numLinks() >= 1) { 4761 throw new IllegalActionException(this, relation, 4762 "Attempt to link more than one relation " 4763 + "to a single port."); 4764 } 4765 } 4766 } else { 4767 if (_isInsideLinkable(relation.getContainer())) { 4768 // An inside link 4769 // Check for existing inside links 4770 if (!isMultiport() && numInsideLinks() >= 1) { 4771 throw new IllegalActionException(this, relation, 4772 "Attempt to link more than one relation " 4773 + "to a single port."); 4774 } 4775 4776 if (relation.getWidth() != 1 || !relation.isWidthFixed()) { 4777 // Relation is a bus. 4778 if (!isMultiport()) { 4779 throw new IllegalActionException(this, relation, 4780 "Attempt to link a bus relation " 4781 + "to a single port."); 4782 } 4783 4784 if (!relation.isWidthFixed()) { 4785 // Make sure there are no other busses already 4786 // connected with unspecified widths. 4787 try { 4788 _getInsideWidth(null); 4789 } catch (InvalidStateException ex) { 4790 throw new IllegalActionException(this, relation, 4791 "Attempt to link a second bus relation " 4792 + "with unspecified width to the inside " 4793 + "of a port."); 4794 } 4795 } 4796 } 4797 } else { 4798 // An outside link 4799 // Check for existing outside links 4800 if (!isMultiport() && numLinks() >= 1) { 4801 throw new IllegalActionException(this, relation, 4802 "Attempt to link more than one relation " 4803 + "to a single port."); 4804 } 4805 4806 if (relation.getWidth() != 1 || !relation.isWidthFixed()) { 4807 // Relation is a bus. 4808 4809 /* This is now allowed. 4810 if (!isMultiport()) { 4811 throw new IllegalActionException(this, relation, 4812 "Attempt to link a bus relation " 4813 + "to a single port."); 4814 } 4815 */ 4816 Iterator<?> relations = linkedRelationList().iterator(); 4817 4818 while (relations.hasNext()) { 4819 IORelation theRelation = (IORelation) relations.next(); 4820 4821 // A null link (supported since indexed links) might 4822 // yield a null relation here. EAL 7/19/00. 4823 if (theRelation != null 4824 && !theRelation.isWidthFixed()) { 4825 throw new IllegalActionException(this, relation, 4826 "Attempt to link a second bus relation " 4827 + "with unspecified width to the outside " 4828 + "of a port."); 4829 } 4830 } 4831 } 4832 } 4833 } 4834 } 4835 4836 /** Get the persistent value for the specified channel, if there is one. 4837 * If the persistent value is a SmoothToken, the first extrapolate its 4838 * value to the current time. 4839 * @param channelIndex The channel. 4840 * @param inside True to check an inside channel. 4841 * @return The persistent value, or null if there isn't one. 4842 * @exception IllegalActionException If getting current time fails. 4843 */ 4844 private Token _getPersistentValue(int channelIndex, boolean inside) 4845 throws IllegalActionException { 4846 Token[] tokens = _persistentTokens; 4847 if (inside) { 4848 tokens = _persistentTokensInside; 4849 } 4850 if (tokens != null && tokens.length > channelIndex 4851 && tokens[channelIndex] != null) { 4852 Token result = tokens[channelIndex]; 4853 if (result instanceof SmoothToken) { 4854 Time currentTime = getModelTime(channelIndex, inside); 4855 return ((SmoothToken) result).extrapolate(currentTime); 4856 } else { 4857 return result; 4858 } 4859 } else if (_persistentToken != null) { 4860 return _persistentToken; 4861 } 4862 return null; 4863 } 4864 4865 /** If the port is an input, return receivers that handle incoming 4866 * channels from the specified relation. If the port is an opaque output 4867 * and the relation is inside linked, return the receivers that handle 4868 * incoming channels from the inside. Since the port may be linked 4869 * multiple times to the specified relation, the <i>occurrences</i> 4870 * argument specifies which of the links we wish to examine. 4871 * The returned value is an array of arrays of the same form 4872 * as that returned by getReceivers() with no arguments. Note that a 4873 * single occurrence of a relation may represent multiple channels 4874 * because it may be a bus. If there are no matching receivers, 4875 * then return an empty array. 4876 * <p> 4877 * This method works only with relations directly linked to this 4878 * port. Use the public method to work on relation groups. If the 4879 * specified relation is not linked to this port, then this method 4880 * returns an empty array. 4881 * @param relation Relations that are linked on the outside or inside. 4882 * @param occurrence The occurrence number that we are interested in, 4883 * starting at 0. 4884 * @param insideLink True if this is an inside link. 4885 * @return The local receivers, or an empty array if there are none. 4886 * @exception IllegalActionException If the relation is not linked 4887 * from the outside. 4888 */ 4889 private Receiver[][] _getReceivers(IORelation relation, int occurrence, 4890 boolean insideLink) throws IllegalActionException { 4891 boolean opaque = isOpaque(); 4892 4893 if (!isInput() && !(opaque && insideLink && isOutput())) { 4894 return _EMPTY_RECEIVER_ARRAY; 4895 } 4896 4897 int width = relation.getWidth(); 4898 4899 if (getWidth() == 1 && width > 1) { 4900 // This can occur if we have a wide relation driving a narrower 4901 // port. 4902 width = 1; 4903 } 4904 4905 if (width <= 0) { 4906 return _EMPTY_RECEIVER_ARRAY; 4907 } 4908 4909 Receiver[][] result = null; 4910 4911 // If the port is opaque, return the local Receivers for the 4912 // relation. 4913 if (opaque) { 4914 // If localReceiversTable is null, then createReceivers() 4915 // hasn't been called, so there is nothing to return. 4916 if (_localReceiversTable == null) { 4917 return _EMPTY_RECEIVER_ARRAY; 4918 } 4919 4920 if (_localReceiversTable.containsKey(relation)) { 4921 // Get the list of receivers for this relation. 4922 List<?> list = _localReceiversTable.get(relation); 4923 4924 try { 4925 result = (Receiver[][]) list.get(occurrence); 4926 } catch (IndexOutOfBoundsException ex) { 4927 return _EMPTY_RECEIVER_ARRAY; 4928 } 4929 4930 if (result.length != width) { 4931 throw new InvalidStateException(this, 4932 "getReceivers(IORelation, int): " 4933 + "Invalid receivers. " + "result.length = " 4934 + result.length + " width = " + width 4935 + ". Need to call createReceivers()?"); 4936 } 4937 } 4938 4939 return result; 4940 } else { 4941 // If a transparent input port, ask its all inside receivers, 4942 // and trim the returned Receivers array to get the 4943 // part corresponding to this occurrence of the IORelation. 4944 Receiver[][] insideReceivers = getReceivers(); 4945 4946 if (insideReceivers == null) { 4947 return _EMPTY_RECEIVER_ARRAY; 4948 } 4949 4950 int insideWidth = insideReceivers.length; 4951 int index = 0; 4952 result = new Receiver[width][]; 4953 4954 Iterator<?> outsideRelations = linkedRelationList().iterator(); 4955 int seen = 0; 4956 4957 while (outsideRelations.hasNext()) { 4958 IORelation outsideRelation = (IORelation) outsideRelations 4959 .next(); 4960 4961 // A null link (supported since indexed links) might 4962 // yield a null relation here. EAL 7/19/00. 4963 if (outsideRelation != null) { 4964 if (outsideRelation == relation) { 4965 if (seen == occurrence) { 4966 // Have to be careful here to get the right 4967 // occurrence of the relation. EAL 7/30/00. 4968 result = new Receiver[width][]; 4969 4970 int receiverSize = java.lang.Math.min(width, 4971 insideWidth - index); 4972 4973 for (int i = 0; i < receiverSize; i++) { 4974 result[i] = insideReceivers[index++]; 4975 } 4976 4977 break; 4978 } else { 4979 seen++; 4980 index += outsideRelation.getWidth(); 4981 4982 if (index > insideWidth) { 4983 break; 4984 } 4985 } 4986 } else { 4987 index += outsideRelation.getWidth(); 4988 4989 if (index > insideWidth) { 4990 break; 4991 } 4992 } 4993 } 4994 } 4995 4996 return result; 4997 } 4998 } 4999 5000 /** Return the width of the port. The width is the sum of the 5001 * widths of the relations that the port is linked to (on the outside). 5002 * Note that this method cannot be used to determine whether a port 5003 * is connected (deeply) to another port that can either supply it with 5004 * data or consume data it produces. The correct methods to use to 5005 * determine that are numberOfSinks() and numberOfSources(). 5006 * This method is read-synchronized on the workspace. 5007 * This method will trigger the width inference algorithm if necessary. 5008 * @param createReceivers True if {@link #getReceivers()} should be called 5009 * if the cached width of the port is not the same as the sum of the width 5010 * of the linked relations. 5011 * @return The width of the port. 5012 * @exception IllegalActionException 5013 * @see #numberOfSinks() 5014 * @see #numberOfSources() 5015 */ 5016 private int _getWidth(boolean createReceivers) 5017 throws IllegalActionException { 5018 try { 5019 _workspace.getReadAccess(); 5020 5021 long version = _workspace.getVersion(); 5022 5023 if (_widthVersion != version) { 5024 5025 // If this is not a multiport, the width is always zero or one. 5026 int sum = 0; 5027 Iterator<?> relations = linkedRelationList().iterator(); 5028 5029 while (relations.hasNext()) { 5030 IORelation relation = (IORelation) relations.next(); 5031 5032 // A null link (supported since indexed links) might 5033 // yield a null relation here. EAL 7/19/00. 5034 if (relation != null) { 5035 5036 // Calling getWidth on a relation for which the width has to be 5037 // inferred will trigger the width inference algorithm here. 5038 sum += relation.getWidth(); 5039 } 5040 } 5041 5042 if (!isMultiport()) { 5043 if (sum > 0) { 5044 _width = 1; 5045 } else { 5046 _width = 0; 5047 } 5048 } else { 5049 if (_width != sum) { 5050 // Need to re-create receivers for Pub/Sub in Opaques 5051 // See getWidthInside() for a similar piece of code. 5052 // Need to check to see if there is a director, otherwise 5053 // _description will fail on MaximumEntropySpectrum, see 5054 // ptolemy/domains/sdf/lib/test/MaximumEntropySpectrum.tcl 5055 if (createReceivers && isOpaque() 5056 && ((Actor) getContainer()) 5057 .getDirector() != null) { 5058 // FIXME: maybe we should only call createReceivers() 5059 // if the sum is less than the _width (see below). 5060 createReceivers(); 5061 } 5062 _width = sum; 5063 } 5064 } 5065 _widthVersion = version; 5066 } 5067 5068 return _width; 5069 } finally { 5070 _workspace.doneReading(); 5071 } 5072 } 5073 5074 private void _init() throws IllegalActionException { 5075 try { 5076 defaultValue = new Parameter(this, "defaultValue"); 5077 } catch (NameDuplicationException e) { 5078 throw new IllegalActionException(this, e.getCause(), 5079 e.getMessage()); 5080 } 5081 } 5082 5083 // Invalidate schedule and type resolution and width inference of the director 5084 // of the container, if there is one. 5085 private void _invalidate() { 5086 Nameable container = getContainer(); 5087 5088 if (container instanceof Actor) { 5089 if (container instanceof CompositeActor) { 5090 ((CompositeActor) container).notifyConnectivityChange(); 5091 } 5092 5093 Director director = ((Actor) container).getDirector(); 5094 5095 if (director != null) { 5096 director.invalidateSchedule(); 5097 director.invalidateResolvedTypes(); 5098 } 5099 // Need to do this for the executive director as well because the port 5100 // may belong to an opaque composite actor. 5101 Director executiveDirector = ((Actor) container) 5102 .getExecutiveDirector(); 5103 if (executiveDirector != null && executiveDirector != director) { 5104 executiveDirector.invalidateSchedule(); 5105 executiveDirector.invalidateResolvedTypes(); 5106 } 5107 } 5108 } 5109 5110 /** If this port is persistent or the token argument is a 5111 * {@link SmoothToken}, then record the value of this token 5112 * as the value for the specified channel. 5113 * @param channelIndex The channel index. 5114 * @param token The token 5115 * @param inside True for an inside channel. 5116 * @exception IllegalActionException If the width of this port cannot be 5117 * determined. 5118 */ 5119 private void _recordPersistentValueIfNecessary(int channelIndex, 5120 Token token, boolean inside) throws IllegalActionException { 5121 Token[] tokens = _persistentTokens; 5122 if (inside) { 5123 tokens = _persistentTokensInside; 5124 } 5125 5126 if (_persistentToken != null || tokens != null 5127 || token instanceof SmoothToken) { 5128 // Token may be persistent. Make sure there is a place to store it. 5129 int width = getWidth(); 5130 if (tokens == null) { 5131 tokens = new Token[width]; 5132 // If a single default has been given, enter that now. 5133 if (_persistentToken != null) { 5134 for (int i = 0; i < width; i++) { 5135 tokens[i] = _persistentToken; 5136 } 5137 } 5138 } else if (tokens.length != width) { 5139 // Width doesn't match previous value. Create a new array and copy 5140 // the old values into it. 5141 Token[] newArray = new Token[width]; 5142 if (tokens.length < width) { 5143 // The width has increased or is longer than the specified array. 5144 // Copy as many as we've got. 5145 System.arraycopy(tokens, 0, newArray, 0, tokens.length); 5146 // For the remaining slots, we need to examine the defaultValue parameter. 5147 Token value = defaultValue.getToken(); 5148 if (value != null && !(value instanceof ArrayToken)) { 5149 // A single default value has been given. Fill the new array with it. 5150 // Otherwise, the array will be filled with null. 5151 for (int i = tokens.length; i < width; i++) { 5152 tokens[i] = value; 5153 } 5154 } 5155 } else { 5156 // The width has decreased or is shorter than the specified array. 5157 // Copy as many as we need. 5158 System.arraycopy(tokens, 0, newArray, 0, width); 5159 } 5160 tokens = newArray; 5161 if (_persistentToken != null) { 5162 for (int i = 0; i < width; i++) { 5163 tokens[i] = _persistentToken; 5164 } 5165 } 5166 } 5167 // The single default value will continue to mark this persistent. 5168 // So don't set it to null. 5169 // _persistentToken = null; 5170 } 5171 if (token instanceof SmoothToken) { 5172 // Token is persistent. 5173 // If the token is generated using the expression language function 5174 // smoothToken(), then it always has time equal to the start time. 5175 // Here, we reset that time to the current time. 5176 // This also ensures that if a SmoothToken is delayed, its time 5177 // gets reset at the receiver. Notice that since tokens are 5178 // immutable, we have to create a new SmoothToken here. 5179 Time currentTime = getModelTime(channelIndex, inside); 5180 Time tokenTime = ((SmoothToken) token).getTime(); 5181 if (!currentTime.equals(tokenTime)) { 5182 token = new SmoothToken(((SmoothToken) token).doubleValue(), 5183 currentTime, ((SmoothToken) token).derivativeValues()); 5184 } 5185 tokens[channelIndex] = token; 5186 } else if (tokens != null && _persistentToken == null 5187 && tokens[channelIndex] instanceof SmoothToken) { 5188 // If the persistence is because of having received a SmoothToken, but we are 5189 // now receiving something that is not a SmoothToken, then we need to reverse the 5190 // persistence. 5191 // FIXME: Potential weird corner case here where defaultValue is an array 5192 // that is not long enough to encompass this channel. 5193 tokens[channelIndex] = null; 5194 } else if (_persistentToken != null) { 5195 tokens[channelIndex] = token; 5196 } 5197 if (inside) { 5198 _persistentTokensInside = tokens; 5199 } else { 5200 _persistentTokens = tokens; 5201 } 5202 } 5203 5204 /////////////////////////////////////////////////////////////////// 5205 //// private variables //// 5206 5207 /** List of communication aspects specified for the port. */ 5208 private List<CommunicationAspect> _communicationAspects; 5209 5210 /** The default width. In case there is no unique solution for a relation 5211 * connected to this port the default width will be used. 5212 */ 5213 private int _defaultWidth = -1; 5214 5215 /** To avoid creating this repeatedly, we use a single version. */ 5216 private static final Receiver[][] _EMPTY_RECEIVER_ARRAY = new Receiver[0][0]; 5217 5218 /** Indicate whether the port is an input, an output, or both. 5219 * The value may be overridden in transparent ports, in that if 5220 * a transparent port is inside linked to an input or output port, 5221 * then it will be considered an inside or output port respectively. 5222 * This determination is cached, so we need variables to track the 5223 * validity of the cache. 5224 * 'transient' means that the variable will not be serialized. 5225 */ 5226 private boolean _isInput; 5227 5228 // Indicate whether the port is an input, an output, or both. 5229 // The value may be overridden in transparent ports, in that if 5230 // a transparent port is inside linked to an input or output port, 5231 // then it will be considered an inside or output port respectively. 5232 // This determination is cached, so we need variables to track the 5233 // validity of the cache. 5234 // 'transient' means that the variable will not be serialized. 5235 private boolean _isOutput; 5236 5237 private transient long _insideInputVersion = -1; 5238 5239 private transient long _insideOutputVersion = -1; 5240 5241 // Flag that the input/output status has been set. 5242 private boolean _isInputOutputStatusSet = false; 5243 5244 // Indicate whether the port is a multiport. Default false. 5245 private boolean _isMultiport = false; 5246 5247 // The cached inside width of the port, which is the sum of the 5248 // widths of the inside relations. The default 0 because 5249 // initially there are no linked relations. It is set or updated 5250 // when getWidthInside() is called. 'transient' means that the 5251 // variable will not be serialized. 5252 private transient int _insideWidth = 0; 5253 5254 // The workspace version number on the last update of the _insideWidth. 5255 // 'transient' means that the variable will not be serialized. 5256 private transient long _insideWidthVersion = -1; 5257 5258 // A cache of the deeply connected Receivers, and the versions. 5259 // 'transient' means that the variable will not be serialized. 5260 private transient Receiver[][] _farReceivers; 5261 5262 private transient long _farReceiversVersion = -1; 5263 5264 // A cache of the local Receivers, and the version. 5265 // 'transient' means that the variable will not be serialized. 5266 private transient Receiver[][] _localReceivers; 5267 5268 // Lists of local receivers, indexed by relation. 5269 private HashMap<IORelation, List<Receiver[][]>> _localReceiversTable; 5270 5271 private transient long _localReceiversVersion = -1; 5272 5273 // A cache of the local Receivers, and the version. 5274 // 'transient' means that the variable will not be serialized. 5275 private transient Receiver[][] _localInsideReceivers; 5276 5277 private transient long _localInsideReceiversVersion = -1; 5278 5279 // A cache of the inside Receivers, and the version. 5280 private transient Receiver[][] _insideReceivers; 5281 5282 private transient long _insideReceiversVersion = -1; 5283 5284 // If port is linked to a communication aspect then all tokens 5285 // are sent to the communication aspect. Receivers and farreceivers 5286 // are replaced by this intermediate receiver. 5287 private IntermediateReceiver _intermediateFarReceiver; 5288 5289 // A cache of the number of sinks, since it's expensive to compute. 5290 private transient int _numberOfSinks; 5291 private transient long _numberOfSinksVersion = -1; 5292 5293 // A cache of the number of sources, since it's expensive to compute. 5294 private transient int _numberOfSources; 5295 private transient long _numberOfSourcesVersion = -1; 5296 5297 /** Value of defaultValue if it is a scalar. */ 5298 private Token _persistentToken; 5299 5300 /** Value of defaultValue (if it is an array) or the most recently received value 5301 * indexed by channel. 5302 */ 5303 private Token[] _persistentTokens; 5304 5305 /** Value of defaultValue (if it is an array) or the most recently received value 5306 * indexed by an inside channel. 5307 */ 5308 private Token[] _persistentTokensInside; 5309 5310 // A cache of the sink port list. 5311 private transient LinkedList<IOPort> _sinkPortList; 5312 private transient long _sinkPortListVersion; 5313 5314 // A cache of the source port list. 5315 private transient LinkedList<IOPort> _sourcePortList; 5316 private transient long _sourcePortListVersion; 5317 5318 // The cached width of the port, which is the sum of the widths of the 5319 // linked relations. The default 0 because initially there are no 5320 // linked relations. It is set or updated when getWidth() is called. 5321 // 'transient' means that the variable will not be serialized. 5322 private transient int _width = 0; 5323 5324 // Constrains on the width of this port (it has to be equal to the parameters). 5325 private Set<Parameter> _widthEqualToParameter = new HashSet<Parameter>(); 5326 5327 // Constraints on the width of this port (it has to be equal to the width of the port). 5328 private Set<IOPort> _widthEqualToPort = new HashSet<IOPort>(); 5329 5330 // The workspace version number on the last update of the _width. 5331 // 'transient' means that the variable will not be serialized. 5332 private transient long _widthVersion = -1; 5333}