001/* Relation 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
028 */
029package ptolemy.actor;
030
031import java.util.Collections;
032import java.util.Enumeration;
033import java.util.HashMap;
034import java.util.Iterator;
035import java.util.LinkedList;
036import java.util.List;
037
038import ptolemy.data.IntToken;
039import ptolemy.data.expr.Parameter;
040import ptolemy.data.type.BaseType;
041import ptolemy.kernel.ComponentRelation;
042import ptolemy.kernel.CompositeEntity;
043import ptolemy.kernel.Entity;
044import ptolemy.kernel.Port;
045import ptolemy.kernel.Relation;
046import ptolemy.kernel.util.Attribute;
047import ptolemy.kernel.util.IllegalActionException;
048import ptolemy.kernel.util.InternalErrorException;
049import ptolemy.kernel.util.InvalidStateException;
050import ptolemy.kernel.util.KernelException;
051import ptolemy.kernel.util.NameDuplicationException;
052import ptolemy.kernel.util.Nameable;
053import ptolemy.kernel.util.Settable;
054import ptolemy.kernel.util.Workspace;
055
056///////////////////////////////////////////////////////////////////
057//// IORelation
058
059/**
060 This class mediates connections between ports that can send data to
061 one another via message passing. One purpose of this relation is to
062 ensure that IOPorts are only connected to IOPorts. A second purpose
063 is to support the notion of a <i>width</i> to represent something
064 like a bus. By default an IORelation is a bus for which the width
065 will be inferred, which corresponds to a width equal to Auto. In
066 Vergil you can change the width from Auto to a specific value, to
067 explicitly specify the width of a relation. Specifying a width
068 equal to zero will disable the relation.
069 A width equal equal to -1 is equivalent to a width equal to Auto,
070 in which case the width will be inferred (if possible) from the
071 context. In particular, if this relation is linked on the inside
072 to a port with some width, then the width of this relation will
073 be inferred to be the enough so that the widths of all inside
074 linked relations adds up to the outside width of the port.
075 If this IORelation is linked to another
076 instance of IORelation, then the width of the two IORelations is
077 constrained to be the same.
078 <p>
079 Instances of IORelation can only be linked to instances of IOPort
080 or instances of IORelation.
081 Derived classes may further constrain this to subclasses of IOPort
082 of IORelation.
083 Such derived classes should override the protected methods _checkPort()
084 and _checkRelation() to throw an exception.
085 <p>
086 To link a IOPort to an IORelation, use the link() or
087 liberalLink() method in the IOPort class.  To remove a link,
088 use the unlink() method. To link (unlink) an IORelation to an IORelation,
089 use the link() (unlink()) method of IORelation.
090 <p>
091 The container for instances of this class can only be instances of
092 CompositeActor.  Derived classes may wish to further constrain the
093 container to subclasses of ComponentEntity.  To do this, they should
094 override the _checkContainer() method.
095
096 @author Edward A. Lee, Jie Liu, Contributor: Bert Rodiers
097 @version $Id$
098 @since Ptolemy II 0.2
099 @Pt.ProposedRating Green (eal)
100 @Pt.AcceptedRating Green (acataldo)
101 */
102public class IORelation extends ComponentRelation {
103    /** Construct a relation in the default workspace with an empty string
104     *  as its name. Add the relation to the directory of the workspace.
105     */
106    public IORelation() {
107        super();
108        _init();
109    }
110
111    /** Construct a relation in the specified workspace with an empty
112     *  string as a name. You can then change the name with setName().
113     *  If the workspace argument is null, then use the default workspace.
114     *  Add the relation to the workspace directory.
115     *
116     *  @param workspace The workspace that will list the relation.
117     */
118    public IORelation(Workspace workspace) {
119        super(workspace);
120        _init();
121    }
122
123    /** Construct a relation with the given name contained by the specified
124     *  entity. The container argument must not be null, or a
125     *  NullPointerException will be thrown.  This relation will use the
126     *  workspace of the container for synchronization and version counts.
127     *  If the name argument is null, then the name is set to the empty string.
128     *  This constructor write-synchronizes on the workspace.
129     *
130     *  @param container The container.
131     *  @param name The name of the relation.
132     *  @exception IllegalActionException If the container is incompatible
133     *   with this relation.
134     *  @exception NameDuplicationException If the name coincides with
135     *   a relation already in the container.
136     */
137    public IORelation(CompositeEntity container, String name)
138            throws IllegalActionException, NameDuplicationException {
139        super(container, name);
140        _init();
141    }
142
143    ///////////////////////////////////////////////////////////////////
144    ////                         parameters                        ////
145
146    /** The width of this relation. This is an integer that defaults
147     *  to WIDTH_TO_INFER, which means that the width will be inferred.
148     */
149    public Parameter width;
150
151    ///////////////////////////////////////////////////////////////////
152    ////                         public methods                    ////
153
154    /** React to a change in an attribute.  This method is called by
155     *  a contained attribute when its value changes.  This overrides
156     *  the base class so that if the attribute is an instance of
157     *  Parameter and the name is "width", then the width of the Relation
158     *  is set.
159     *  @param attribute The attribute that changed.
160     *  @exception IllegalActionException If the change is not acceptable
161     *   to this container.
162     */
163    @Override
164    public void attributeChanged(Attribute attribute)
165            throws IllegalActionException {
166        if (attribute instanceof Parameter
167                && "width".equals(attribute.getName())) {
168            IntToken t = (IntToken) ((Parameter) attribute).getToken();
169
170            if (t != null) {
171                int width = t.intValue();
172                _setWidth(width);
173            }
174        }
175    }
176
177    /** Clone the object into the specified workspace. The new object is
178     *  <i>not</i> added to the directory of that workspace (you must do this
179     *  yourself if you want it there).
180     *  The result is a new relation with no links and no container, but with
181     *  the same width as the original.
182     *
183     *  @param workspace The workspace for the cloned object.
184     *  @exception CloneNotSupportedException If one or more of the attributes
185     *   cannot be cloned.
186     *  @return A new ComponentRelation.
187     */
188    @Override
189    public Object clone(Workspace workspace) throws CloneNotSupportedException {
190        IORelation newObject = (IORelation) super.clone(workspace);
191        newObject._inferredWidthVersion = -1;
192        newObject._cachedWidth = -2;
193        return newObject;
194    }
195
196    /** Return the receivers of all input ports linked to this
197     *  relation, directly or indirectly through a relation group,
198     *  except those in the port
199     *  given as an argument. The returned value is an array of
200     *  arrays. The first index (the row) specifies the group, where
201     *  a group receives the same data from a channel.
202     *  Each channel normally receives distinct data. The
203     *  second index (the column) specifies the receiver number within
204     *  the group of receivers that get copies from the same channel.
205     *  <p>
206     *  The number of groups (rows) is less than or equal to the
207     *  width of the relation, which is always at least one. If
208     *  there are no receivers then return null.
209     *  <p>
210     *  For each channel, there may be any number of receivers in the group.
211     *  The individual receivers are selected using the second index of the
212     *  returned array of arrays.  If there are no receivers in the group,
213     *  then the channel is represented by null.  I.e., if the returned
214     *  array of arrays is <i>x</i> and the channel number is <i>c</i>,
215     *  then <i>x</i>[<i>c</i>] is null.  Otherwise, it is an array, where
216     *  the size of the array is the number of receivers in the group.
217     *  <p>
218     *  NOTE: This method may have the effect of creating new receivers in the
219     *  remote input ports and losing the previous receivers in those ports,
220     *  together with any data they may contain.  This occurs only if the
221     *  topology has changed since the receivers were created, and that change
222     *  resulting in one of those ports not having the right number of
223     *  receivers.
224     *  <p>
225     *  This method read-synchronizes on the workspace.
226     *
227     *  @see IOPort#getRemoteReceivers
228     *  @param except The port to exclude, or null to not
229     *   exclude any ports.
230     *  @return The receivers associated with this relation.
231     *  @exception IllegalActionException If thrown while determining
232     *  the cascade.
233     */
234    public Receiver[][] deepReceivers(IOPort except)
235            throws /*InvalidStateException,*/IllegalActionException {
236        try {
237            _workspace.getReadAccess();
238
239            Receiver[][] result = new Receiver[0][0];
240            Iterator<?> inputs = linkedDestinationPortList(except).iterator();
241            Receiver[][] receivers; //= new Receiver[0][0];
242
243            // NOTE: We have to be careful here to keep track of
244            // multiple occurrences of a port in this list.
245            // EAL 7/30/00.
246            HashMap<IOPort, Integer> seen = new HashMap<IOPort, Integer>();
247
248            while (inputs.hasNext()) {
249                IOPort p = (IOPort) inputs.next();
250
251                if (p.isInsideGroupLinked(this) && !p.isOpaque()) {
252                    // if p is a transparent port and this relation links
253                    // from the inside, then get the Receivers outside p.
254                    try {
255                        receivers = p.getRemoteReceivers(this);
256                    } catch (IllegalActionException ex) {
257                        throw new InternalErrorException(this, ex, null);
258                    }
259                } else {
260                    // if p not a transparent port, or this relation is linked
261                    // to p from the outside.
262                    try {
263                        // Note that this may be an inside or outside linked
264                        // relation.
265                        // NOTE: We have to be careful here to keep track of
266                        // multiple occurrences of a port in this list.
267                        // EAL 7/30/00.
268                        int occurrence = 0;
269
270                        if (seen.containsKey(p)) {
271                            occurrence = seen.get(p).intValue();
272                            occurrence++;
273                        }
274
275                        seen.put(p, Integer.valueOf(occurrence));
276
277                        receivers = p._getReceiversLinkedToGroup(this,
278                                occurrence);
279                    } catch (IllegalActionException ex) {
280                        throw new InternalErrorException(this, ex, null);
281                    }
282                }
283
284                result = _cascade(result, receivers);
285            }
286
287            return result;
288        } finally {
289            _workspace.doneReading();
290        }
291    }
292
293    /** Return the width of the IORelation, which is always at least one.
294     *  If the width has been set to the value of WIDTH_TO_INFER, then
295     *  the relation is a bus with
296     *  unspecified width, and the width needs to be inferred from the
297     *  way the relation is connected.  This is done by checking the
298     *  ports that this relation is linked to from the inside and setting
299     *  the width to the maximum of those port widths, minus the widths of
300     *  other relations linked to those ports on the inside. Each such port is
301     *  allowed to have at most one inside relation with an unspecified
302     *  width, or an exception is thrown.  If this inference yields a width
303     *  of zero, then return one.
304     *
305     *  @return The width, which is at least zero.
306     *  @exception IllegalActionException If thrown while getting the
307     *  user width, determining if the width inference is needed or if
308     *  thrown by inferWidths().
309     *  @see #setWidth(int)
310     */
311    public int getWidth() throws IllegalActionException {
312        int width = _getUserWidth();
313        if (_USE_NEW_WIDTH_INFERENCE_ALGO) {
314
315            // If _width equals to the value of WIDTH_TO_INFER
316            // we might need to infer it. Since the width inference
317            // is cached we only need to infer it in case
318            // needsWidthInference() returns true.
319            if (width == WIDTH_TO_INFER) {
320                if (needsWidthInference()) {
321
322                    Nameable container = getContainer();
323
324                    if (container instanceof CompositeActor) {
325                        ((CompositeActor) container).inferWidths();
326                    } else {
327                        throw new IllegalActionException(this,
328                                "Can't infer the widths "
329                                        + "of the relations since no container or container is not a CompositeActor.");
330                    }
331
332                    assert _inferredWidthVersion == _workspace.getVersion();
333                    assert !needsWidthInference();
334                }
335                return _inferredWidth;
336            }
337            return width;
338        } else {
339            if (width == 0) {
340                return _inferWidth();
341            }
342            return width;
343        }
344    }
345
346    /** Return true if the relation has a definite width (i.e.,
347     *  setWidth() has not been called with a value equal to
348     *  WIDTH_TO_INFER.
349     *  @return True if the width has been set to a positive value.
350     *  @exception IllegalActionException If the expression for the width cannot
351     *   be parsed or cannot be evaluated, or if the result of evaluation
352     *   violates type constraints, or if the result of evaluation is null
353     *   and there are variables that depend on this one.
354     */
355    public boolean isWidthFixed() throws IllegalActionException {
356        int width = _getUserWidth();
357        return width != WIDTH_TO_INFER;
358    }
359
360    /** List the input ports that this relation connects to from the
361     *  outside, and the output ports that it connects to from
362     *  the inside. I.e., list the ports through or to which we
363     *  could send data. Two ports are connected if they are
364     *  linked to relations in the same relation group.
365     *  This method read-synchronizes on the workspace.
366     *  @see ptolemy.kernel.Relation#linkedPorts
367     *  @return An enumeration of IOPort objects.
368     */
369    public List<IOPort> linkedDestinationPortList() {
370        return linkedDestinationPortList(null);
371    }
372
373    /** List the input ports that this relation connects to from the
374     *  outside and the output ports that it connects to from
375     *  the inside, except the port given as an argument.
376     *  I.e., list the ports through or to which we
377     *  could send data. Two ports are connected if they are
378     *  linked to relations in the same relation group.
379     *  This method read-synchronizes on the workspace.
380     *  @see ptolemy.kernel.Relation#linkedPortList(ptolemy.kernel.Port)
381     *  @param except The port not included in the returned list, or
382     *   null to not exclude any ports.
383     *  @return A list of IOPort objects.
384     */
385    public List<IOPort> linkedDestinationPortList(IOPort except) {
386        try {
387            _workspace.getReadAccess();
388
389            // NOTE: The result could be cached for efficiency, but
390            // it would have to be cached in a hashtable indexed by the
391            // except argument.  Probably not worth it.
392            LinkedList<IOPort> resultPorts = new LinkedList<IOPort>();
393            Iterator<?> ports = linkedPortList().iterator();
394
395            while (ports.hasNext()) {
396                IOPort p = (IOPort) ports.next();
397
398                if (p != except) {
399                    if (p.isInsideGroupLinked(this)) {
400                        // Linked from the inside
401                        if (p.isOutput()) {
402                            resultPorts.addLast(p);
403                        }
404                    } else {
405                        if (p.isInput()) {
406                            resultPorts.addLast(p);
407                        }
408                    }
409                }
410            }
411
412            return resultPorts;
413        } finally {
414            _workspace.doneReading();
415        }
416    }
417
418    /** Enumerate the input ports that we are linked to from the outside,
419     *  and the output ports that we are linked to from the inside.
420     *  I.e., enumerate the ports through or to which we could send data.
421     *  This method is deprecated and calls linkedDestinationPortList().
422     *  This method read-synchronizes on the workspace.
423     *  @see ptolemy.kernel.Relation#linkedPorts
424     *  @deprecated Use linkedDestinationPortList() instead.
425     *  @return An enumeration of IOPort objects.
426     */
427    @Deprecated
428    @SuppressWarnings("unchecked")
429    public Enumeration linkedDestinationPorts() {
430        return linkedDestinationPorts(null);
431    }
432
433    /** Enumerate the input ports that we are linked to from the
434     *  outside, and the output ports that we are linked to from
435     *  the inside, except the port given as an argument.
436     *  I.e., enumerate the ports through or to which we could send data.
437     *  This method is deprecated and calls
438     *  linkedDestinationPortList(IOPort).
439     *  This method read-synchronizes on the workspace.
440     *  @see ptolemy.kernel.Relation#linkedPorts(ptolemy.kernel.Port)
441     *  @param except The port not included in the returned Enumeration.
442     *  @deprecated Use linkDestinationPortList(IOPort) instead.
443     *  @return An enumeration of IOPort objects.
444     */
445    @Deprecated
446    @SuppressWarnings("unchecked")
447    public Enumeration linkedDestinationPorts(IOPort except) {
448        return Collections.enumeration(linkedDestinationPortList(except));
449    }
450
451    /** List the output ports that this relation connects to from the
452     *  outside and the input ports that it connects to from
453     *  the inside.
454     *  I.e., list the ports through or from which we
455     *  could receive data. Two ports are connected if they are
456     *  linked to relations in the same relation group.
457     *  This method read-synchronizes on the workspace.
458     *  @see ptolemy.kernel.Relation#linkedPorts
459     *  @return An enumeration of IOPort objects.
460     */
461    public List<IOPort> linkedSourcePortList() {
462        return linkedSourcePortList(null);
463    }
464
465    /** List the output ports that this relation connects to from the
466     *  outside and the input ports that it connects to from
467     *  the inside, except the port given as an argument.
468     *  I.e., list the ports through or from which we
469     *  could receive data. Two ports are connected if they are
470     *  linked to relations in the same relation group.
471     *  This method read-synchronizes on the workspace.
472     *  @see ptolemy.kernel.Relation#linkedPortList(ptolemy.kernel.Port)
473     *  @param except The port not included in the returned list.
474     *  @return A list of IOPort objects.
475     */
476    public List<IOPort> linkedSourcePortList(IOPort except) {
477        try {
478            _workspace.getReadAccess();
479
480            // NOTE: The result could be cached for efficiency, but
481            // it would have to be cached in a hashtable indexed by the
482            // except argument.  Probably not worth it.
483            LinkedList<IOPort> resultPorts = new LinkedList<IOPort>();
484            Iterator<?> ports = linkedPortList().iterator();
485
486            while (ports.hasNext()) {
487                IOPort p = (IOPort) ports.next();
488
489                if (p != except) {
490                    if (p.isInsideGroupLinked(this)) {
491                        // Linked from the inside
492                        if (p.isInput()) {
493                            resultPorts.addLast(p);
494                        }
495                    } else {
496                        if (p.isOutput()) {
497                            resultPorts.addLast(p);
498                        }
499                    }
500                }
501            }
502
503            return resultPorts;
504        } finally {
505            _workspace.doneReading();
506        }
507    }
508
509    /** Enumerate the output ports that we are linked to from the outside
510     *  and the input ports that we are linked to from the inside.
511     *  I.e. enumerate the ports from or through which we might receive
512     *  data. This method is deprecated and calls
513     *  linkedSourcePortList().
514     *  This method read-synchronizes on the workspace.
515     *  @see ptolemy.kernel.Relation#linkedPorts
516     *  @deprecated Use linkedSourcePortList() instead.
517     *  @return An enumeration of IOPort objects.
518     */
519    @Deprecated
520    @SuppressWarnings("unchecked")
521    public Enumeration linkedSourcePorts() {
522        return Collections.enumeration(linkedSourcePortList());
523    }
524
525    /** Enumerate the output ports that we are linked to from the outside
526     *  and the input ports that we are linked to from the inside.
527     *  I.e. enumerate the ports from or through which we might receive
528     *  This method is deprecated and calls
529     *  linkedSourcePortList(IOPort).
530     *  This method read-synchronizes on the workspace.
531     *  @see ptolemy.kernel.Relation#linkedPorts(ptolemy.kernel.Port)
532     *  @param except The port not included in the returned Enumeration.
533     *  @deprecated Use linkedSourcePortList(IOPort) instead.
534     *  @return An enumeration of IOPort objects.
535     */
536    @Deprecated
537    @SuppressWarnings("unchecked")
538    public Enumeration linkedSourcePorts(IOPort except) {
539        return Collections.enumeration(linkedSourcePortList(except));
540    }
541
542    /**
543     * Determine whether for this relation width inference needs to be performed.
544     * @return True when width inference needs to be performed.
545     *  @exception IllegalActionException If the expression for the width cannot
546     *   be parsed or cannot be evaluated, or if the result of evaluation
547     *   violates type constraints, or if the result of evaluation is null
548     *   and there are variables that depend on this one.
549     */
550    public boolean needsWidthInference() throws IllegalActionException {
551        // In case we width has been fixed or the version has not changed since the last width
552        // inference, we of course don't need to do width inference.
553        // In case the version changed it still might happen that we do not have to
554        // width inference, since the change might not be related to or has no influence
555        // on the width of relations. Hence we check with the director who is aware or changes
556        // related to the connectivity.
557        int width = _getUserWidth();
558        boolean widthInferenceValid = width != WIDTH_TO_INFER
559                || _inferredWidthVersion == _workspace.getVersion();
560        if (!widthInferenceValid) {
561            Nameable container = getContainer();
562
563            if (container instanceof CompositeActor) {
564                widthInferenceValid = !((CompositeActor) container)
565                        .needsWidthInference();
566                if (widthInferenceValid) {
567                    _inferredWidthVersion = _workspace.getVersion();
568                }
569            } else {
570                // If we don't have a director or manager we can't determine the inferred width.
571                // You could argue it is wrong to set the _inferredWidth = 0, however
572                // the user can't run the model anyway and hence needs to add a director
573                // to run it, at which time the width inference will be executed again.
574                // If there is no manager but a director it means the user has not run
575                // the model. Since we don't update the version we make sure that the
576                // width is updated when the model is initialized.
577                _inferredWidth = 0;
578                widthInferenceValid = true;
579            }
580        }
581        return !widthInferenceValid;
582    }
583
584    /** Specify the container, adding the relation to the list
585     *  of relations in the container.
586     *  If this relation already has a container, remove it
587     *  from that container first.  Otherwise, remove it from
588     *  the list of objects in the workspace. If the argument is null, then
589     *  unlink the ports from the relation, remove it from
590     *  its container, and add it to the list of objects in the workspace.
591     *  If the relation is already contained by the container, do nothing.
592     *  <p>
593     *  The container must be an
594     *  instance of CompositeActor or null, otherwise an exception is thrown.
595     *  Derived classes may further constrain the class of the container
596     *  to a subclass of CompositeActor.
597     *  <p>
598     *  This method invalidates the schedule and resolved types of the
599     *  director of the container, if there is one.
600     *  <p>
601     *  This method is write-synchronized on the workspace.
602     *
603     *  @param container The proposed container.
604     *  @exception IllegalActionException If the container is not a
605     *   CompositeActor or null, or this entity and the container are not in
606     *   the same workspace.
607     *  @exception NameDuplicationException If the name collides with a name
608     *   already on the relations list of the container.
609     */
610    @Override
611    public void setContainer(CompositeEntity container)
612            throws IllegalActionException, NameDuplicationException {
613        if (!(container instanceof CompositeActor) && container != null) {
614            throw new IllegalActionException(this, container,
615                    "IORelation can only be contained by CompositeActor.");
616        }
617
618        // Invalidate schedule and type resolution of the old container.
619        Nameable oldContainer = getContainer();
620
621        if (oldContainer instanceof CompositeActor) {
622            Director director = ((CompositeActor) oldContainer).getDirector();
623
624            if (director != null) {
625                director.invalidateSchedule();
626                director.invalidateResolvedTypes();
627            }
628        }
629
630        // Invalidate schedule and type resolution of the new container.
631
632        // Either container == null or container instanceof CompositeActor == true
633        if (container != null) {
634            Director director = ((CompositeActor) container).getDirector();
635
636            if (director != null) {
637                director.invalidateSchedule();
638                director.invalidateResolvedTypes();
639            }
640        }
641
642        super.setContainer(container);
643    }
644
645    /** Set the width of this relation and all relations in its
646     *  relation group. The width is the number of
647     *  channels that the relation represents.  If the argument
648     *  is equal to WIDTH_TO_INFER, then the relation becomes a bus with
649     *  unspecified width,
650     *  and the width will be inferred from the way the relation is used
651     *  (but will never be less than zero).
652     *  This method invalidates
653     *  the resolved types on the director of the container, if there is
654     *  one, and notifies each connected actor that its connections
655     *  have changed.
656     *  This method write-synchronizes on the workspace.
657     *
658     *  @param widthValue The width of the relation.
659     *  @exception IllegalActionException If the argument is not zero, one,
660     *   or equal to WIDTH_TO_INFER and the relation is linked to a
661     *   non-multiport. Or when the argument is less than zero and different
662     *   from WIDTH_TO_INFER.
663     *  @see ptolemy.kernel.util.Workspace#getWriteAccess()
664     *  @see #getWidth()
665     */
666    public void setWidth(int widthValue) throws IllegalActionException {
667        width.setToken(new IntToken(widthValue));
668    }
669
670    ///////////////////////////////////////////////////////////////////
671    ////                         public variables                  ////
672
673    /** Indicate that the description(int) method should describe the width
674     *  of the relation, and whether it has been fixed.
675     */
676    public static final int CONFIGURATION = 512;
677
678    /** The value of the width we should infer. */
679    public static final int WIDTH_TO_INFER = -1;
680
681    ///////////////////////////////////////////////////////////////////
682    ////                         protected methods                 ////
683
684    /** Throw an exception if the specified port cannot be linked to this
685     *  relation (is not of class IOPort).
686     *  @param port The candidate port to link to.
687     *  @exception IllegalActionException If the port is not an IOPort.
688     */
689    @Override
690    protected void _checkPort(Port port) throws IllegalActionException {
691        if (!(port instanceof IOPort)) {
692            throw new IllegalActionException(this, port,
693                    "IORelation can only link to a IOPort.");
694        }
695    }
696
697    /** Throw an exception if the specified relation is not an instance
698     *  of IORelation or if it does not have the same width as this relation.
699     *  @param relation The relation to link to.
700     *  @param symmetric If true, the call _checkRelation() on the specified
701     *   relation with this as an argument.
702     *  @exception IllegalActionException If this relation has no container,
703     *   or if this relation is not an acceptable relation for the specified
704     *   relation, or if this relation and the specified relation do not
705     *   have the same width.
706     */
707    @Override
708    protected void _checkRelation(Relation relation, boolean symmetric)
709            throws IllegalActionException {
710        if (!(relation instanceof IORelation)) {
711            throw new IllegalActionException(this, relation,
712                    "IORelation can only link to an IORelation.");
713        }
714
715        int otherWidth = ((IORelation) relation)._getUserWidth();
716        int width = _getUserWidth();
717        if (otherWidth != width) {
718            // If one of both widths equals WIDTH_TO_INFER we set both to the other width
719            //  (which will be different from WIDTH_TO_INFER).
720            if (otherWidth == WIDTH_TO_INFER) {
721                ((IORelation) relation).setWidth(width);
722            } else if (width == WIDTH_TO_INFER) {
723                setWidth(otherWidth);
724            } else {
725                throw new IllegalActionException(this, relation,
726                        "Relations have different widths: " + _getUserWidth()
727                                + " != "
728                                + ((IORelation) relation)._getUserWidth());
729            }
730        }
731
732        super._checkRelation(relation, symmetric);
733    }
734
735    /** Return a description of the object.  The level of detail depends
736     *  on the argument, which is an or-ing of the static final constants
737     *  defined in the NamedObj class and in this class.
738     *  Lines are indented according to
739     *  to the level argument using the protected method _getIndentPrefix().
740     *  Zero, one or two brackets can be specified to surround the returned
741     *  description.  If one is specified it is the the leading bracket.
742     *  This is used by derived classes that will append to the description.
743     *  Those derived classes are responsible for the closing bracket.
744     *  An argument other than 0, 1, or 2 is taken to be equivalent to 0.
745     *  <p>
746     *  If the detail argument sets the bit defined by the constant
747     *  CONFIGURATION, then append to the description is a field
748     *  of the form "configuration {width <i>integer</i> ?fixed?}", where the
749     *  word "fixed" is present if the relation has fixed width, and is
750     *  absent if the relation is a bus with inferred width (isWidthFixed()
751     *  returns false).
752     *
753     *  This method is read-synchronized on the workspace.
754     *
755     *  @param detail The level of detail.
756     *  @param indent The amount of indenting.
757     *  @param bracket The number of surrounding brackets (0, 1, or 2).
758     *  @return A description of the object.
759     *  @exception IllegalActionException If thrown while getting the
760     *  description of subcomponents.
761     */
762    @Override
763    protected String _description(int detail, int indent, int bracket)
764            throws IllegalActionException {
765        try {
766            _workspace.getReadAccess();
767
768            String result;
769
770            if (bracket == 1 || bracket == 2) {
771                result = super._description(detail, indent, 1);
772            } else {
773                result = super._description(detail, indent, 0);
774            }
775
776            if ((detail & CONFIGURATION) != 0) {
777                if (result.trim().length() > 0) {
778                    result += " ";
779                }
780
781                result += "configuration {";
782                result += "width " + getWidth();
783
784                if (isWidthFixed()) {
785                    result += " fixed";
786                }
787
788                result += "}";
789            }
790
791            if (bracket == 2) {
792                result += "}";
793            }
794
795            return result;
796        } finally {
797            _workspace.doneReading();
798        }
799    }
800
801    /**Determines whether width inference should be skipped or not.
802     * @return True when width inference needs to be skipped.
803     */
804    protected boolean _skipWidthInference() {
805        return false;
806    }
807
808    ///////////////////////////////////////////////////////////////////
809    ////                         packaged methods                  ////
810
811    /** Set the inferred width of this relation. The width is the number of
812     *  channels that the relation represents.
813     *  This method is not synchronized on the workspace.
814     *  This packaged method is only meant for the width inference
815     *  algorithm. It should not be used in other circumstances.
816     *  Precondition: you should only infer the width in case it
817            is not set by the user.
818     *  @param width The inferred width of the relation.
819     *  @exception IllegalActionException If the expression for the width cannot
820     *   be parsed or cannot be evaluated, or if the result of evaluation
821     *   violates type constraints, or if the result of evaluation is null
822     *   and there are variables that depend on this one.
823     */
824    void _setInferredWidth(int width) throws IllegalActionException {
825        assert _getUserWidth() == WIDTH_TO_INFER;
826        // Precondition: you should only infer the width in case it
827        // is not set by the user.
828        assert width >= 0;
829        _inferredWidthVersion = _workspace.getVersion();
830        _inferredWidth = width;
831    }
832
833    ///////////////////////////////////////////////////////////////////
834    ////                        private parameters                 ////
835
836    /** A parameter to be able to set the width to Auto to automatically
837     * infer widths. This is an integer that equals WIDTH_TO_INFER.
838     */
839    private Parameter _auto = null;
840
841    ///////////////////////////////////////////////////////////////////
842    ////                         private methods                   ////
843
844    /** Cascade two Receiver arrays to form a new array. For each row, each
845     *  element of the second array is appended behind the elements of the
846     *  first array. This method is solely for deepReceivers.
847     *  The two input arrays must have the same number of rows.
848     * @exception IllegalActionException
849     */
850    private Receiver[][] _cascade(Receiver[][] array1, Receiver[][] array2)
851            throws InvalidStateException, IllegalActionException {
852        if (array1 == null || array1.length <= 0) {
853            return array2;
854        }
855
856        if (array2 == null || array2.length <= 0) {
857            return array1;
858        }
859
860        int width = getWidth();
861        Receiver[][] result = new Receiver[width][0];
862
863        for (int i = 0; i < width; i++) {
864            if (array1[i] == null) {
865                result[i] = array2[i];
866            } else if (array1[i].length <= 0) {
867                result[i] = array2[i];
868            } else if (array2[i] == null) {
869                result[i] = array1[i];
870            } else if (array2[i].length <= 0) {
871                result[i] = array1[i];
872            } else {
873                int m1 = array1[i].length;
874                int m2 = array2[i].length;
875                result[i] = new Receiver[m1 + m2];
876
877                for (int j = 0; j < m1; j++) {
878                    result[i][j] = array1[i][j];
879                }
880
881                for (int j = m1; j < m1 + m2; j++) {
882                    result[i][j] = array2[i][j - m1];
883                }
884            }
885        }
886
887        return result;
888    }
889
890    /** Return the width set by the user
891     *  @return The width set by the user.
892     *  @exception IllegalActionException If the expression for the width cannot
893     *   be parsed or cannot be evaluated, or if the result of evaluation
894     *   violates type constraints, or if the result of evaluation is null
895     *   and there are variables that depend on this one.
896     */
897    private int _getUserWidth() throws IllegalActionException {
898        if (_cachedWidth == -2) {
899            IntToken t = (IntToken) width.getToken();
900
901            if (t != null) {
902                int width = t.intValue();
903                _setWidth(width);
904            }
905        }
906        return _cachedWidth;
907    }
908
909    /** Determine whether widths are currently being inferred or not.
910     *  @return True When widths are currently being inferred.
911     *  @exception IllegalActionException If toplevel not a CompositeActor.
912     */
913    private boolean _inferringWidths() throws IllegalActionException {
914        Nameable container = getContainer();
915
916        if (container instanceof CompositeActor) {
917            return ((CompositeActor) container).inferringWidths();
918        }
919        return false;
920    }
921
922    /** Infer the width of the port from how it is connected.
923     *  Throw a runtime exception if this cannot be done (normally,
924     *  the methods that construct a topology ensure that it can be
925     *  be done).  The returned value is always at least one.
926     *  This method is not read-synchronized on the workspace, so the caller
927     *  should be.
928     *  @return The inferred width.
929     * @exception IllegalActionException
930     */
931    private int _inferWidth() throws IllegalActionException {
932        //The old algorithm for width inference
933        assert !_USE_NEW_WIDTH_INFERENCE_ALGO;
934        long version = _workspace.getVersion();
935
936        if (version != _inferredWidthVersion) {
937            _inferredWidth = 1;
938
939            Iterator<?> ports = linkedPortList().iterator();
940
941            // Note that depending on the order of the ports get iterated,
942            // the inferred width may be different if different ports have
943            // different widths. This is nondeterministic.
944            // However, the model behavior is not affected by this because
945            // the relation with the smallest width along a path decides
946            // the number of signals that can be passed through.
947            while (ports.hasNext()) {
948                IOPort p = (IOPort) ports.next();
949
950                // Infer the width of this port from the linked connections.
951                // Note we assume that this method is only called upon a
952                // multiport.
953                // To guarantee this method successfully infer widths, we have
954                // to check and ensure that there is at most one input relation
955                // or one output relation whose width is not fixed (unknown).
956                // This requirement is conservative. For example, an output
957                // port may have two buses with their widths not fixed.
958                // Furthermore, if one of the buses is connected to an input
959                // port and its width can be determined from the internal
960                // connections associated with that input port, the width of
961                // the other bus can also be resolved. However, to support this,
962                // a fixed-point iteration has to be performed, but there is
963                // no guarantee of existence of a useful fixed-point whose
964                // widths are all non-zero. Therefore, we take the conservative
965                // approach.
966                // To infer the unknown width, we resolve the equation where
967                // the sum of the widths of input relations equals the sum of
968                // those of output relations.
969                int portInsideWidth = 0;
970                int portOutsideWidth = 0;
971                int difference = 0;
972
973                if (p.isInsideGroupLinked(this)) {
974                    // I am linked on the inside...
975                    portInsideWidth = p._getInsideWidth(this);
976                    portOutsideWidth = p._getOutsideWidth(null);
977
978                    // the same as portOutsideWidth = p.getWidth();
979                    difference = portOutsideWidth - portInsideWidth;
980                } else if (p.isLinked(this)) {
981                    // I am linked on the outside...
982                    portInsideWidth = p._getInsideWidth(null);
983                    portOutsideWidth = p._getOutsideWidth(this);
984                    difference = portInsideWidth - portOutsideWidth;
985                }
986
987                if (difference > _inferredWidth) {
988                    _inferredWidth = difference;
989                }
990            }
991
992            _inferredWidthVersion = version;
993        }
994
995        return _inferredWidth;
996    }
997
998    /** Create an initialize the width parameter. */
999    private void _init() {
1000        try {
1001
1002            _auto = new Parameter(this, "Auto");
1003            _auto.setExpression(Integer.toString(WIDTH_TO_INFER));
1004            _auto.setTypeEquals(BaseType.INT);
1005            _auto.setVisibility(Settable.NONE);
1006            _auto.setPersistent(false);
1007
1008            width = new Parameter(this, "width");
1009            width.setExpression("Auto");
1010            width.setTypeEquals(BaseType.INT);
1011
1012        } catch (KernelException ex) {
1013            throw new InternalErrorException(ex);
1014        }
1015    }
1016
1017    /** Set the width of this relation and all relations in its
1018     *  relation group. The width is the number of
1019     *  channels that the relation represents.  If the argument
1020     *  is equal to the value of WIDTH_TO_INFER, then the relation becomes
1021     *  a bus with unspecified width,
1022     *  and the width will be inferred from the way the relation is used
1023     *  (but will never be less than zero).
1024     *  This method invalidates
1025     *  the resolved types on the director of the container, if there is
1026     *  one, and notifies each connected actor that its connections
1027     *  have changed.
1028     *  This method write-synchronizes on the workspace.
1029     *
1030     *  @param width The width of the relation.
1031     *  @exception IllegalActionException If the argument is not zero, one,
1032     *   or equal to WIDTH_TO_INFER and the relation is linked to a
1033     *   non-multiport. Or when the argument is less than zero and different
1034     *   from WIDTH_TO_INFER.
1035     *  @see ptolemy.kernel.util.Workspace#getWriteAccess()
1036     *  @see #getWidth()
1037     */
1038    private void _setWidth(int width) throws IllegalActionException {
1039        if (_USE_NEW_WIDTH_INFERENCE_ALGO) {
1040            if (width == _cachedWidth) {
1041                // No change.
1042                return;
1043            }
1044            try {
1045                _workspace.getWriteAccess();
1046
1047                // Check legitimacy of the change.
1048                if (width < 0 && width != WIDTH_TO_INFER) {
1049                    throw new IllegalActionException(this, "" + width
1050                            + " is not a valid width for this relation.");
1051                }
1052
1053                /* rodiers: I'd rather keep the following exception since it makes the
1054                 * model more consistent, but some tests seem to use this pattern.
1055                 *
1056                // Check for non-multiports on a link: should either be 0, 1 or should be inferred.
1057                 if (width != 1 && width != 0 && width != WIDTH_TO_INFER) {
1058                     for (Object object : linkedPortList()) {
1059                         IOPort p = (IOPort) object;
1060                         if (!p.isMultiport()) {
1061                             throw new IllegalActionException(this, p,
1062                                 "Cannot make bus because the "
1063                                 + "relation is linked to a non-multiport.");
1064                         }
1065                     }
1066                 }
1067                 */
1068
1069                _cachedWidth = width;
1070
1071                // Set the width of all relations in the relation group.
1072                Iterator<?> relations = relationGroupList().iterator();
1073
1074                while (!_suppressWidthPropagation && relations.hasNext()) {
1075                    IORelation relation = (IORelation) relations.next();
1076
1077                    if (relation == this) {
1078                        continue;
1079                    }
1080
1081                    // If the relation has a width parameter, set that
1082                    // value. Otherwise, just set its width directly.
1083                    // Have to disable back propagation.
1084                    try {
1085                        relation._suppressWidthPropagation = true;
1086                        relation.width.setToken(new IntToken(width));
1087                    } finally {
1088                        relation._suppressWidthPropagation = false;
1089                    }
1090                }
1091
1092                // Invalidate schedule and type resolution.
1093                Nameable container = getContainer();
1094
1095                if (container instanceof CompositeActor) {
1096                    ((CompositeActor) container).notifyConnectivityChange();
1097                    Director director = ((CompositeActor) container)
1098                            .getDirector();
1099
1100                    if (director != null) {
1101                        director.invalidateSchedule();
1102                        director.invalidateResolvedTypes();
1103
1104                    }
1105                }
1106
1107                // According to the comments this used to happen for this reason:
1108                //      Do this as a second pass so that it does not
1109                //      get executed if the change is aborted
1110                //      above by an exception.
1111                //      FIXME: Haven't completely dealt with this
1112                //      possibility since the above changes may have
1113                //      partially completed.
1114                // With the new width inference algorithm the code below does not
1115                // need to be executed for this reason. However some actors
1116                // do some initialization in the connectionsChanged method that they
1117                // don't do at other points of time.
1118
1119                for (Object port : linkedPortList()) {
1120                    IOPort p = (IOPort) port;
1121                    Entity portContainer = (Entity) p.getContainer();
1122
1123                    if (portContainer != null) {
1124                        portContainer.connectionsChanged(p);
1125                    }
1126                }
1127            } finally {
1128                // About conditionally executing doneWriting:
1129                //      When the user updates the width we want to increase
1130                //      the workspace version to invalidate widths that are being
1131                //      cached by IOPort.
1132                //      Parameters however have in some sense a strange behavior,
1133                //      when the user sets a Parameter (such as width) this typically
1134                //      happens with setToken, which - in case the Parameter is not
1135                //      lazy - immediately results in the call attributedChanged, which
1136                //      will call _setWidth. However when the project is opened, setExpression
1137                //      is used. In the case the expression in not immediately evaluated, but
1138                //      only when it is necessary. When you call getToken, the expression is evaluated
1139                //      which results in the call of attributedChanged, which again results
1140                //      in the call _setWidth. In this case however the model didn't change this
1141                //      time, but earlier. Typically this happens after opening the model, of creating
1142                //      a new relation. This already increased the version of the model, and hence
1143                //      cached values or refreshed. Triggering of the width then happens when doing
1144                //      width inference. Then we don't want to increase the version number since it would
1145                //      invalidate all widths immediately.
1146                //      Probably this construct works in practise, but it remains a dangerous one...
1147                if (_inferringWidths()) {
1148                    _workspace.doneTemporaryWriting();
1149                } else {
1150                    _workspace.doneWriting();
1151                }
1152            }
1153        } else {
1154            if (width == _cachedWidth) {
1155                // No change.
1156                return;
1157            }
1158            try {
1159                _workspace.getWriteAccess();
1160
1161                if (width <= 0) {
1162                    // Check legitimacy of the change.
1163                    try {
1164                        _inferWidth();
1165                    } catch (InvalidStateException ex) {
1166                        throw new IllegalActionException(this, ex,
1167                                "Cannot use unspecified width on this relation "
1168                                        + "because of its links.");
1169                    }
1170                }
1171
1172                // Check for non-multiports on a link.
1173                /* This is now allowed.
1174                 if (width != 1) {
1175                 Iterator ports = linkedPortList().iterator();
1176
1177                 while (ports.hasNext()) {
1178                 IOPort p = (IOPort) ports.next();
1179
1180                 // Check for non-multiports.
1181                 if (!p.isMultiport()) {
1182                 throw new IllegalActionException(this, p,
1183                 "Cannot make bus because the "
1184                 + "relation is linked to a non-multiport.");
1185                 }
1186                 }
1187                 }
1188                 */
1189                _cachedWidth = width;
1190
1191                // Set the width of all relations in the relation group.
1192                Iterator<?> relations = relationGroupList().iterator();
1193
1194                while (!_suppressWidthPropagation && relations.hasNext()) {
1195                    IORelation relation = (IORelation) relations.next();
1196
1197                    if (relation == this) {
1198                        continue;
1199                    }
1200
1201                    // If the relation has a width parameter, set that
1202                    // value. Otherwise, just set its width directly.
1203                    // Have to disable back propagation.
1204                    try {
1205                        relation._suppressWidthPropagation = true;
1206                        relation.width.setToken(new IntToken(width));
1207                    } finally {
1208                        relation._suppressWidthPropagation = false;
1209                    }
1210                }
1211
1212                // Do this as a second pass so that it does not
1213                // get executed if the change is aborted
1214                // above by an exception.
1215                // FIXME: Haven't completely dealt with this
1216                // possibility since the above changes may have
1217                // partially completed.
1218                Iterator<?> ports = linkedPortList().iterator();
1219
1220                while (ports.hasNext()) {
1221                    IOPort p = (IOPort) ports.next();
1222                    Entity portContainer = (Entity) p.getContainer();
1223
1224                    if (portContainer != null) {
1225                        portContainer.connectionsChanged(p);
1226                    }
1227                }
1228
1229                // Invalidate schedule and type resolution.
1230                Nameable container = getContainer();
1231
1232                if (container instanceof CompositeActor) {
1233                    Director director = ((CompositeActor) container)
1234                            .getDirector();
1235
1236                    if (director != null) {
1237                        director.invalidateSchedule();
1238                        director.invalidateResolvedTypes();
1239                    }
1240                }
1241            } finally {
1242                _workspace.doneWriting();
1243            }
1244        }
1245    }
1246
1247    ///////////////////////////////////////////////////////////////////
1248    ////                         packaged variables                 ////
1249
1250    /** Indicate whether the new or the old width inference algo should be used
1251     *  This is a packaged field.
1252     */
1253    public static final boolean _USE_NEW_WIDTH_INFERENCE_ALGO = true;
1254
1255    ///////////////////////////////////////////////////////////////////
1256    ////                         private variables                 ////
1257
1258    // Cached inferred width.
1259    private transient int _inferredWidth = -1;
1260
1261    // The workspace version for the cached inferred width.
1262    private transient long _inferredWidthVersion = -1;
1263
1264    // Suppress propagation of width changes.
1265    private boolean _suppressWidthPropagation = false;
1266
1267    // The cached value of the width parameter.
1268    private int _cachedWidth = -2;
1269
1270}