001/* An actor that iterates a contained actor over input arrays.
002
003 Copyright (c) 2004-2015 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.actor.lib.hoc;
029
030import java.io.Writer;
031import java.util.ArrayList;
032import java.util.HashSet;
033import java.util.Iterator;
034import java.util.LinkedList;
035import java.util.List;
036import java.util.Set;
037
038import ptolemy.actor.Actor;
039import ptolemy.actor.CompositeActor;
040import ptolemy.actor.Director;
041import ptolemy.actor.Executable;
042import ptolemy.actor.FiringEvent;
043import ptolemy.actor.IOPort;
044import ptolemy.actor.IOPortEvent;
045import ptolemy.actor.NoRoomException;
046import ptolemy.actor.NoTokenException;
047import ptolemy.actor.QueueReceiver;
048import ptolemy.actor.Receiver;
049import ptolemy.actor.TypedCompositeActor;
050import ptolemy.actor.TypedIOPort;
051import ptolemy.actor.util.ArrayElementTypeFunction;
052import ptolemy.actor.util.GLBFunction;
053import ptolemy.data.ArrayToken;
054import ptolemy.data.IntToken;
055import ptolemy.data.Token;
056import ptolemy.data.expr.Variable;
057import ptolemy.data.type.ArrayType;
058import ptolemy.data.type.BaseType;
059import ptolemy.data.type.Type;
060import ptolemy.data.type.TypeLattice;
061import ptolemy.graph.CPO;
062import ptolemy.graph.Inequality;
063import ptolemy.graph.InequalityTerm;
064import ptolemy.kernel.ComponentEntity;
065import ptolemy.kernel.CompositeEntity;
066import ptolemy.kernel.Port;
067import ptolemy.kernel.util.IllegalActionException;
068import ptolemy.kernel.util.InternalErrorException;
069import ptolemy.kernel.util.NameDuplicationException;
070import ptolemy.kernel.util.Nameable;
071import ptolemy.kernel.util.Workspace;
072
073///////////////////////////////////////////////////////////////////
074//// IterateOverArray
075
076/**
077 This actor iterates the contained actor or model over input arrays.
078 To use it, either drop an actor on it and provide arrays to the inputs,
079 or use a default configuration where the actor is effectively a
080 composite actor. In the latter case,
081 you can simply look inside and
082 populate that actor with a submodel that will be applied to the
083 array elements.  The submodel is required to have a director.
084 An SDF director will
085 often be sufficient for operations taken on array elements,
086 but other directors can be used as well.
087 Note that this inside director should not impose a limit
088 on the number of iterations of the inside model. If it does,
089 then that limit will be respected, which may result in a failure
090 to iterate over all the input data.
091 <p>
092 Each input port expects an array. When this actor fires,
093 an array is read on each input port that has one, and its
094 contents are provided sequentially to the contained actor or model.
095 This actor then iterates the contained actor or model until either
096 there are no more input data for the actor or the prefire()
097 method of the actor or model
098 returns false. If postfire() of the actor returns false,
099 then postfire() of this actor will return false, requesting
100 a halt to execution of the model.  The outputs from the
101 contained actor are collected into arrays that are
102 produced on the outputs of this actor.</p>
103 <p>
104 A special variable named "iterationCount" can be used in
105 any expression setting the value of a parameter of this actor
106 or its contents. This variable has an integer value that
107 starts at 1 during the first iteration of the contained
108 actor(s) and is incremented by 1 on each firing. If the
109 inside actors consume one token on each firing, then
110 its final value will be the size of the input array(s).</p>
111 <p>
112 This actor is properly viewed as a "higher-order component" in
113 that its contained actor is a parameter that specifies how to
114 operate on input arrays.  It is inspired by the higher-order
115 functions of functional languages, but unlike those, the
116 contained actor need not be functional. That is, it can have
117 state.</p>
118 <p>
119 Note that you cannot place class definitions inside this
120 actor. There should be no need to because class instances
121 inside it can be instances of classes defined outside of it.</p>
122 <p>
123 This actor (and many of the other higher-order components)
124 has its intellectual roots in the higher-order functions
125 of functional languages, which have been in use since
126 the 1970s. Similar actors were implemented in Ptolemy
127 Classic, and are described in Lee &amp; Parks, "Dataflow
128 Process Networks," <i>Proceedings of the IEEE</i>, 1995.
129 Those were inspired by [2].
130 Alternative approaches are found dataflow visual programming
131 since the beginning (Sutherland in the 1960s, Prograph and
132 Labview in the 1980s), and in time-based visual languages
133 (Simulink in the 1990s).</p>
134 <p>
135 There are a number of known bugs or limitations in this
136 implementation:</p>
137 <ul>
138 <li> FIXME: When you drop in an actor, and then another actor,
139 and then select "undo," the second actor is deleted without
140 the first one being re-created. Thus, undo is only a partial
141 undo.  The fix to this is extremely complicated. Probably the
142 only viable mechanism is to use UndoStackAttribute.getUndoInfo()
143 to get the undo stack and then to manipulate the contents
144 of that stack directly.</li>
145 <li> FIXME: There should be an option to reset between
146 firings of the inside actor.</li>
147 <li> FIXME: If you drop a new actor onto an
148 IterateOverArray in a subclass, it will replace the
149 version inherited from the prototype. This is not right,
150 since it violates the derivation invariant. Any attempt
151 to modify the contained object in the prototype will trigger
152 an exception.  There are two possible fixes. One is to
153 relax the derivation invariant and allow derived objects
154 to not perfectly mirror the hierarchy of the prototype.
155 Another is for this class to somehow refuse to accept
156 the new object in a subclass. But it is not obvious how
157 to do this.</li>
158 <li>
159 FIXME: If an instance of IterateOverArray in a derived class has
160 overridden values of parameters, those are lost if contained
161 entity of the instance in the base class is replaced and
162 then an undo is requested.</li>
163 </ul>
164 <b>References</b>
165 <ol>
166 <li> E. A. Lee and T. M. Parks, "Dataflow Process Networks,"
167 Proceedings of the IEEE, 83(5): 773-801, May, 1995.</li>
168 <li> H. J. Reekie,
169<a href="http://ptolemy.eecs.berkeley.edu/~johnr/papers/thesis.html#in_browser">Realtime Signal Processing: Dataflow, Visual,
170 and Functional Programming</a>," Ph.D. Thesis,
171 University of Technology, Sydney, Sydney, Australia, 1995.</li>
172 </ol>
173
174 @author Edward A. Lee, Steve Neuendorffer
175 @version $Id$
176 @since Ptolemy II 4.1
177 @Pt.ProposedRating Yellow (eal)
178 @Pt.AcceptedRating Red (neuendor)
179 */
180public class IterateOverArray extends MirrorComposite {
181    /** Create an actor with a name and a container.
182     *  The container argument must not be null, or a
183     *  NullPointerException will be thrown.  This actor will use the
184     *  workspace of the container for synchronization and version counts.
185     *  If the name argument is null, then the name is set to the empty string.
186     *  Increment the version of the workspace.
187     *  This actor will have no
188     *  local director initially, and its executive director will be simply
189     *  the director of the container.
190     *  You should set a director before attempting to execute it.
191     *  @param container The container actor.
192     *  @param name The name of this actor.
193     *  @exception IllegalActionException If the container is incompatible
194     *   with this actor.
195     *  @exception NameDuplicationException If the name coincides with
196     *   an actor already in the container.
197     */
198    public IterateOverArray(CompositeEntity container, String name)
199            throws IllegalActionException, NameDuplicationException {
200        super(container, name);
201        _init();
202    }
203
204    /** Construct an IterateOverArray in the specified workspace with
205     *  no container and an empty string as a name. You can then change
206     *  the name with setName(). If the workspace argument is null, then
207     *  use the default workspace.  You should set the local director or
208     *  executive director before attempting to send data to the actor
209     *  or to execute it. Add the actor to the workspace directory.
210     *  Increment the version number of the workspace.
211     *  @param workspace The workspace that will list the actor.
212     *  @exception IllegalActionException If the container is incompatible
213     *   with this actor.
214     *  @exception NameDuplicationException If the name coincides with
215     *   an actor already in the container.
216     */
217    public IterateOverArray(Workspace workspace)
218            throws IllegalActionException, NameDuplicationException {
219        super(workspace);
220        _init();
221    }
222
223    ///////////////////////////////////////////////////////////////////
224    ////                         public methods                    ////
225
226    /** Clone the object into the specified workspace. This overrides
227     *  the base class to instantiate a new IterateDirector and to set
228     *  the association with iterationCount.
229     *  @param workspace The workspace for the new object.
230     *  @return A new NamedObj.
231     *  @exception CloneNotSupportedException If any of the attributes
232     *   cannot be cloned.
233     *  @see #exportMoML(Writer, int, String)
234     */
235    @Override
236    public Object clone(Workspace workspace) throws CloneNotSupportedException {
237        IterateOverArray result = (IterateOverArray) super.clone(workspace);
238        try {
239            // Remove the old inner IterateDirector(s) that is(are) in the wrong workspace.
240            String iterateDirectorName = null;
241            Iterator iterateDirectors = result
242                    .attributeList(IterateDirector.class).iterator();
243            while (iterateDirectors.hasNext()) {
244                IterateDirector oldIterateDirector = (IterateDirector) iterateDirectors
245                        .next();
246                if (iterateDirectorName == null) {
247                    iterateDirectorName = oldIterateDirector.getName();
248                }
249                oldIterateDirector.setContainer(null);
250            }
251
252            // Create a new IterateDirector that is in the right workspace.
253            IterateDirector iterateDirector = result.new IterateDirector(
254                    workspace);
255            iterateDirector.setContainer(result);
256            iterateDirector.setName(iterateDirectorName);
257        } catch (Throwable throwable) {
258            throw new CloneNotSupportedException(
259                    "Could not clone: " + throwable);
260        }
261        result._iterationCount = (Variable) result
262                .getAttribute("iterationCount");
263        return result;
264    }
265
266    /** Override the base class to return a specialized port.
267     *  @param name The name of the port to create.
268     *  @return A new instance of IteratePort, an inner class.
269     *  @exception NameDuplicationException If the container already has a port
270     *  with this name.
271     */
272    @Override
273    public Port newPort(String name) throws NameDuplicationException {
274        try {
275            IteratePort result = new IteratePort(this, name);
276
277            // NOTE: We would like prevent deletion via MoML
278            // (or name changes, for that matter), but the following
279            // also prevents making it an input, which makes
280            // adding ports via the port dialog fail.
281            // result.setDerivedLevel(1);
282            // Force the port to be persistent despite being derived.
283            // result.setPersistent(true);
284            return result;
285        } catch (IllegalActionException ex) {
286            // This exception should not occur, so we throw a runtime
287            // exception.
288            throw new InternalErrorException(this, ex, null);
289        }
290    }
291
292    /** Override the base class to ensure that the input ports of this
293     *  actor all have array types.
294     *  @return A list of instances of Inequality.
295     *  @exception IllegalActionException If the typeConstraints
296     *  of one of the deeply contained objects throws it.
297     *  @see ptolemy.graph.Inequality
298     */
299    @Override
300    public Set<Inequality> typeConstraints() throws IllegalActionException {
301        Iterator ports = inputPortList().iterator();
302
303        while (ports.hasNext()) {
304            TypedIOPort port = (TypedIOPort) ports.next();
305            port.setTypeAtLeast(ArrayType.ARRAY_BOTTOM);
306            // With backward type resolution, we also need the following
307            // to ensure that the type does not resolve to GENERAL.
308            port.setTypeAtMost(new ArrayType(BaseType.GENERAL));
309        }
310
311        return super.typeConstraints();
312    }
313
314    ///////////////////////////////////////////////////////////////////
315    ////                         protected methods                 ////
316
317    /** Check types from a source port to a group of destination ports,
318     *  assuming the source port is connected to all the ports in the
319     *  group of destination ports.  Return a list of instances of
320     *  Inequality that have type conflicts.  This overrides the base
321     *  class so that if one of the ports belongs to this IterateOverArray
322     *  actor, then its element type is compared against the inside port.
323     *  @param sourcePort The source port.
324     *  @param destinationPortList A list of destination ports.
325     *  @return A list of instances of Inequality indicating the
326     *   type constraints that are not satisfied.
327     */
328    @Override
329    protected List _checkTypesFromTo(TypedIOPort sourcePort,
330            List destinationPortList) {
331        List result = new LinkedList();
332
333        boolean isUndeclared = sourcePort.getTypeTerm().isSettable();
334
335        if (!isUndeclared) {
336            // sourcePort has a declared type.
337            Type srcDeclared = sourcePort.getType();
338            Iterator destinationPorts = destinationPortList.iterator();
339
340            while (destinationPorts.hasNext()) {
341                TypedIOPort destinationPort = (TypedIOPort) destinationPorts
342                        .next();
343                isUndeclared = destinationPort.getTypeTerm().isSettable();
344
345                if (!isUndeclared) {
346                    // both source/destination ports are declared,
347                    // check type
348                    Type destinationDeclared = destinationPort.getType();
349
350                    int compare;
351
352                    // If the source port belongs to me, then we want to
353                    // compare its array element type to the type of the
354                    // destination.
355                    if (sourcePort.getContainer() == this
356                            && destinationPort.getContainer() != this) {
357                        // The source port belongs to me, but not the
358                        // destination.
359                        Type srcElementType = ((ArrayType) srcDeclared)
360                                .getElementType();
361                        compare = TypeLattice.compare(srcElementType,
362                                destinationDeclared);
363                    } else if (sourcePort.getContainer() != this
364                            && destinationPort.getContainer() == this) {
365                        // The destination port belongs to me, but not
366                        // the source.
367                        Type destinationElementType = ((ArrayType) destinationDeclared)
368                                .getElementType();
369                        compare = TypeLattice.compare(srcDeclared,
370                                destinationElementType);
371                    } else {
372                        compare = TypeLattice.compare(srcDeclared,
373                                destinationDeclared);
374                    }
375
376                    if (compare == CPO.HIGHER || compare == CPO.INCOMPARABLE) {
377                        Inequality inequality = new Inequality(
378                                sourcePort.getTypeTerm(),
379                                destinationPort.getTypeTerm());
380                        result.add(inequality);
381                    }
382                }
383            }
384        }
385
386        return result;
387    }
388
389    /** Return the type constraints on all connections starting from the
390     *  specified source port to all the ports in a group of destination
391     *  ports. This overrides the base class to ensure that if the source
392     *  port or the destination port is a port of this composite, then
393     *  the port is forced to be an array type and the proper constraint
394     *  on the element type of the array is made. If the source port
395     *  has no possible sources of data, then no type constraints are
396     *  added for it.
397     *  @param sourcePort The source port.
398     *  @return A list of instances of Inequality.
399     */
400    @Override
401    protected List _destinationTypeConstraints(TypedIOPort sourcePort) {
402        Iterator<IOPort> destinationPorts;
403        List<Inequality> result = new LinkedList<Inequality>();
404        boolean srcUndeclared = sourcePort.getTypeTerm().isSettable();
405
406        if (sourcePort.isInput()) {
407            destinationPorts = sourcePort.insideSinkPortList().iterator();
408        } else {
409            destinationPorts = sourcePort.sinkPortList().iterator();
410        }
411
412        while (destinationPorts.hasNext()) {
413            TypedIOPort destinationPort = (TypedIOPort) destinationPorts.next();
414            boolean destUndeclared = destinationPort.getTypeTerm().isSettable();
415
416            if (srcUndeclared || destUndeclared) {
417                // At least one of the source/destination ports does
418                // not have declared type, form type constraint.
419                if (sourcePort.getContainer() == this
420                        && destinationPort.getContainer() == this) {
421                    // Both ports belong to this.
422                    // Require the output to be at least the input.
423                    Inequality ineq1 = new Inequality(sourcePort.getTypeTerm(),
424                            destinationPort.getTypeTerm());
425                    result.add(ineq1);
426
427                    // Finally, if backward type inference is enabled,
428                    // require that the source array element type be greater
429                    // than or equal to the GLB of all the destination ports.
430                    if (isBackwardTypeInferenceEnabled()) {
431                        InequalityTerm typeTerm = sourcePort.getTypeTerm();
432                        if (typeTerm.isSettable()) {
433                            result.add(new Inequality(
434                                    new GLBArrayFunction(sourcePort),
435                                    typeTerm));
436                        }
437                    }
438                } else if (sourcePort.getContainer().equals(this)) {
439                    // The source port belongs to this, so its array element
440                    // type must be less than or equal to the type of the destination
441                    // port.
442                    if (sourcePort.sourcePortList().size() == 0) {
443                        // Skip this port. It is not connected on the outside.
444                        continue;
445                    }
446
447                    // Require the source port to be an array.
448                    Inequality arrayInequality = new Inequality(
449                            ArrayType.ARRAY_BOTTOM, sourcePort.getTypeTerm());
450                    result.add(arrayInequality);
451
452                    // Next require that the element type of the
453                    // source port array be compatible with the
454                    // destination port.
455                    try {
456                        Inequality ineq = new Inequality(
457                                ArrayType.elementType(sourcePort),
458                                destinationPort.getTypeTerm());
459                        result.add(ineq);
460
461                        // Finally, if backward type inference is enabled,
462                        // require that the source array element type be greater
463                        // than or equal to the GLB of all the destination ports.
464                        if (isBackwardTypeInferenceEnabled()) {
465                            InequalityTerm typeTerm = sourcePort.getTypeTerm();
466                            if (typeTerm.isSettable()) {
467                                result.add(new Inequality(
468                                        new GLBArrayFunction(sourcePort),
469                                        typeTerm));
470                            }
471                        }
472                    } catch (IllegalActionException e) {
473                        throw new InternalErrorException(e);
474                    }
475
476                } else if (destinationPort.getContainer().equals(this)) {
477                    // Require that the destination port type be an array
478                    // with elements compatible with the source port.
479                    try {
480                        Inequality ineq = new Inequality(
481                                ArrayType.arrayOf(sourcePort),
482                                destinationPort.getTypeTerm());
483                        result.add(ineq);
484
485                        // Also require that the source port type
486                        // be greater than or equal to the GLB of all
487                        // its destination ports (or array element types
488                        // of destination ports that are ports of this
489                        // IterateOverArray actor).
490                        // This ensures that backward type inference occurs.
491                        // NOTE: We used to do this only if backward type
492                        // inference was enabled globally. But the cost of
493                        // doing this locally is small, and there is no
494                        // mechanism for coercing the type of the inside
495                        // actor, so if we want to be able to coerce the
496                        // type without backward type inference being
497                        // enabled globally, then we need to do this here.
498                        // if (isBackwardTypeInferenceEnabled()) {
499                        InequalityTerm typeTerm = sourcePort.getTypeTerm();
500                        if (typeTerm.isSettable()) {
501                            result.add(
502                                    new Inequality(
503                                            new ArrayElementTypeFunction(
504                                                    destinationPort),
505                                            typeTerm));
506                        }
507                        // }
508                    } catch (IllegalActionException e) {
509                        throw new InternalErrorException(e);
510                    }
511                }
512            }
513        }
514
515        return result;
516    }
517
518    ///////////////////////////////////////////////////////////////////
519    ////                         private variables                 ////
520
521    // Variable that reflects the current iteration count on the
522    // inside.
523    private Variable _iterationCount;
524
525    ///////////////////////////////////////////////////////////////////
526    ////                         private methods                   ////
527
528    /** Initialize the class. */
529    private void _init()
530            throws IllegalActionException, NameDuplicationException {
531        setClassName("ptolemy.actor.lib.hoc.IterateOverArray");
532
533        // Create the IterateDirector in the proper workspace.
534        IterateDirector iterateDirector = new IterateDirector(workspace());
535        iterateDirector.setContainer(this);
536        iterateDirector.setName(uniqueName("IterateDirector"));
537
538        _iterationCount = new Variable(this, "iterationCount", new IntToken(0));
539        _iterationCount.setTypeEquals(BaseType.INT);
540    }
541
542    ///////////////////////////////////////////////////////////////////
543    ////                         inner classes                     ////
544
545    ///////////////////////////////////////////////////////////////////
546    //// GLBArrayFunction
547
548    /** This class implements a monotonic function that returns an array
549     *  type with element type equal to the greatest
550     *  lower bound (GLB) of its arguments, or if any
551     *  of its arguments is itself a port belonging to
552     *  this IterateOverArray actor, then its array element type.
553     */
554    private class GLBArrayFunction extends GLBArrayElementFunction {
555
556        public GLBArrayFunction(TypedIOPort sourcePort) {
557            super(sourcePort);
558        }
559
560        /** Return the current value of this monotonic function.
561         *  @return A Type.
562         */
563        @Override
564        public Object getValue() throws IllegalActionException {
565            Type elementType = (Type) super.getValue();
566            return new ArrayType(elementType);
567        }
568    }
569
570    ///////////////////////////////////////////////////////////////////
571    //// GLBArrayElementFunction
572
573    /** This class implements a monotonic function that returns the greatest
574     *  lower bound (GLB) of its arguments or the array element type
575     *  of its arguments. Specifically, if one of the arguments is a port
576     *  belonging to the enclosing task, then it references the array element
577     *  type of that port rather than the port type.
578     */
579    private class GLBArrayElementFunction extends GLBFunction {
580
581        public GLBArrayElementFunction(TypedIOPort sourcePort) {
582            super(sourcePort);
583        }
584
585        /** Return the current value of this monotonic function.
586         *  @return A Type.
587         */
588        @Override
589        public Object getValue() throws IllegalActionException {
590            _updateArguments();
591
592            Set<Type> types = new HashSet<Type>();
593            types.addAll(_cachedTypes);
594            for (InequalityTerm _cachedTerm : _cachedTerms) {
595                Object termObject = _cachedTerm.getAssociatedObject();
596                if (termObject instanceof IOPort && ((IOPort) termObject)
597                        .getContainer() == IterateOverArray.this) {
598                    // The type term belongs to a port of this IterateOverArray actor.
599                    // Use its element type rather than its type.
600                    Object value = _cachedTerm.getValue();
601                    if (value instanceof ArrayType) {
602                        types.add(((ArrayType) value).getElementType());
603                    } else if (value.equals(BaseType.GENERAL)) {
604                        // To ensure that this function is monotonic, we have to
605                        // handle the case where the value is greater than ArrayType.
606                        // The only thing greater than ArrayType is GENERAL.
607                        types.add(BaseType.GENERAL);
608                    }
609                    // If the value is not an array type, then it must be unknown,
610                    // so we don't need to add it to the collection. Adding unknown
611                    // to the arguments to GLB does nothing.
612                } else {
613                    types.add((Type) _cachedTerm.getValue());
614                }
615            }
616            // If there are no destination outputs at all, then set
617            // the output type to unknown.
618            if (types.size() == 0) {
619                return BaseType.UNKNOWN;
620            }
621            // If there is only one destination, the GLB is equal to the
622            // type of that port.
623            if (types.size() == 1) {
624                return types.toArray()[0];
625            }
626
627            return TypeLattice.lattice().greatestLowerBound(types);
628        }
629    }
630
631    ///////////////////////////////////////////////////////////////////
632    //// IterateComposite
633
634    /** This is a specialized composite actor for use in IterateOverArray.
635     *  In particular, it ensures that if ports are added or deleted
636     *  locally, then corresponding ports will be added or deleted
637     *  in the container.  That addition will result in appropriate
638     *  connections being made.
639     */
640    public static class IterateComposite
641            extends MirrorComposite.MirrorCompositeContents {
642        // NOTE: This has to be a static class so that MoML can
643        // instantiate it.
644
645        /** Construct an actor with a name and a container.
646         *  @param container The container.
647         *  @param name The name of this actor.
648         *  @exception IllegalActionException If the container is incompatible
649         *   with this actor.
650         *  @exception NameDuplicationException If the name coincides with
651         *   an actor already in the container.
652         */
653        public IterateComposite(CompositeEntity container, String name)
654                throws IllegalActionException, NameDuplicationException {
655            super(container, name);
656        }
657
658        /** Override the base class to return a specialized port.
659         *  @param name The name of the port to create.
660         *  @return A new instance of IteratePort, an inner class.
661         *  @exception NameDuplicationException If the container already has
662         *  a port with this name.
663         */
664        @Override
665        public Port newPort(String name) throws NameDuplicationException {
666            try {
667                return new IteratePort(this, name);
668            } catch (IllegalActionException ex) {
669                // This exception should not occur, so we throw a runtime
670                // exception.
671                throw new InternalErrorException(this, ex, null);
672            }
673        }
674    }
675
676    ///////////////////////////////////////////////////////////////////
677    //// IterateDirector
678
679    /** This is a specialized director that fires contained actors
680     *  in the order in which they appear in the actor list repeatedly
681     *  until either there is no more input data for the actor or
682     *  the prefire() method of the actor returns false. If postfire()
683     *  of any actor returns false, then postfire() of this director
684     *  will return false, requesting a halt to execution of the model.
685     */
686    private class IterateDirector extends Director {
687        /** Construct an IterateDirector in the specified workspace with
688         *  no container and an empty string as a name. You can then change
689         *  the name with setName(). If the workspace argument is null, then
690         *  use the default workspace.  You should set the local director or
691         *  executive director before attempting to send data to the actor
692         *  or to execute it. Add the actor to the workspace directory.
693         *  Increment the version number of the workspace.
694         *  @param workspace The workspace that will list the actor.
695         *  @exception IllegalActionException If the container is incompatible
696         *   with this actor.
697         *  @exception NameDuplicationException If the name coincides with
698         *   an actor already in the container.
699         */
700        public IterateDirector(Workspace workspace)
701                throws IllegalActionException, NameDuplicationException {
702            super(workspace);
703            setPersistent(false);
704        }
705
706        /** Invoke iterations on the contained actor of the
707         *  container of this director repeatedly until either it runs out
708         *  of input data or prefire() returns false. If postfire() of the
709         *  actor returns false, then set a flag indicating to postfire() of
710         *  this director to return false.
711         *  @exception IllegalActionException If any called method of
712         *   of the contained actor throws it, or if the contained
713         *   actor is not opaque.
714         */
715        @Override
716        public void fire() throws IllegalActionException {
717            // Don't call "super.fire();" here, this actor contains its
718            // own director.
719            CompositeActor container = (CompositeActor) getContainer();
720            Iterator actors = container.entityList().iterator();
721            _postfireReturns = true;
722
723            while (actors.hasNext() && !_stopRequested) {
724                Actor actor = (Actor) actors.next();
725
726                if (!((ComponentEntity) actor).isOpaque()) {
727                    throw new IllegalActionException(container,
728                            "Inside actor is not opaque "
729                                    + "(perhaps it needs a director).");
730                }
731
732                int result = Executable.COMPLETED;
733                int iterationCount = 0;
734
735                while (result != Executable.NOT_READY) {
736                    iterationCount++;
737                    _iterationCount.setToken(new IntToken(iterationCount));
738
739                    if (_debugging) {
740                        _debug(new FiringEvent(this, actor,
741                                FiringEvent.BEFORE_ITERATE, iterationCount));
742                    }
743
744                    result = actor.iterate(1);
745
746                    if (_debugging) {
747                        _debug(new FiringEvent(this, actor,
748                                FiringEvent.AFTER_ITERATE, iterationCount));
749                    }
750
751                    // Should return if there is no more input data,
752                    // irrespective of return value of prefire() of
753                    // the actor, which is not reliable.
754                    boolean outOfData = true;
755                    Iterator inPorts = actor.inputPortList().iterator();
756
757                    while (inPorts.hasNext()) {
758                        IOPort port = (IOPort) inPorts.next();
759
760                        for (int i = 0; i < port.getWidth(); i++) {
761                            if (port.hasToken(i)) {
762                                outOfData = false;
763                                break;
764                            }
765                        }
766                    }
767
768                    if (outOfData) {
769                        if (_debugging) {
770                            _debug("No more input data for: "
771                                    + ((Nameable) actor).getFullName());
772                        }
773
774                        break;
775                    }
776
777                    if (result == Executable.STOP_ITERATING) {
778                        if (_debugging) {
779                            _debug("Actor requests halt: "
780                                    + ((Nameable) actor).getFullName());
781                        }
782
783                        _postfireReturns = false;
784                        break;
785                    }
786                }
787            }
788        }
789
790        /** Return a new instance of QueueReceiver.
791         *  @return A new instance of QueueReceiver.
792         *  @see QueueReceiver
793         */
794        @Override
795        public Receiver newReceiver() {
796            return new QueueReceiver();
797        }
798
799        /** Override the base class to return the logical AND of
800         *  what the base class postfire() method returns and the
801         *  flag set in fire().  As a result, this will return
802         *  false if any contained actor returned false in its
803         *  postfire() method.
804         */
805        @Override
806        public boolean postfire() throws IllegalActionException {
807            boolean superReturns = super.postfire();
808            return superReturns && _postfireReturns;
809        }
810
811        /** Transfer data from an input port of the
812         *  container to the ports it is connected to on the inside.
813         *  This method extracts tokens from the input array and
814         *  provides them sequentially to the corresponding ports
815         *  of the contained actor.
816         *  @param port The port to transfer tokens from.
817         *  @return True if at least one data token is transferred.
818         *  @exception IllegalActionException Not thrown in this base class.
819         */
820        @Override
821        public boolean transferInputs(IOPort port)
822                throws IllegalActionException {
823            boolean result = false;
824
825            for (int i = 0; i < port.getWidth(); i++) {
826                // NOTE: This is not compatible with certain cases
827                // in PN, where we don't want to block on a port
828                // if nothing is connected to the port on the
829                // inside.
830                try {
831                    if (port.isKnown(i)) {
832                        if (port.hasToken(i)) {
833                            Token t = port.get(i);
834
835                            if (_debugging) {
836                                _debug(getName(), "transferring input from "
837                                        + port.getName());
838                            }
839
840                            ArrayToken arrayToken = (ArrayToken) t;
841
842                            for (int j = 0; j < arrayToken.length(); j++) {
843                                port.sendInside(i, arrayToken.getElement(j));
844                            }
845
846                            result = true;
847                        }
848                    }
849                } catch (NoTokenException ex) {
850                    // this shouldn't happen.
851                    throw new InternalErrorException(this, ex, null);
852                }
853            }
854
855            return result;
856        }
857
858        /** Transfer data from the inside receivers of an output port of the
859         *  container to the ports it is connected to on the outside.
860         *  This method packages the available tokens into a single array.
861         *  @param port The port to transfer tokens from.
862         *  @return True if at least one data token is transferred.
863         *  @exception IllegalActionException Not thrown in this base class.
864         *  @see IOPort#transferOutputs
865         */
866        @Override
867        public boolean transferOutputs(IOPort port)
868                throws IllegalActionException {
869            boolean result = false;
870
871            // Output type might be GENERAL, in which case, we
872            // let the element type be GENERAL.
873            Type elementType = BaseType.GENERAL;
874            Type portType = ((TypedIOPort) port).getType();
875            if (portType instanceof ArrayType) {
876                elementType = ((ArrayType) portType).getElementType();
877            }
878
879            for (int i = 0; i < port.getWidthInside(); i++) {
880                try {
881                    ArrayList list = new ArrayList();
882
883                    while (port.isKnownInside(i) && port.hasNewTokenInside(i)) {
884                        Token t = port.getInside(i);
885                        list.add(t);
886                    }
887
888                    if (list.size() != 0) {
889                        Token[] tokens = (Token[]) list
890                                .toArray(new Token[list.size()]);
891
892                        if (_debugging) {
893                            _debug(getName(),
894                                    "transferring output to " + port.getName());
895                        }
896
897                        port.send(i, new ArrayToken(elementType, tokens));
898                    } else {
899                        // Send an empty list
900                        port.send(i, new ArrayToken(elementType));
901                    }
902
903                    result = true;
904                } catch (NoTokenException ex) {
905                    throw new InternalErrorException(this, ex, null);
906                }
907            }
908
909            return result;
910        }
911
912        //////////////////////////////////////////////////////////////
913        ////                   private variables                  ////
914        // Indicator that at least one actor returned false in postfire.
915        private boolean _postfireReturns = true;
916    }
917
918    ///////////////////////////////////////////////////////////////////
919    //// IteratePort
920
921    /** This is a specialized port for IterateOverArray.
922     *  If the container is an instance of IterateOverArray,
923     *  then it handles type conversions between
924     *  the array types of the ports of the enclosing IterateOverArray
925     *  actor and the scalar types (or arrays with one less dimension)
926     *  of the actor that are contained.  It has a notion of an
927     *  "associated port," and ensures that changes to the status
928     *  of one port (whether it is input, output, or multiport)
929     *  are reflected in the associated port.
930     */
931    public static class IteratePort extends MirrorPort {
932        /** Construct a port in the specified workspace with an empty
933         *  string as a name. You can then change the name with setName().
934         *  If the workspace argument
935         *  is null, then use the default workspace.
936         *  The object is added to the workspace directory.
937         *  Increment the version number of the workspace.
938         *  @param workspace The workspace that will list the port.
939         * @exception IllegalActionException If port parameters cannot be initialized.
940         */
941        public IteratePort(Workspace workspace) throws IllegalActionException {
942            // This constructor is needed for Shallow codgen.
943            super(workspace);
944        }
945
946        // NOTE: This class has to be static because otherwise the
947        // constructor has an extra argument (the first argument,
948        // actually) that is an instance of the enclosing class.
949        // The MoML parser cannot know what the instance of the
950        // enclosing class is, so it would not be able to instantiate
951        // these ports.
952
953        /** Create a new instance of a port for IterateOverArray.
954         *  @param container The container for the port.
955         *  @param name The name of the port.
956         *  @exception IllegalActionException Not thrown in this base class.
957         *  @exception NameDuplicationException Not thrown in this base class.
958         */
959        public IteratePort(TypedCompositeActor container, String name)
960                throws IllegalActionException, NameDuplicationException {
961            super(container, name);
962
963            // NOTE: Ideally, Port are created when an entity is added.
964            // However, there appears to be no clean way to do this.
965            // Instead, ports are added when an entity is added via a
966            // change request registered with this IterateOverArray actor.
967            // Consequently, these ports have to be persistent, and this
968            // constructor and class have to be public.
969            // setPersistent(false);
970        }
971
972        /** Override the base class to convert the token to the element
973         *  type rather than to the type of the port, unless the port
974         *  is of type GENERAL, in which case, no conversion is necessary.
975         *  @param token The token to convert.
976         *  @return The converted token.
977         *  @exception IllegalActionException If the conversion is
978         *   invalid.
979         */
980        @Override
981        public Token convert(Token token) throws IllegalActionException {
982            if (!(getContainer() instanceof IterateOverArray) || !isOutput()) {
983                return super.convert(token);
984            }
985            if (getType().equals(BaseType.GENERAL)) {
986                return token;
987            }
988            Type type = ((ArrayType) getType()).getElementType();
989
990            if (type.equals(token.getType())) {
991                return token;
992            } else {
993                Token newToken = type.convert(token);
994                return newToken;
995            }
996        }
997
998        /** Override the base class to convert the token to the element
999         *  type rather than to the type of the port.
1000         *  @param channelIndex The index of the channel, from 0 to width-1
1001         *  @param token The token to send
1002         *  @exception NoRoomException If there is no room in the receiver.
1003         *  @exception IllegalActionException Not thrown in this base class.
1004         */
1005        @Override
1006        public void sendInside(int channelIndex, Token token)
1007                throws IllegalActionException, NoRoomException {
1008            if (!(getContainer() instanceof IterateOverArray)) {
1009                super.sendInside(channelIndex, token);
1010                return;
1011            }
1012
1013            Receiver[][] farReceivers;
1014
1015            if (_debugging) {
1016                _debug("send inside to channel " + channelIndex + ": " + token);
1017            }
1018
1019            if (_hasPortEventListeners) {
1020                _notifyPortEventListeners(new IOPortEvent(this,
1021                        IOPortEvent.SEND_BEGIN, channelIndex, true, token));
1022            }
1023
1024            try {
1025                try {
1026                    _workspace.getReadAccess();
1027
1028                    ArrayType type = (ArrayType) getType();
1029                    int compare = TypeLattice.compare(token.getType(),
1030                            type.getElementType());
1031
1032                    if (compare == CPO.HIGHER || compare == CPO.INCOMPARABLE) {
1033                        throw new IllegalActionException(
1034                                "Run-time type checking failed. Token type: "
1035                                        + token.getType().toString()
1036                                        + ", port: " + getFullName()
1037                                        + ", port type: "
1038                                        + getType().toString());
1039                    }
1040
1041                    // Note that the getRemoteReceivers() method doesn't throw
1042                    // any non-runtime exception.
1043                    farReceivers = deepGetReceivers();
1044
1045                    if (farReceivers == null
1046                            || farReceivers[channelIndex] == null) {
1047                        return;
1048                    }
1049                } finally {
1050                    _workspace.doneReading();
1051                }
1052
1053                for (int j = 0; j < farReceivers[channelIndex].length; j++) {
1054                    TypedIOPort port = (TypedIOPort) farReceivers[channelIndex][j]
1055                            .getContainer();
1056                    Token newToken = port.convert(token);
1057                    farReceivers[channelIndex][j].put(newToken);
1058                }
1059            } catch (ArrayIndexOutOfBoundsException ex) {
1060                // NOTE: This may occur if the channel index is out of range.
1061                // This is allowed, just do nothing.
1062            } finally {
1063                if (_hasPortEventListeners) {
1064                    _notifyPortEventListeners(new IOPortEvent(this,
1065                            IOPortEvent.SEND_END, channelIndex, true, token));
1066                }
1067            }
1068        }
1069    }
1070}