001/* Extract an element 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 java.util.HashSet;
031import java.util.Set;
032
033import ptolemy.actor.parameters.PortParameter;
034import ptolemy.data.ArrayToken;
035import ptolemy.data.BooleanToken;
036import ptolemy.data.IntToken;
037import ptolemy.data.expr.Parameter;
038import ptolemy.data.type.ArrayType;
039import ptolemy.data.type.BaseType;
040import ptolemy.data.type.MonotonicFunction;
041import ptolemy.data.type.Typeable;
042import ptolemy.graph.Inequality;
043import ptolemy.graph.InequalityTerm;
044import ptolemy.kernel.CompositeEntity;
045import ptolemy.kernel.util.IllegalActionException;
046import ptolemy.kernel.util.InternalErrorException;
047import ptolemy.kernel.util.NameDuplicationException;
048import ptolemy.kernel.util.Workspace;
049
050///////////////////////////////////////////////////////////////////
051//// ArrayElement
052
053/**
054 Extract an element from an array.  This actor reads an array from the
055 <i>input</i> port and sends one of its elements to the <i>output</i>
056 port.  The element that is extracted is determined by the
057 <i>index</i> parameter (or port).  It is required that
058 0 &lt;= <i>index</i> &lt; <i>N</i>, where <i>N</i> is the
059 length of the input array, or
060 an exception will be thrown by the fire() method.
061
062 @see LookupTable
063 @see RecordDisassembler
064 @author Edward A. Lee, Elaine Cheong
065 @version $Id$
066 @since Ptolemy II 1.0
067 @Pt.ProposedRating Green (celaine)
068 @Pt.AcceptedRating Green (cxh)
069 */
070public class ArrayElement extends Transformer {
071    /** Construct an actor with the given container and name.
072     *  @param container The container.
073     *  @param name The name of this actor.
074     *  @exception IllegalActionException If the actor cannot be contained
075     *   by the proposed container.
076     *  @exception NameDuplicationException If the container already has an
077     *   actor with this name.
078     */
079    public ArrayElement(CompositeEntity container, String name)
080            throws NameDuplicationException, IllegalActionException {
081        super(container, name);
082
083        // set type constraints.
084        output.setTypeAtLeast(ArrayType.elementType(input));
085
086        // Set parameters.
087        index = new PortParameter(this, "index");
088        index.setTypeEquals(BaseType.INT);
089        index.setExpression("0");
090        new Parameter(index.getPort(), "_showName", BooleanToken.TRUE);
091    }
092
093    ///////////////////////////////////////////////////////////////////
094    ////                         parameters                        ////
095
096    /** The index into the input array.  This is an integer that
097     *  defaults to 0, and is required to be less than or equal to the
098     *  length of the input array. If the port is left unconnected,
099     *  then the parameter value will be used.
100     */
101    public PortParameter index;
102
103    ///////////////////////////////////////////////////////////////////
104    ////                         public methods                    ////
105
106    /** Override the base class to set type constraints.
107     *  @param workspace The workspace for the new object.
108     *  @return A new instance of ArrayElement.
109     *  @exception CloneNotSupportedException If a derived class contains
110     *   an attribute that cannot be cloned.
111     */
112    @Override
113    public Object clone(Workspace workspace) throws CloneNotSupportedException {
114        ArrayElement newObject = (ArrayElement) super.clone(workspace);
115        try {
116            newObject.output
117                    .setTypeAtLeast(ArrayType.elementType(newObject.input));
118        } catch (IllegalActionException e) {
119            // Should have been caught before.
120            throw new InternalErrorException(e);
121        }
122        return newObject;
123    }
124
125    /** Force the type of the input to be an array with fields of a type
126     *  greater than or equal to the type of the output port.
127     */
128    @Override
129    protected Set<Inequality> _customTypeConstraints() {
130        Set<Inequality> result = new HashSet<Inequality>();
131        if (isBackwardTypeInferenceEnabled()) {
132            result.add(new Inequality(new ArrayTypeFunction(output),
133                    input.getTypeTerm()));
134        }
135        return result;
136    }
137
138    /** Consume at most one array from the input port and produce
139     *  one of its elements on the output port.  If there is no token
140     *  on the input, then no output is produced.
141     *  @exception IllegalActionException If the <i>index</i> parameter
142     *   (or port value) is out of range.
143     */
144    @Override
145    public void fire() throws IllegalActionException {
146        super.fire();
147        // NOTE: This has be outside the if because we need to ensure
148        // that if an index token is provided that it is consumed even
149        // if there is no input token.
150        index.update();
151
152        int indexValue = ((IntToken) index.getToken()).intValue();
153
154        if (input.hasToken(0)) {
155            ArrayToken token = (ArrayToken) input.get(0);
156
157            if (indexValue < 0 || indexValue >= token.length()) {
158                throw new IllegalActionException(this,
159                        "index " + indexValue
160                                + " is out of range for the input "
161                                + "array, which has length " + token.length());
162            }
163
164            output.send(0, token.getElement(indexValue));
165        }
166    }
167
168    /** A monotonic function that returns an array type of which the
169     *  fields have the same type as the provided argument.
170     *  @author Marten Lohstroh
171     */
172    private static class ArrayTypeFunction extends MonotonicFunction {
173
174        /** Construct a ArrayElementTypeFunction whose argument is the
175         *  type of the specified object.
176         *  @param typeable A Typeable object.
177         */
178        public ArrayTypeFunction(Typeable typeable) {
179            _typeable = typeable;
180        }
181
182        ///////////////////////////////////////////////////////////////////
183        ////                         public methods                    ////
184
185        /** Return the current value of this monotonic function.
186         *  @return A Type.
187         *  @exception IllegalActionException If the type of the argument
188         *   cannot be determined.
189         */
190        @Override
191        public Object getValue() throws IllegalActionException {
192            return new ArrayType(_typeable.getType());
193        }
194
195        /** Return the type variables for this function, which is
196         *  the type term of the specified typeable, unless it has a constant type,
197         *  in which case return an empty array.
198         *  @return An array of InequalityTerms.
199         */
200        @Override
201        public InequalityTerm[] getVariables() {
202            InequalityTerm term = _typeable.getTypeTerm();
203            if (term.isSettable()) {
204                InequalityTerm[] result = new InequalityTerm[1];
205                result[0] = term;
206                return result;
207            } else {
208                return new InequalityTerm[0];
209            }
210        }
211
212        ///////////////////////////////////////////////////////////////////
213        ////                         private variables                 ////
214
215        /** The argument. */
216        private Typeable _typeable;
217
218    }
219
220}