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}