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}