001/*
002Below is the copyright agreement for the Ptolemy II system.
003
004Copyright (c) 1995-2014 The Regents of the University of California.
005All rights reserved.
006
007Permission is hereby granted, without written agreement and without
008license or royalty fees, to use, copy, modify, and distribute this
009software and its documentation for any purpose, provided that the above
010copyright notice and the following two paragraphs appear in all copies
011of this software.
012
013IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
014FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
015ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
016THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
017SUCH DAMAGE.
018
019THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
020INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
021MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
022PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
023CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
024ENHANCEMENTS, OR MODIFICATIONS.
025
026Ptolemy II includes the work of others, to see those copyrights, follow
027the copyright link on the splash page or see copyright.htm.
028 */
029package ptolemy.actor.util;
030
031import java.util.ArrayList;
032import java.util.HashSet;
033import java.util.LinkedList;
034import java.util.List;
035import java.util.Set;
036
037import ptolemy.actor.IOPort;
038import ptolemy.actor.TypedIOPort;
039import ptolemy.data.type.BaseType;
040import ptolemy.data.type.MonotonicFunction;
041import ptolemy.data.type.Type;
042import ptolemy.data.type.TypeLattice;
043import ptolemy.graph.InequalityTerm;
044import ptolemy.kernel.util.IllegalActionException;
045
046///////////////////////////////////////////////////////////////////
047//// GLBFunction
048
049/** This class implements a monotonic function that returns the greatest
050 *  lower bound (GLB) of its arguments. These arguments are the port type
051 *  variables of the destination ports of the TypedIOPort passed to the
052 *  constructor. This function is used to define a type constraint asserting
053 *  that the type of the output of this port is greater than or equal to the
054 *  GLB of its destinations.
055 *  <p>
056 *  NOTE: It may seem counterintuitive that the constraint is "greater than
057 *  or equal to" rather than "less than or equal to". But the latter
058 *  constraint is already implied by the connections, since the output port
059 *  type is required to be less than or equal to each destination port type.
060 *  The combination of these constraints has the effect of setting the type
061 *  of the output equal to the GLB of the types of the destination ports.
062 *  This resolved type is, in fact, the most specific type that satisfies the
063 *  constraints of all the downstream ports.
064 * @author Edward A. Lee, Marten Lohstroh
065 * @version $Id: GLBFunction.java$
066 * @since Ptolemy II 10.0
067 * @Pt.ProposedRating Red (eal)
068 * @Pt.AcceptedRating Red (cxh)
069 */
070public class GLBFunction extends MonotonicFunction {
071
072    /** Construct a GLBFunction that finds the greatest lower bound of the
073     *  type variable of the destination ports connected to the TypedIOPort
074     *  that is given as an argument. If the boolean argument inside is true
075     *  then the port is required to be an input port and the arguments for
076     *  this <code>GLBFunction</code> will be the inside destination ports.
077     *  Otherwise, the port is required to be an output port and the arguments
078     *  for this <code>GLBFunction</code> will be the outside destination ports.
079     *
080     *  @param sourcePort The port connected to the ports of which their type
081     *  variables are used to calculate the greatest lower bound.
082     *
083     */
084    public GLBFunction(TypedIOPort sourcePort) {
085        _sourcePort = sourcePort;
086    }
087
088    ///////////////////////////////////////////////////////////////////
089    ////                         public methods                    ////
090
091    /** Return the current value of this monotonic function.
092     *  @return A Type.
093     *  @exception IllegalActionException If thrown while getting the
094     *  value of the cached terms.
095     */
096    @Override
097    public Object getValue() throws IllegalActionException {
098        _updateArguments();
099
100        Set<Type> types = new HashSet<Type>();
101        types.addAll(_cachedTypes);
102        for (InequalityTerm _cachedTerm : _cachedTerms) {
103            Type type = (Type) _cachedTerm.getValue();
104            // if (type != BaseType.UNKNOWN)
105            // enabling this will make the function non-monotonic which may
106            // cause type resolution to diverge
107            types.add(type);
108        }
109        // If there are no destination outputs at all, then set
110        // the output type to unknown.
111        if (types.size() == 0) {
112            return BaseType.UNKNOWN;
113        }
114        // If there is only one destination, the GLB is equal to the
115        // type of that port.
116        if (types.size() == 1) {
117            return types.toArray()[0];
118        }
119
120        return TypeLattice.lattice().greatestLowerBound(types);
121    }
122
123    /** Return the type variables for this function, which are
124     *  the type variables for all the destination ports.
125     *  @return An array of InequalityTerms.
126     */
127    @Override
128    public InequalityTerm[] getVariables() {
129        _updateArguments();
130        return _cachedTerms;
131    }
132
133    /** Return the type constants for this function, which are
134     *  the type constant types for all the destination ports.
135     *  @return An array of Types.
136     */
137    public Type[] getConstants() {
138        _updateArguments();
139        return _cachedTypes.toArray(new Type[_cachedTypes.size()]);
140    }
141
142    ///////////////////////////////////////////////////////////////////
143    ////                         protected methods                 ////
144
145    /** Update the arguments used in <code>getValue()</code>, which are the
146     *  InequalityTerms and Types of the destination ports. The arguments are
147     *  only updated if the workspace version has changed.
148     */
149    protected void _updateArguments() {
150        List<IOPort> destinations = null;
151        if (_sourcePort.getContainer().workspace()
152                .getVersion() == _previousWorkspaceVersion) {
153            return;
154        }
155        ArrayList<InequalityTerm> portTypeTermList = new ArrayList<InequalityTerm>();
156        _cachedTypes = new HashSet<Type>();
157        // Make sure to support ports that are both input and output.
158        if (_sourcePort.isOutput()) {
159            destinations = _sourcePort.sinkPortList();
160        }
161        if (_sourcePort.isInput()) {
162            if (destinations == null) {
163                destinations = _sourcePort.insideSinkPortList();
164            } else {
165                destinations = new LinkedList<IOPort>(destinations);
166                destinations.addAll(_sourcePort.insideSinkPortList());
167            }
168        }
169
170        for (IOPort destination : destinations) {
171            InequalityTerm destinationTypeTerm = ((TypedIOPort) destination)
172                    .getTypeTerm();
173            if (destinationTypeTerm.isSettable()) {
174                portTypeTermList.add(destinationTypeTerm);
175            } else {
176                _cachedTypes.add(((TypedIOPort) destination).getType());
177            }
178        }
179        _cachedTerms = portTypeTermList
180                .toArray(new InequalityTerm[portTypeTermList.size()]);
181        _previousWorkspaceVersion = _sourcePort.getContainer().workspace()
182                .getVersion();
183    }
184
185    /**
186     * Provide a more descriptive string representation.
187     * @return A description of this term.
188     */
189    @Override
190    public String toString() {
191        return "GreatestLowerBound(destinations)";
192    }
193
194    ///////////////////////////////////////////////////////////////////
195    ////                         protected variables               ////
196
197    /** The constant types found in destination ports. */
198    protected Set<Type> _cachedTypes;
199
200    /** The types terms found in destination ports. */
201    protected InequalityTerm[] _cachedTerms;
202
203    /** The workspace version number at time of last update of arguments. */
204    protected long _previousWorkspaceVersion = -1;
205
206    /** The source port. */
207    protected TypedIOPort _sourcePort;
208}