001/* Read ArrayTokens and send their elements to the output.
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 java.util.HashSet;
031import java.util.Set;
032
033import ptolemy.actor.util.ArrayOfTypesFunction;
034import ptolemy.data.ArrayToken;
035import ptolemy.data.BooleanToken;
036import ptolemy.data.IntToken;
037import ptolemy.data.Token;
038import ptolemy.data.expr.Parameter;
039import ptolemy.data.type.ArrayType;
040import ptolemy.data.type.BaseType;
041import ptolemy.graph.Inequality;
042import ptolemy.kernel.CompositeEntity;
043import ptolemy.kernel.util.Attribute;
044import ptolemy.kernel.util.IllegalActionException;
045import ptolemy.kernel.util.InternalErrorException;
046import ptolemy.kernel.util.NameDuplicationException;
047import ptolemy.kernel.util.Workspace;
048
049///////////////////////////////////////////////////////////////////
050//// ArrayToSequence
051
052/**
053 <p>This actor reads an array at the input and writes the array elements
054 as a sequence to the output. The parameter <i>arrayLength</i> can be
055 used to specify the length of arrays that the actor will accept.
056 If the <i>enforceArrayLength</i> parameter true, then if an input
057 array does not match <i>arrayLength</i>, the fire() method will throw
058 an exception.  This feature is important in domains, such as SDF,
059 that do static scheduling based on production and consumption
060 rates.  For other domains, such as DE and PN, the <i>enforceArrayLength</i>
061 parameter can be set to false, in which case the <i>arrayLength</i>
062 parameter will be ignored.</p>
063 <p>
064 This actor is polymorphic. It can accept ArrayTokens with any element
065 type and send out tokens corresponding to that type.
066 </p>
067
068 @author Yuhong Xiong, Marten Lohstroh
069 @version $Id$
070 @since Ptolemy II 0.4
071 @Pt.ProposedRating Yellow (yuhong)
072 @Pt.AcceptedRating Yellow (neuendor)
073 */
074public class ArrayToSequence extends SDFTransformer {
075    /** Construct an actor with the given container and name.
076     *  @param container The container.
077     *  @param name The name of this actor.
078     *  @exception IllegalActionException If the actor cannot be contained
079     *   by the proposed container.
080     *  @exception NameDuplicationException If the container already has an
081     *   actor with this name.
082     */
083    public ArrayToSequence(CompositeEntity container, String name)
084            throws NameDuplicationException, IllegalActionException {
085        super(container, name);
086
087        // Set type constraints.
088        output.setTypeAtLeast(ArrayType.elementType(input));
089
090        // Set parameters.
091        arrayLength = new Parameter(this, "arrayLength");
092        arrayLength.setExpression("1");
093        enforceArrayLength = new Parameter(this, "enforceArrayLength");
094        enforceArrayLength.setExpression("true");
095        enforceArrayLength.setTypeEquals(BaseType.BOOLEAN);
096
097        output_tokenProductionRate.setExpression("arrayLength");
098
099        // Set the icon.
100        _attachText("_iconDescription",
101                "<svg>\n" + "<polygon points=\"-15,-15 15,15 15,-15 -15,15\" "
102                        + "style=\"fill:white\"/>\n" + "</svg>\n");
103    }
104
105    ///////////////////////////////////////////////////////////////////
106    ////                         parameters                        ////
107
108    /** The size of the input array.  This is an integer that defaults
109     *  to 1.
110     */
111    public Parameter arrayLength;
112
113    /** If true, then enforce the <i>arrayLength</i> parameter by
114     *  throwing an exception if it is violated. This is a boolean
115     *  that defaults to true.
116     */
117    public Parameter enforceArrayLength;
118
119    ///////////////////////////////////////////////////////////////////
120    ////                         public methods                    ////
121
122    /** If the argument is the <i>arrayLength</i> parameter, then
123     *  check to make sure its value is not negative.
124     *  @param attribute The attribute that has changed.
125     *  @exception IllegalActionException If the parameters are out of range.
126     */
127    @Override
128    public void attributeChanged(Attribute attribute)
129            throws IllegalActionException {
130        if (attribute == arrayLength) {
131            int rate = ((IntToken) arrayLength.getToken()).intValue();
132
133            if (rate < 0) {
134                throw new IllegalActionException(this,
135                        "Invalid arrayLength: " + rate);
136            }
137        } else {
138            super.attributeChanged(attribute);
139        }
140    }
141
142    /** Clone the actor into the specified workspace. This calls the
143     *  base class and then creates new ports and parameters.
144     *  @param workspace The workspace for the new object.
145     *  @return A new actor.
146     *  @exception CloneNotSupportedException If a derived class contains
147     *   an attribute that cannot be cloned.
148     */
149    @Override
150    public Object clone(Workspace workspace) throws CloneNotSupportedException {
151        ArrayToSequence newObject = (ArrayToSequence) super.clone(workspace);
152        try {
153            newObject.output
154                    .setTypeAtLeast(ArrayType.elementType(newObject.input));
155        } catch (IllegalActionException e) {
156            throw new InternalErrorException(e);
157        }
158        return newObject;
159    }
160
161    /** Consume the input ArrayToken and produce the outputs.
162     *  @exception IllegalActionException If a runtime type conflict occurs.
163     */
164    @Override
165    public void fire() throws IllegalActionException {
166        super.fire();
167
168        ArrayToken token = (ArrayToken) input.get(0);
169        int rate = ((IntToken) arrayLength.getToken()).intValue();
170        boolean enforce = ((BooleanToken) enforceArrayLength.getToken())
171                .booleanValue();
172
173        if (enforce && token.length() != rate) {
174            throw new IllegalActionException(this,
175                    "The " + "number of elements in the input ArrayToken ("
176                            + token.length()
177                            + ") is not the same as the arrayLength "
178                            + "parameter (" + rate + ").");
179        }
180
181        Token[] elements = token.arrayValue();
182
183        // We no longer send the complete array all at once, since this might in
184        // for example PN lead to larger buffer sizes than strictly necessary.
185        // output.send(0, elements, elements.length);
186
187        for (Token newToken : elements) {
188            output.send(0, newToken);
189        }
190    }
191
192    ///////////////////////////////////////////////////////////////////
193    ////                         protected methods                 ////
194
195    /** Do not establish the usual default type constraints. Instead, the type
196     *  of the output port is constrained to be no less than the type of the
197     *  elements of the input array (set in the constructor of this class).
198     */
199    @Override
200    protected Set<Inequality> _defaultTypeConstraints() {
201        return null;
202    }
203
204    /** Add a type constraint for backward type inference that forces
205     *  the input to be an array of which the elements have a type
206     *  greater than or equal to the output port. If the
207     *  <i>enforceArrayLength</i> parameter is set to true, the input
208     *  is also forced to have a length equal to the <i>arrayLength</i>
209     *  parameter.
210     *  If backward type inference is disabled, this method returns
211     *  an empty set.
212     *  @see ArrayOfTypesFunction
213     *  @return A set of inequalities.
214     */
215    @Override
216    protected Set<Inequality> _customTypeConstraints() {
217        Set<Inequality> result = new HashSet<Inequality>();
218
219        if (isBackwardTypeInferenceEnabled()) {
220            try {
221                // constrain the input to be an array of a type greater
222                // than or equal to the type of the output (for backward
223                // type inference)
224                if (((BooleanToken) enforceArrayLength.getToken())
225                        .booleanValue()) {
226                    result.add(
227                            new Inequality(
228                                    new ArrayOfTypesFunction(output,
229                                            ((IntToken) arrayLength.getToken())
230                                                    .intValue()),
231                                    input.getTypeTerm()));
232                } else {
233                    result.add(new Inequality(new ArrayOfTypesFunction(output),
234                            input.getTypeTerm()));
235                }
236
237            } catch (IllegalActionException e) {
238                // this should not happen
239                e.printStackTrace();
240            }
241        }
242        return result;
243    }
244
245}