001/* Extract a subarray from an 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.actor.parameters.PortParameter;
031import ptolemy.data.ArrayToken;
032import ptolemy.data.IntToken;
033import ptolemy.data.Token;
034import ptolemy.data.expr.Parameter;
035import ptolemy.data.type.ArrayType;
036import ptolemy.data.type.Type;
037import ptolemy.kernel.CompositeEntity;
038import ptolemy.kernel.util.IllegalActionException;
039import ptolemy.kernel.util.NameDuplicationException;
040import ptolemy.kernel.util.StringAttribute;
041import ptolemy.kernel.util.Workspace;
042
043///////////////////////////////////////////////////////////////////
044//// ArrayExtract
045
046/**
047 Extract a subarray from an array.  This actor reads an array from the
048 <i>input</i> port and sends a subarray to the <i>output</i>
049 port, possibly padded with zeros. The segment of the input array
050 starting at <i>sourcePosition</i> with length <i>extractLength</i> is
051 copied to the output array, starting at <i>destinationPosition</i>.
052 The total length of the output array is <i>outputArrayLength</i>.
053 Any of its entries that are not supplied by the input have value
054 zero (of the same type as the entries in the input array).
055 With the default values of the parameters, only the first element
056 of the input array is copied to the output array, which has length one.
057 The output type is the same as the input type.
058
059 @author Edward A. Lee, Elaine Cheong
060 @version $Id$
061 @since Ptolemy II 1.0
062 @Pt.ProposedRating Green (celaine)
063 @Pt.AcceptedRating Green (cxh)
064 */
065public class ArrayExtract extends Transformer {
066    /** Construct an actor with the given container and name.
067     *  @param container The container.
068     *  @param name The name of this actor.
069     *  @exception IllegalActionException If the actor cannot be contained
070     *   by the proposed container.
071     *  @exception NameDuplicationException If the container already has an
072     *   actor with this name.
073     */
074    public ArrayExtract(CompositeEntity container, String name)
075            throws NameDuplicationException, IllegalActionException {
076        super(container, name);
077
078        // Set type constraints.
079        input.setTypeAtLeast(ArrayType.ARRAY_BOTTOM);
080        output.setTypeAtLeast(input);
081        // FIXME: correct type constraint for length
082        output.setTypeAtLeast(ArrayType.ARRAY_UNSIZED_BOTTOM);
083
084        // Set parameters.
085        sourcePosition = new PortParameter(this, "sourcePosition");
086        sourcePosition.setExpression("0");
087        new StringAttribute(sourcePosition.getPort(), "_cardinal")
088                .setExpression("SOUTH");
089        new Parameter(sourcePosition.getPort(), "_showName")
090                .setExpression("true");
091        extractLength = new PortParameter(this, "extractLength");
092        extractLength.setExpression("1");
093        new StringAttribute(extractLength.getPort(), "_cardinal")
094                .setExpression("SOUTH");
095        new Parameter(extractLength.getPort(), "_showName")
096                .setExpression("true");
097        destinationPosition = new PortParameter(this, "destinationPosition");
098        destinationPosition.setExpression("0");
099        new StringAttribute(destinationPosition.getPort(), "_cardinal")
100                .setExpression("SOUTH");
101        new Parameter(destinationPosition.getPort(), "_showName")
102                .setExpression("true");
103        outputArrayLength = new PortParameter(this, "outputArrayLength");
104        outputArrayLength.setExpression("1");
105        new StringAttribute(outputArrayLength.getPort(), "_cardinal")
106                .setExpression("SOUTH");
107        new Parameter(outputArrayLength.getPort(), "_showName")
108                .setExpression("true");
109    }
110
111    ///////////////////////////////////////////////////////////////////
112    ////                         parameters                        ////
113
114    /** The index into the input array at which to start copying.
115     *  This is a non-negative integer that defaults to 0, and is
116     *  required to be less than the length of the input array.
117     */
118    public PortParameter sourcePosition;
119
120    /** The length of the segment of the input array that is copied
121     *  to the output. This is a non-negative integer that defaults
122     *  to 1. The sum of it and the <i>sourcePosition</i> is
123     *  required to be less than or equal to the length of the input array.
124     */
125    public PortParameter extractLength;
126
127    /** The index into the output array at which to start copying.
128     *  This is a non-negative integer that defaults to 0, and is
129     *  required to be less than the length of the output array.
130     */
131    public PortParameter destinationPosition;
132
133    /** The total length of the output array.
134     *  This is a non-negative integer that defaults to 1.  It is
135     *  required to be at least <i>destinationPosition</i> plus
136     *  <i>extractLength</i>.
137     */
138    public PortParameter outputArrayLength;
139
140    ///////////////////////////////////////////////////////////////////
141    ////                         public methods                    ////
142
143    /** Clone the actor into the specified workspace. This calls the
144     *  base class and then sets up the type constraints.
145     *  @param workspace The workspace for the new object.
146     *  @return A new actor.
147     *  @exception CloneNotSupportedException If a derived class contains
148     *   an attribute that cannot be cloned.
149     */
150    @Override
151    public Object clone(Workspace workspace) throws CloneNotSupportedException {
152        ArrayExtract newObject = (ArrayExtract) super.clone(workspace);
153
154        // Set the type constraints.
155        newObject.input.setTypeAtLeast(ArrayType.ARRAY_BOTTOM);
156        newObject.output.setTypeAtLeast(newObject.input);
157        newObject.output.setTypeAtLeast(ArrayType.ARRAY_UNSIZED_BOTTOM);
158
159        return newObject;
160    }
161
162    /** Consume one array from the input port and send a subarray to
163     *  the output port, padding the subarray with zeros if necessary.
164     *  @exception IllegalActionException If any parameter value
165     *   is out of range.
166     */
167    @Override
168    public void fire() throws IllegalActionException {
169        super.fire();
170        sourcePosition.update();
171        extractLength.update();
172        destinationPosition.update();
173        outputArrayLength.update();
174
175        if (input.hasToken(0)) {
176            ArrayToken inputValue = (ArrayToken) input.get(0);
177            Type inputElementType = inputValue.getElementType();
178            Token[] inputArray = inputValue.arrayValue();
179            int sourcePositionValue = ((IntToken) sourcePosition.getToken())
180                    .intValue();
181            int extractLengthValue = ((IntToken) extractLength.getToken())
182                    .intValue();
183            int destinationPositionValue = ((IntToken) destinationPosition
184                    .getToken()).intValue();
185            int outputArrayLengthValue = ((IntToken) outputArrayLength
186                    .getToken()).intValue();
187
188            try {
189                Token[] outputArray = new Token[outputArrayLengthValue];
190
191                Token zero = inputArray[0].zero();
192
193                for (int i = 0; i < destinationPositionValue; i++) {
194                    outputArray[i] = zero;
195                }
196                int j = sourcePositionValue;
197                for (int i = destinationPositionValue; i < destinationPositionValue
198                        + extractLengthValue; i++) {
199                    outputArray[i] = inputValue.getElement(j++);
200                }
201                for (int i = destinationPositionValue
202                        + extractLengthValue; i < outputArrayLengthValue; i++) {
203                    outputArray[i] = zero;
204                }
205
206                output.send(0, new ArrayToken(inputElementType, outputArray));
207            } catch (IndexOutOfBoundsException ex) {
208                throw new IllegalActionException(this,
209                        "Parameter values out of range for the array supplied."
210                                + "inputArray has length " + inputArray.length);
211            }
212        }
213    }
214}