001/* Append arrays together to form a larger array.
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.actor.lib;
029
030import ptolemy.data.ArrayToken;
031import ptolemy.data.Token;
032import ptolemy.data.type.ArrayType;
033import ptolemy.data.type.Type;
034import ptolemy.kernel.CompositeEntity;
035import ptolemy.kernel.util.IllegalActionException;
036import ptolemy.kernel.util.NameDuplicationException;
037import ptolemy.kernel.util.Workspace;
038
039///////////////////////////////////////////////////////////////////
040//// ArrayAppend
041
042/**
043 An actor that appends ArrayTokens together.  This actor has a single input
044 multiport, and a single output port.  The types on the input and the output
045 port must both be the same array type.  During each firing, this actor reads
046 up to one ArrayToken from each channel of the input port and creates an
047 ArrayToken of the same type on the output port.  If no token is available on
048 a particular channel, then there will be no contribution to the output.
049 The output is an array of length equal to the sum of the lengths of
050 the input arrays (which may be zero if either there are no input
051 arrays or the lengths of the input arrays are all zero).
052
053 @author Steve Neuendorffer
054 @version $Id$
055 @since Ptolemy II 1.0
056 @Pt.ProposedRating Green (celaine)
057 @Pt.AcceptedRating Green (cxh)
058 */
059public class ArrayAppend extends Transformer {
060    /** Construct an actor with the given container and name.
061     *  @param container The container.
062     *  @param name The name of this actor.
063     *  @exception IllegalActionException If the actor cannot be contained
064     *   by the proposed container.
065     *  @exception NameDuplicationException If the container already has an
066     *   actor with this name.
067     */
068    public ArrayAppend(CompositeEntity container, String name)
069            throws NameDuplicationException, IllegalActionException {
070        super(container, name);
071
072        // The input is a multiport.
073        input.setMultiport(true);
074
075        // Set type constraints.
076        input.setTypeAtLeast(ArrayType.ARRAY_BOTTOM);
077        output.setTypeAtLeast(input);
078        output.setTypeAtLeast(ArrayType.ARRAY_UNSIZED_BOTTOM);
079    }
080
081    ///////////////////////////////////////////////////////////////////
082    ////                         public methods                    ////
083
084    /** Clone the actor into the specified workspace. This calls the
085     *  base class and then creates new ports and parameters.
086     *  @param workspace The workspace for the new object.
087     *  @return A new actor.
088     *  @exception CloneNotSupportedException If a derived class contains
089     *   an attribute that cannot be cloned.
090     */
091    @Override
092    public Object clone(Workspace workspace) throws CloneNotSupportedException {
093        ArrayAppend newObject = (ArrayAppend) super.clone(workspace);
094
095        // Set the type constraints.
096        newObject.input.setTypeAtLeast(ArrayType.ARRAY_BOTTOM);
097        newObject.output.setTypeAtLeast(newObject.input);
098        newObject.output.setTypeAtLeast(ArrayType.ARRAY_UNSIZED_BOTTOM);
099        return newObject;
100    }
101
102    /** Consume at most one ArrayToken from each channel of the input port
103     *  and produce a single ArrayToken on the output
104     *  port that contains all of the tokens contained in all of the
105     *  arrays read from the input. If all input arrays are empty,
106     *  or if there are no input arrays, then output an empty array
107     *  of the appropriate type.
108     *  @exception IllegalActionException If a runtime type conflict occurs,
109     *   or if there are no input channels.
110     */
111    @Override
112    public void fire() throws IllegalActionException {
113        super.fire();
114        int width = input.getWidth();
115        if (width == 0) {
116            throw new IllegalActionException(this, "No input channels.");
117        }
118        // NOTE: Do not use System.arraycopy here because the
119        // arrays being appended may be subclasses of ArrayToken,
120        // so values have to be accessed via the getElement() method,
121        // which is overridden in the subclasses. Use the append()
122        // method of ArrayToken instead.
123        ArrayToken[] arraysToAppend = new ArrayToken[width];
124        int resultWidth = 0;
125        for (int i = 0; i < width; i++) {
126            if (input.hasToken(i)) {
127                Token token = input.get(i);
128                try {
129                    ArrayToken arrayToken = (ArrayToken) token;
130                    arraysToAppend[i] = arrayToken;
131                    resultWidth += arrayToken.length();
132                } catch (ClassCastException ex) {
133                    throw new IllegalActionException(this, ex,
134                            "Cannot cast \"" + token + "\" to an ArrayToken");
135                }
136            }
137        }
138        if (resultWidth > 0) {
139            output.send(0, ArrayToken.append(arraysToAppend));
140        } else {
141            Type elementType = ((ArrayType) input.getType()).getElementType();
142            output.send(0, new ArrayToken(elementType));
143        }
144    }
145}