001/* Bundle a sequence of N input tokens into an ArrayToken.
002
003 Copyright (c) 1998-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.domains.sdf.lib;
029
030import ptolemy.actor.parameters.PortParameter;
031import ptolemy.actor.util.ActorTypeUtil;
032import ptolemy.actor.util.ArrayElementTypeFunction;
033import ptolemy.data.ArrayToken;
034import ptolemy.data.IntToken;
035import ptolemy.data.Token;
036import ptolemy.kernel.CompositeEntity;
037import ptolemy.kernel.util.Attribute;
038import ptolemy.kernel.util.IllegalActionException;
039import ptolemy.kernel.util.InternalErrorException;
040import ptolemy.kernel.util.NameDuplicationException;
041import ptolemy.kernel.util.Workspace;
042
043///////////////////////////////////////////////////////////////////
044//// SequenceToArray
045
046/**
047 <p>This actor bundles a specified number of input tokens into a single array
048 and broadcasts the resulting array on all output channels.
049 The number of tokens to be bundled is specified by the <i>arrayLength</i>
050 parameter.
051 </p><p>
052 This actor is polymorphic. It can accept inputs of any type, as long
053 as the type does not change, and will produce an array with elements
054 of the corresponding type.
055 </p>
056
057 @author Yuhong Xiong
058 @version $Id$
059 @since Ptolemy II 0.4
060 @Pt.ProposedRating Green (yuhong)
061 @Pt.AcceptedRating Yellow (neuendor)
062 */
063public class SequenceToArray extends SDFTransformer {
064    /** Construct an actor with the given container and name.
065     *  @param container The container.
066     *  @param name The name of this actor.
067     *  @exception IllegalActionException If the actor cannot be contained
068     *   by the proposed container.
069     *  @exception NameDuplicationException If the container already has an
070     *   actor with this name.
071     */
072    public SequenceToArray(CompositeEntity container, String name)
073            throws NameDuplicationException, IllegalActionException {
074        super(container, name);
075
076        input_tokenConsumptionRate.setExpression("arrayLength");
077
078        /* Note that the output ports need not be multiports since all output
079         * channels carry the same data. Using multiports this way is not
080         * recommended, so please do not emulate this design in your own actors.
081         * Changing this actor to the recommended design would break existing
082         * models, so we leave it as is.
083         */
084        output.setMultiport(true);
085        output.setDefaultWidth(1);
086
087        // Set parameters.
088        arrayLength = new PortParameter(this, "arrayLength");
089        arrayLength.setExpression("1");
090
091        // Set the output type to be an ArrayType with element types
092        // at least as general as the input type.
093        output.setTypeAtLeast(ActorTypeUtil.arrayOf(input, arrayLength));
094        // For backward type inference.
095        input.setTypeAtLeast(new ArrayElementTypeFunction(output));
096
097        // Set the icon.
098        _attachText("_iconDescription",
099                "<svg>\n" + "<polygon points=\"-15,-15 15,15 15,-15 -15,15\" "
100                        + "style=\"fill:white\"/>\n" + "</svg>\n");
101    }
102
103    ///////////////////////////////////////////////////////////////////
104    ////                         parameters                        ////
105
106    /** The size of the output array.  This is an integer that defaults
107     *  to 1.
108     */
109    public PortParameter arrayLength;
110
111    ///////////////////////////////////////////////////////////////////
112    ////                         public methods                    ////
113
114    /** Ensure that the arrayLength parameter is not negative.
115     *  @param attribute The attribute that has changed.
116     *  @exception IllegalActionException If the parameters are out of range.
117     */
118    @Override
119    public void attributeChanged(Attribute attribute)
120            throws IllegalActionException {
121        if (attribute == arrayLength) {
122            int rate = ((IntToken) arrayLength.getToken()).intValue();
123
124            if (rate < 0) {
125                throw new IllegalActionException(this,
126                        "Invalid arrayLength: " + rate);
127            }
128        } else {
129            super.attributeChanged(attribute);
130        }
131    }
132
133    /** Clone the actor into the specified workspace. This calls the
134     *  base class and then creates new ports and parameters.
135     *  @param workspace The workspace for the new object.
136     *  @return A new actor.
137     *  @exception CloneNotSupportedException If a derived class contains
138     *   an attribute that cannot be cloned.
139     */
140    @Override
141    public Object clone(Workspace workspace) throws CloneNotSupportedException {
142        SequenceToArray newObject = (SequenceToArray) super.clone(workspace);
143        try {
144            newObject.output.setTypeAtLeast(ActorTypeUtil
145                    .arrayOf(newObject.input, newObject.arrayLength));
146            newObject.input.setTypeAtLeast(
147                    new ArrayElementTypeFunction(newObject.output));
148        } catch (IllegalActionException e) {
149            throw new InternalErrorException(e);
150        }
151        return newObject;
152    }
153
154    /** Consume the inputs and produce the output ArrayToken.
155     *  @exception IllegalActionException If not enough tokens are available.
156     */
157    @Override
158    public void fire() throws IllegalActionException {
159        super.fire();
160
161        int length = ((IntToken) arrayLength.getToken()).intValue();
162
163        Token[] valueArray = new Token[length];
164        System.arraycopy(input.get(0, length), 0, valueArray, 0, length);
165
166        output.broadcast(new ArrayToken(input.getType(), valueArray));
167    }
168
169    /** Return true if the input port has enough tokens for this actor to
170     *  fire. The number of tokens required is determined by the
171     *  value of the <i>arrayLength</i> parameter.
172     *  @return boolean True if there are enough tokens at the input port
173     *   for this actor to fire.
174     *  @exception IllegalActionException If the hasToken() query to the
175     *   input port throws it.
176     *  @see ptolemy.actor.IOPort#hasToken(int, int)
177     */
178    @Override
179    public boolean prefire() throws IllegalActionException {
180        arrayLength.update();
181        int length = ((IntToken) arrayLength.getToken()).intValue();
182
183        if (!input.hasToken(0, length)) {
184            if (_debugging) {
185                _debug("Called prefire(), which returns false.");
186            }
187
188            return false;
189        } else {
190            return super.prefire();
191        }
192    }
193}