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}