001/* A type of tokens that contain arbitrary Java objects. 002 003 Copyright (c) 2008-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.type; 030 031import ptolemy.data.ObjectToken; 032import ptolemy.data.Token; 033import ptolemy.graph.CPO; 034import ptolemy.kernel.util.IllegalActionException; 035 036/** 037 038 A type of tokens that contain arbitrary Java objects. An instance of 039 this class specifies the Java class that the contents of the 040 ObjectTokens of that type must be instances of. 041 042 <p>A special type lattice is defined for variants of ObjectType. The 043 top element of the elements in the type lattice is an ObjectType that 044 does not specify any Java class as the class for the contents of its 045 tokens. In the expression language, that element can be referred to 046 with "object" or "object()". Any ObjectToken conforms to this type. A 047 subtype of this type specifies a Java class. In the expression 048 language, such a subtype can be defined with "object(string)", where 049 string is a string (starting and ending with a quote). For example, 050 the following expression refers to a variant of ObjectType to which 051 only ObjectTokens containing atomic actors as their contents conform: 052 <pre>object("ptolemy.actor.AtomicActor")</pre> This ObjectType is a 053 subtype of the most general ObjectType, "object". Furthermore, it is 054 also a subtype of "object(\"ptolemy.kernel.Entity\")", and at the 055 same time a supertype of "object(\"ptolemy.actor.lib.Const\")". 056 057 <p>The bottom element of the type lattice is an artificial ObjectType 058 with {@link BottomClass} as its specified Java class. In Java, the 059 class hierarchy does not form a lattice, so this artificial type is 060 needed to be the greatest lower bound for any two classes if one is 061 not a subclass of the other. 062 063 @author Thomas Huining Feng 064 @version $Id$ 065 @since Ptolemy II 8.0 066 @see ObjectToken 067 @Pt.ProposedRating Red (tfeng) 068 @Pt.AcceptedRating Red (tfeng) 069 */ 070public class ObjectType extends StructuredType implements Cloneable { 071 072 /** Construct an ObjectType with null as the Java class specified in it. 073 * This type is the most general type (top element) among all the 074 * ObjectTypes. 075 */ 076 public ObjectType() { 077 this(null); 078 } 079 080 /** Construct an ObjectType with the given Java class as the class 081 * specified in it. 082 * 083 * @param valueClass The Java class. 084 */ 085 public ObjectType(Class<?> valueClass) { 086 _class = valueClass; 087 } 088 089 /** Construct an ObjectType with the given Java class as the class 090 * specified in it. 091 * 092 * @param value The actual object value, or null if the value is unknown. 093 * @param valueClass The Java class. 094 * @exception IllegalActionException If the <i>value</i> is not 095 * an instance of <i>valueClass</i>. 096 */ 097 public ObjectType(Object value, Class<?> valueClass) 098 throws IllegalActionException { 099 if (value != null && valueClass != null 100 && !valueClass.isInstance(value)) { 101 throw new IllegalActionException("The value " + value + " is not " 102 + "an instance of class " + valueClass); 103 } 104 _value = value; 105 _class = valueClass; 106 } 107 108 /////////////////////////////////////////////////////////////////// 109 //// public methods //// 110 111 /** Return a deep clone of this type. 112 * @return A Type. 113 */ 114 @Override 115 public Object clone() { 116 ObjectType type = new ObjectType(); 117 type._value = _value; 118 type._class = _class; 119 return type; 120 } 121 122 /** Convert the specified token into a token having the type 123 * represented by this object. 124 * @param token a token. 125 * @return a token. 126 * @exception IllegalActionException If lossless conversion 127 * cannot be done. 128 */ 129 @Override 130 public Token convert(Token token) throws IllegalActionException { 131 if (token instanceof ObjectToken) { 132 return token; 133 } 134 throw new IllegalArgumentException( 135 Token.notSupportedConversionMessage(token, this.toString())); 136 } 137 138 /** Determine if the argument represents the same type as this object. 139 * @param object A Type. 140 * @return True if the argument represents the same type as this 141 * object; false otherwise. 142 */ 143 @Override 144 public boolean equals(Object object) { 145 if (!(object instanceof ObjectType)) { 146 return false; 147 } else { 148 Class<?> class1 = _class; 149 Class<?> class2 = ((ObjectType) object)._class; 150 return class1 == class2 || class1 != null && class1.equals(class2); 151 } 152 } 153 154 /** Return the class for tokens that this type represents. The returned 155 * class is always {@link ObjectToken}. 156 * @return The class for tokens that this type represents. 157 */ 158 @Override 159 public Class<?> getTokenClass() { 160 return ObjectToken.class; 161 } 162 163 /** Get the actual value. 164 * 165 * @return The actual value, or null if it is unknown. 166 */ 167 public Object getValue() { 168 return _value; 169 } 170 171 /** Get the Java class specified in this type, of which the contents of 172 * ObjectTokens conforming to this type must be instances. 173 * 174 * @return The Java class specified in this type. 175 */ 176 public Class<?> getValueClass() { 177 return _class; 178 } 179 180 /** Return the hash code for this object. 181 * 182 * @return The hash code. 183 */ 184 @Override 185 public int hashCode() { 186 int hash = 324342; 187 if (_class != null) { 188 hash += _class.hashCode(); 189 } 190 return hash; 191 } 192 193 /** Ignore, as this type does not have elements. 194 * @param type The type to initialize unknown elements to. 195 */ 196 @Override 197 public void initialize(Type type) { 198 } 199 200 /** Return true if this type does not correspond to a single token 201 * class. This occurs if the type is not instantiable, or it 202 * represents either an abstract base class or an interface. 203 * @return True if this type does not correspond to a single token 204 * class. 205 */ 206 @Override 207 public boolean isAbstract() { 208 return _class != null; 209 } 210 211 /** Return true if the specified type is less than or equal to this type. 212 * @param type The type to be checked 213 * @return True if the specified type is less than or equal to this type. 214 */ 215 @Override 216 public boolean isCompatible(Type type) { 217 if (type.equals(BaseType.UNKNOWN)) { 218 return true; 219 } 220 if (!(type instanceof ObjectType)) { 221 return false; 222 } 223 return _isLessThanOrEqualTo((ObjectType) type, this); 224 } 225 226 /** Test if the argument type is compatible with this type. 227 * Compatible is defined as follows: If this type is a constant, the 228 * argument is compatible if it is the same or less than this type in 229 * the type lattice; If this type is a variable, the argument is 230 * compatible if it is a substitution instance of this type. 231 * @return True if the argument is compatible with this type. 232 */ 233 @Override 234 public boolean isConstant() { 235 return true; 236 } 237 238 /** Determine if this Type corresponds to an instantiable token 239 * class. 240 * @return True if this type corresponds to an instantiable 241 * token class. 242 */ 243 @Override 244 public boolean isInstantiable() { 245 return true; 246 } 247 248 /** Return true if the specified type is a substitution instance of this 249 * type. For the argument to be a substitution instance, it must be 250 * either the same as this type, or it must be a type that can be 251 * obtained by replacing the BaseType.UNKNOWN component of this type by 252 * another type. 253 * @param type A Type. 254 * @return True if the argument is a substitution instance of this type. 255 */ 256 @Override 257 public boolean isSubstitutionInstance(Type type) { 258 return equals(type); 259 } 260 261 /** Return a string describing this object. 262 * @return A string of form 'object("classname")'. 263 */ 264 @Override 265 public String toString() { 266 if (_class == null) { 267 return "object(null)"; 268 } else { 269 return "object(\"" + _class.getName() + "\")"; 270 } 271 } 272 273 /////////////////////////////////////////////////////////////////// 274 //// public fields //// 275 276 /** The bottom element among all ObjectTypes. 277 */ 278 public static final ObjectType BOTTOM = new ObjectType(BottomClass.class); 279 280 /** The top element among all ObjectTypes. 281 * The value of this variable is == to the 282 * {@link ptolemy.data.type.BaseType#OBJECT} so that 283 * code generation can use "xxx == BaseType.OBJECT". 284 */ 285 public static final ObjectType TOP = BaseType.OBJECT; 286 287 /** An artificial Java class that serves as the bottom element. 288 * No other class subclasses this, so there is nothing below it. 289 */ 290 public static class BottomClass { 291 } 292 293 /////////////////////////////////////////////////////////////////// 294 //// protected methods //// 295 296 /** Compare this type with the specified type. The specified type 297 * must be of the same structured type, otherwise an exception will 298 * be thrown. 299 * This method returns one of ptolemy.graph.CPO.LOWER, 300 * ptolemy.graph.CPO.SAME, ptolemy.graph.CPO.HIGHER, 301 * ptolemy.graph.CPO.INCOMPARABLE, indicating this type is lower 302 * than, equal to, higher than, or incomparable with the 303 * specified type in the type hierarchy, respectively. 304 * @param type a StructuredType. 305 * @return An integer. 306 * @exception IllegalArgumentException If the specified type is 307 * not the same structured type as this one. 308 */ 309 @Override 310 protected int _compare(StructuredType type) { 311 if (!(type instanceof ObjectType)) { 312 throw new IllegalArgumentException("ObjectType._compare: " 313 + "The argument is not an ObjectType."); 314 } 315 if (this.equals(type)) { 316 return CPO.SAME; 317 } 318 if (_isLessThanOrEqualTo(this, (ObjectType) type)) { 319 return CPO.LOWER; 320 } 321 if (_isLessThanOrEqualTo((ObjectType) type, this)) { 322 return CPO.HIGHER; 323 } 324 return CPO.INCOMPARABLE; 325 } 326 327 /** Return a static instance of this object type. The return 328 * value is used by TypeLattice to represent this type. 329 * @return The bottom object type. 330 */ 331 @Override 332 protected StructuredType _getRepresentative() { 333 return BOTTOM; 334 } 335 336 /** Return the greatest lower bound of this type with the specified 337 * type. The specified type must be of the same structured type, 338 * otherwise an exception will be thrown. 339 * @param type a StructuredType. 340 * @return a StructuredType. 341 * @exception IllegalArgumentException If the specified type is 342 * not the same structured type as this one. 343 */ 344 @Override 345 protected StructuredType _greatestLowerBound(StructuredType type) { 346 if (!(type instanceof ObjectType)) { 347 throw new IllegalArgumentException( 348 "ObjectType._greatestLowerBound: The argument is not an " 349 + "ObjectType."); 350 } 351 352 ObjectType objectType = (ObjectType) type; 353 if (_isLessThanOrEqualTo(this, objectType)) { 354 return this; 355 } else if (_isLessThanOrEqualTo(objectType, this)) { 356 return type; 357 } else { 358 return BOTTOM; 359 } 360 } 361 362 /** Return the least upper bound of this type with the specified 363 * type. The specified type must be of the same structured type, 364 * otherwise an exception will be thrown. 365 * @param type a StructuredType. 366 * @return a StructuredType. 367 * @exception IllegalArgumentException If the specified type is 368 * not the same structured type as this one. 369 */ 370 @Override 371 protected StructuredType _leastUpperBound(StructuredType type) { 372 if (!(type instanceof ObjectType)) { 373 throw new IllegalArgumentException( 374 "ObjectType._leastUpperBound: The argument is not an " 375 + "ObjectType."); 376 } 377 378 ObjectType objectType = (ObjectType) type; 379 if (_isLessThanOrEqualTo(this, objectType)) { 380 return objectType; 381 } else if (_isLessThanOrEqualTo(objectType, this)) { 382 return this; 383 } else { 384 Class<?> class1 = _class; 385 Class<?> class2 = objectType._class; 386 if (class2 != null) { 387 while (class1 != null) { 388 if (class1.isAssignableFrom(class2)) { 389 return new ObjectType(class1); 390 } else { 391 class1 = class1.getSuperclass(); 392 } 393 } 394 } 395 return TOP; 396 } 397 } 398 399 /////////////////////////////////////////////////////////////////// 400 //// private methods //// 401 402 /** Test whether the first type is less than or equal to the second in the 403 * type lattice. 404 * @param t1 The first class. 405 * @param t2 The second class. 406 * @return true if the first class is less than or equal to the second; 407 * false otherwise. 408 */ 409 private boolean _isLessThanOrEqualTo(ObjectType t1, ObjectType t2) { 410 Class<?> class1 = t1._class; 411 Class<?> class2 = t2._class; 412 if (class1 == null && class2 == null) { 413 return true; 414 } else if (class1 == null) { 415 return false; 416 } else if (class2 == null) { 417 return true; 418 } else if (class1.equals(BottomClass.class)) { 419 return true; 420 } else { 421 return class2.isAssignableFrom(class1); 422 } 423 } 424 425 /////////////////////////////////////////////////////////////////// 426 //// private fields //// 427 428 /** The Java class specified in this type. 429 */ 430 private Class<?> _class; 431 432 /** The actual Object, or null if the actual object is unknown. 433 */ 434 private Object _value; 435}