001/* An actor to put tokens in order.
002
003 Copyright (c) 1997-2014 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;
029
030import java.util.TreeMap;
031
032import ptolemy.actor.TypedIOPort;
033import ptolemy.data.IntToken;
034import ptolemy.data.Token;
035import ptolemy.data.expr.Parameter;
036import ptolemy.data.type.BaseType;
037import ptolemy.kernel.CompositeEntity;
038import ptolemy.kernel.util.IllegalActionException;
039import ptolemy.kernel.util.NameDuplicationException;
040import ptolemy.kernel.util.Workspace;
041
042///////////////////////////////////////////////////////////////////
043//// Sequencer
044
045/**
046 This actor takes a sequence of inputs tagged with a sequence number
047 and produces them on the output port in the order given by the
048 sequence number.  The sequence numbers are integers starting
049 with zero.  On each firing, this actor consumes one token
050 from the <i>input</i> port and one token from the
051 <i>sequenceNumber</i> port. If the sequence number is the
052 next one in the sequence, then the token read from the <i>input</i>
053 port is produced on the <i>output</i> port.  Otherwise,
054 it is saved until its sequence number is the next one
055 in the sequence.  If an output is produced, then it may
056 be immediately followed by tokens that were previously
057 saved, if their sequence numbers are next.
058
059 @author Edward A. Lee
060 @version $Id$
061 @since Ptolemy II 1.0
062 @Pt.ProposedRating Yellow (eal)
063 @Pt.AcceptedRating Yellow (ctsay)
064 */
065public class Sequencer extends Transformer implements SequenceActor {
066    /** Construct an actor in the specified container with the specified
067     *  name.
068     *  @param container The container.
069     *  @param name The name of this actor within the container.
070     *  @exception IllegalActionException If the actor cannot be contained
071     *   by the proposed container.
072     *  @exception NameDuplicationException If the name coincides with
073     *   an actor already in the container.
074     */
075    public Sequencer(CompositeEntity container, String name)
076            throws IllegalActionException, NameDuplicationException {
077        super(container, name);
078
079        sequenceNumber = new TypedIOPort(this, "sequenceNumber", true, false);
080        sequenceNumber.setTypeEquals(BaseType.INT);
081
082        startingSequenceNumber = new Parameter(this, "startingSequenceNumber");
083        startingSequenceNumber.setExpression("0");
084    }
085
086    ///////////////////////////////////////////////////////////////////
087    ////                     ports and parameters                  ////
088
089    /** Input for the sequence number. The type is int. */
090    public TypedIOPort sequenceNumber;
091
092    /** The first number of the sequence.  This is an int that
093     *  defaults to 0.
094     */
095    public Parameter startingSequenceNumber;
096
097    ///////////////////////////////////////////////////////////////////
098    ////                         public methods                    ////
099
100    /** Clone the actor into the specified workspace.
101     *  @param workspace The workspace for the new object.
102     *  @return A new actor.
103     *  @exception CloneNotSupportedException If a derived class contains
104     *   an attribute that cannot be cloned.
105     */
106    @Override
107    public Object clone(Workspace workspace) throws CloneNotSupportedException {
108        Sequencer newObject = (Sequencer) super.clone(workspace);
109
110        newObject._pending = new TreeMap();
111        return newObject;
112    }
113
114    /** Read a token from the <i>sequenceNumber</i> port and from
115     *  the <i>input</i> port, and output the next token(s) in the
116     *  sequence, or none if the next token in the sequence has not
117     *  yet been seen.  This method will throw a NoTokenException if
118     *  <i>sequenceNumber</i> or <i>input</i> does not have a token.
119     *  @exception IllegalActionException If there is no director.
120     */
121    @Override
122    public void fire() throws IllegalActionException {
123        super.fire();
124        _sequenceNumberOfInput = ((IntToken) sequenceNumber.get(0)).intValue();
125        _nextToken = input.get(0);
126
127        if (_sequenceNumberOfInput == _nextSequenceNumber) {
128            output.send(0, _nextToken);
129            _fireProducedOutput = true;
130        }
131    }
132
133    /** Reset current sequence number to the value given by the
134     *  <i>startingSequenceNumber</i> parameter.
135     *  @exception IllegalActionException If accessing the
136     *   <i>startingSequenceNumber</i> parameter causes an exception.
137     */
138    @Override
139    public void initialize() throws IllegalActionException {
140        super.initialize();
141        _fireProducedOutput = false;
142        _nextSequenceNumber = ((IntToken) startingSequenceNumber.getToken())
143                .intValue();
144        _pending.clear();
145    }
146
147    /** If the fire() method produced the input token then check to
148     *  whether any pending tokens have subsequent sequence numbers.
149     *  @exception IllegalActionException If there is no director.
150     */
151    @Override
152    public boolean postfire() throws IllegalActionException {
153        if (_fireProducedOutput) {
154            _nextSequenceNumber++;
155
156            if (_pending.size() > 0) {
157                Integer nextKey = (Integer) _pending.firstKey();
158                int next = nextKey.intValue();
159
160                while (next == _nextSequenceNumber) {
161                    _nextSequenceNumber++;
162
163                    Token token = (Token) _pending.remove(nextKey);
164                    output.send(0, token);
165
166                    if (_pending.size() == 0) {
167                        break;
168                    }
169
170                    nextKey = (Integer) _pending.firstKey();
171                    next = nextKey.intValue();
172                }
173            }
174
175            _fireProducedOutput = false;
176        } else {
177            _pending.put(Integer.valueOf(_sequenceNumberOfInput), _nextToken);
178        }
179
180        return super.postfire();
181    }
182
183    /** Return false if either the <i>input</i> port or the
184     *  <i>sequenceNumber</i> port lacks an input token.
185     *  Otherwise, return whatever the superclass returns.
186     *  @return False if there are not enough tokens to fire.
187     *  @exception IllegalActionException If there is no director.
188     */
189    @Override
190    public boolean prefire() throws IllegalActionException {
191        _fireProducedOutput = false;
192
193        if (!sequenceNumber.hasToken(0)) {
194            return false;
195        }
196
197        if (!input.hasToken(0)) {
198            return false;
199        }
200
201        return super.prefire();
202    }
203
204    ///////////////////////////////////////////////////////////////////
205    ////                         private variables                 ////
206    // Indicator that an output was produced by the fire() method.
207    private boolean _fireProducedOutput = false;
208
209    // Indicator of the next sequence number for the output.
210    private int _nextSequenceNumber;
211
212    // Token consumed by fire() to be recorded in postfire().
213    private Token _nextToken;
214
215    // The sorted pending data.
216    private TreeMap _pending = new TreeMap();
217
218    // The sequence number of the data read in the fire() method.
219    private int _sequenceNumberOfInput;
220}