001/* A token that contains a reference to an arbitrary object.
002
003 Copyright (c) 1997-2015 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
028 */
029package ptolemy.data;
030
031import ptolemy.data.type.BaseType;
032import ptolemy.data.type.ObjectType;
033import ptolemy.data.type.Type;
034import ptolemy.data.type.TypeLattice;
035import ptolemy.graph.CPO;
036import ptolemy.kernel.util.IllegalActionException;
037import ptolemy.kernel.util.InternalErrorException;
038
039///////////////////////////////////////////////////////////////////
040//// ObjectToken
041
042/**
043 A token that contains a reference to an arbitrary object.
044 Note that when this token constructed, the object passed to the constructor
045 is not cloned. Thus, care must be exercised to ensure that actors do
046 not modify that object in a nondeterministic way, unless such nondeterminism
047 is acceptable.
048
049 @author Edward A. Lee
050 @version $Id$
051 @since Ptolemy II 0.2
052 @Pt.ProposedRating Yellow (yuhong)
053 @Pt.AcceptedRating Yellow (wbwu)
054 */
055public class ObjectToken extends Token {
056
057    /** Construct an empty token.
058     */
059    public ObjectToken() {
060    }
061
062    /** Construct a token with a reference to the specified object.
063     *  @param value The specified object referred to by this token.
064     *  @exception IllegalActionException If the argument is not of
065     *  the appropriate type (may be thrown by derived classes, but is
066     *  not thrown here).
067     */
068    public ObjectToken(Object value) throws IllegalActionException {
069        _value = value;
070        _class = null;
071    }
072
073    /** Construct a token with the given value and the given class as the
074     *  value's type.
075     *
076     *  @param value The value.
077     *  @param valueClass The class of the value.
078     *  @exception IllegalActionException If the argument is not of
079     *  the appropriate type.
080     */
081    public ObjectToken(Object value, Class<?> valueClass)
082            throws IllegalActionException {
083        if (value != null && valueClass != null
084                && !valueClass.isInstance(value)) {
085            throw new IllegalActionException("The value " + value + " is not "
086                    + "an instance of class " + valueClass);
087        }
088        _value = value;
089        _class = valueClass;
090    }
091
092    /** Convert the specified token into an instance of ObjectToken.
093     *  This method does lossless conversion.
094     *  If the argument is already an instance of ObjectToken,
095     *  it is returned without any change. Otherwise, if the argument
096     *  is below ObjectToken in the type hierarchy, it is converted to
097     *  an instance of ObjectToken or one of the subclasses of
098     *  ObjectToken and returned. If none of the above condition is
099     *  met, an exception is thrown.
100     *  @param token The token to be converted to an ObjectToken.
101     *  @return An ObjectToken.
102     *  @exception IllegalActionException If the conversion
103     *   cannot be carried out.
104     */
105    public static ObjectToken convert(Token token)
106            throws IllegalActionException {
107        if (token instanceof ObjectToken) {
108            return (ObjectToken) token;
109        }
110
111        throw new IllegalActionException(
112                notSupportedConversionMessage(token, "object"));
113    }
114
115    ///////////////////////////////////////////////////////////////////
116    ////                         public methods                    ////
117
118    /** Return true if the argument is an instance of ObjectToken and its
119     *  contained object is equal to the object contained in this token,
120     *  as tested by the equals() method of the contained object.
121     *  @param object An instance of Object.
122     *  @return True if the argument is an instance of ObjectToken and its
123     *   contained object is equal to the object contained in this token.
124     */
125    @Override
126    public boolean equals(Object object) {
127        if (object == null) {
128            return false;
129        }
130        // This test rules out subclasses.
131        if (object.getClass() != getClass()) {
132            return false;
133        }
134
135        ObjectToken objectToken = (ObjectToken) object;
136        if (_class == null && objectToken._class == null
137                || _class != null && _class.equals(objectToken._class)) {
138            return _value == null && objectToken._value == null
139                    || _value != null && _value.equals(objectToken._value);
140        } else {
141            return false;
142        }
143    }
144
145    /** Return the type of this token.
146     *  @return BaseType.OBJECT
147     */
148    @Override
149    public Type getType() {
150        if (_class == null) {
151            return BaseType.OBJECT;
152        } else {
153            try {
154                return new ObjectType(_value, _class);
155            } catch (IllegalActionException e) {
156                throw new InternalErrorException("This ObjectToken does not "
157                        + "contain a value that conforms to the specified "
158                        + "class.");
159            }
160        }
161    }
162
163    /** Return the value of the token, a reference to an object.
164     *  @return The Object in this token.
165     */
166    public Object getValue() {
167        return _value;
168    }
169
170    /** Return the class of the object contained in this token.
171     *  @return The class of the object.
172     */
173    public Class getValueClass() {
174        return _class;
175    }
176
177    /** Return a hash code value for this token. This method returns the
178     *  hash code of the contained object.
179     *  @return A hash code value for this token.
180     */
181    @Override
182    public int hashCode() {
183        if (_value == null && _class == null) {
184            return 0;
185        } else if (_value == null) {
186            return _class.hashCode();
187        } else if (_class == null) {
188            return _value.hashCode();
189        } else {
190            return _value.hashCode() + _class.hashCode();
191        }
192    }
193
194    /** Test that the value of this token is close to the first argument,
195     *  where "close" means that the distance between them is less than
196     *  or equal to the second argument.  This method only makes sense
197     *  for tokens where the distance between them is reasonably
198     *  represented as a double. If the argument token is not of
199     *  the same type as this token, then either this token or the
200     *  argument will be converted, if possible, to the type of the other.
201     *  <p>
202     *  Subclasses should not
203     *  generally override this method, but override the protected
204     *  _isCloseTo() method to ensure that type conversion is performed
205     *  consistently.
206     *  @param token The token to test closeness of this token with.
207     *  @param epsilon The value that we use to determine whether two
208     *   tokens are close.  Ignored in this class.
209     *  @return A boolean token that contains the value true if the
210     *   value and units of this token are close to those of the
211     *   argument token.
212     *  @exception IllegalActionException If the argument token and
213     *   this token are of incomparable types, or the operation does
214     *   not make sense for the given types.
215     */
216    @Override
217    public final BooleanToken isCloseTo(Token token, double epsilon)
218            throws IllegalActionException {
219        // FIXME: This is copied from AbstractConvertibleToken.
220
221        // Note that if we had absolute(), subtraction() and islessThan()
222        // we could perhaps define this method for all tokens.
223        int typeInfo = TypeLattice.compare(getType(), token);
224
225        if (typeInfo == CPO.SAME) {
226            return _isCloseTo(token, epsilon);
227        } else if (typeInfo == CPO.HIGHER) {
228            AbstractConvertibleToken convertedArgument = (AbstractConvertibleToken) getType()
229                    .convert(token);
230
231            try {
232                BooleanToken result = _isCloseTo(convertedArgument, epsilon);
233                return result;
234            } catch (IllegalActionException ex) {
235                // If the type-specific operation fails, then create a
236                // better error message that has the types of the
237                // arguments that were passed in.
238                throw new IllegalActionException(null, ex,
239                        notSupportedMessage("isCloseTo", this, token));
240            }
241        } else if (typeInfo == CPO.LOWER) {
242            return token.isCloseTo(this, epsilon);
243        } else {
244            throw new IllegalActionException(
245                    notSupportedIncomparableMessage("isCloseTo", this, token));
246        }
247    }
248
249    /** Compare this ObjectToken to the given argument, and return true if the
250     *  values contained in the two are the same Java object.
251     *
252     *  @param rightArgument The argument.
253     *  @return true if the values are the same Java object, or false otherwise.
254     */
255    @Override
256    public BooleanToken isEqualTo(Token rightArgument) {
257        if (rightArgument instanceof ObjectToken
258                && ((ObjectToken) rightArgument)._value == _value) {
259            return BooleanToken.TRUE;
260        } else {
261            return BooleanToken.FALSE;
262        }
263    }
264
265    /** Return an ObjectToken with value null and class specified by the
266     *  className argument.
267     *
268     *  @param className The className.
269     *  @return The ObjectToken.
270     *  @exception IllegalActionException If the class with className as its name
271     *   cannot be loaded.
272     */
273    public static ObjectToken object(String className)
274            throws IllegalActionException {
275        try {
276            Class objectClass = Class.forName(className);
277            return new ObjectToken(null, objectClass);
278        } catch (ClassNotFoundException e) {
279            throw new IllegalActionException(null, e,
280                    "Unable to load class " + className);
281        }
282    }
283
284    /** Return the value of this token as a string.
285     *  The returned syntax looks like a function call to a one argument method
286     *  named "object".  The argument is the string representation of the
287     *  contained object, or the string "null" if the object is null.  Notice
288     *  that this syntax is not currently parsable by the expression language.
289     *  @return A String representing the object.
290     */
291    @Override
292    public String toString() {
293        String value = _value == null ? "null" : _value.toString();
294        // String clazz = _class == null ? "" : ": " + _class.getName();
295        return "object(" + value + ")";
296    }
297
298    /** A new empty ObjectToken. */
299    public static final ObjectToken NULL = new ObjectToken();
300
301    ///////////////////////////////////////////////////////////////////
302    ////                         protected methods                 ////
303
304    /** Test for closeness of the values of this Token and the argument
305     *  Token.  It is assumed that the type of the argument is
306     *  an ObjectToken.
307     *  @param rightArgument The token to add to this token.
308     *  @param epsilon The value that we use to determine whether two
309     *  tokens are close.  This parameter is ignored by this class.
310     *  @return A BooleanToken containing the result.
311     *  @exception IllegalActionException If this method is not
312     *  supported by the derived class.
313     */
314    protected BooleanToken _isCloseTo(Token rightArgument, double epsilon)
315            throws IllegalActionException {
316        return _isEqualTo(rightArgument);
317    }
318
319    /** Test for equality of the values of this Token and the argument
320     *  Token.  It is assumed that the type of the argument is
321     *  ObjectToken.
322     *  @param rightArgument The token to add to this token.
323     *  @return A BooleanToken containing the result.
324     *  @exception IllegalActionException If this method is not
325     *  supported by the derived class.
326     */
327    protected BooleanToken _isEqualTo(Token rightArgument)
328            throws IllegalActionException {
329        ObjectToken convertedArgument = (ObjectToken) rightArgument;
330        return BooleanToken.getInstance(
331                toString().compareTo(convertedArgument.toString()) == 0);
332    }
333
334    ///////////////////////////////////////////////////////////////////
335    ////                         protected variables               ////
336
337    /** The class of the object.
338     */
339    protected Class _class = null;
340
341    /** The actual Object.
342     *  This is protected to allow access in derived classes only.
343     */
344    protected Object _value = null;
345}