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}