001/* Base class for data capsules.
002
003 Copyright (c) 1997-2014 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 Added pow() method for integer exponentiation.
028 Don't use to represent pure events.
029 */
030package ptolemy.data;
031
032import ptolemy.data.type.BaseType;
033import ptolemy.data.type.Type;
034import ptolemy.kernel.util.IllegalActionException;
035
036///////////////////////////////////////////////////////////////////
037//// Token
038
039/**
040 Token is the base class for data capsules.  Tokens are immutable,
041 meaning that their value cannot change after construction.  They have
042 a set of polymorphic methods providing a set of basic arithmetic and
043 logical operations.  Generally, derived classes should override the
044 methods to implement type specific operations that make sense for a
045 given type.  For operations that are non-sensical for a given type,
046 such as division of matrices, the implementation of this base class
047 can be used, which simply throws an exception.
048
049 <p> Generally, it is painful to implement both the operation and
050 operationReverse methods of this class.  It is also painful to
051 implement tokens that are automatically converted to other tokens in a
052 consistent fashion.  As such, there are several subclasses of this
053 class that implement these methods and provide a somewhat nicer
054 abstraction.  The ScalarToken derived class is useful for many types
055 that are losslessly convertible to other types and may be associated
056 with units, such as IntToken.  The MatrixToken derived class is useful
057 for implementing matrices of ScalarTokens, such as IntMatrixToken.
058 The AbstractNotConvertible derived class is useful for implementing
059 tokens that are not losslessly convertible to a token in implemented
060 in another class, such as ArrayToken.  Lastly,
061 AbstractConvertibleToken is useful for implementing tokens that are
062 losslessly convertible to a token in another class, but don't have
063 units, such as BooleanToken.
064
065 <p> Instances of this base class *should not* be used to represent
066 pure events, i.e., to indicate that an event is present. To represent
067 pure events, it is better to use the EventToken class.  The reasoning
068 is that the type BaseType.GENERAL is reserved to represent types which
069 the type system cannot represent exactly.  Using the EventToken class,
070 and the type BaseType.EVENT allows typesafe use of pure events.
071
072 <p>Nil, null or missing tokens are common in analytical systems like R and SAS
073 where they are used to handle sparsely populated data sources.
074 This class has support for such tokens, see {@link #NIL} for details.
075
076 @author Neil Smyth, Yuhong Xiong, Edward A. Lee, Christopher Brooks,
077 Steve Neuendorffer
078 @version $Id$
079 @since Ptolemy II 0.2
080 @Pt.ProposedRating Yellow (cxh)
081 @Pt.AcceptedRating Red (cxh) nil token code
082
083 @see ScalarToken
084 @see AbstractConvertibleToken
085 @see AbstractNotConvertibleToken
086 @see MatrixToken
087 */
088public class Token {
089
090    /** Create a Token.
091     */
092    public Token() {
093        super();
094    }
095
096    ///////////////////////////////////////////////////////////////////
097    ////                         public methods                    ////
098
099    /** Return a new token whose value is the sum of this token and
100     *  the argument.
101     *  @param rightArgument The token to add to this token.
102     *  @return A new token containing the result.
103     *  If either this token or the argument token is a nil token, then
104     *  {@link #NIL} is returned.
105     *  @exception IllegalActionException If the argument token and
106     *  this token are of incomparable types, or the operation does
107     *  not make sense for the given types.
108     */
109    public Token add(Token rightArgument) throws IllegalActionException {
110        if (isNil() || rightArgument.isNil()) {
111            return Token.NIL;
112        }
113        throw new IllegalActionException(
114                notSupportedMessage("add", this, rightArgument));
115    }
116
117    /** Return a new token whose value is the sum of this token
118     *  and the argument.
119     *  @param leftArgument The token to add this token to.
120     *  @return A new token containing the result.
121     *  If either this token or the argument token is a nil token, then
122     *  {@link #NIL} is returned.
123     *  @exception IllegalActionException If the argument token and
124     *  this token are of incomparable types, or the operation does
125     *  not make sense for the given types.
126     */
127    public Token addReverse(Token leftArgument) throws IllegalActionException {
128        if (isNil() || leftArgument.isNil()) {
129            return Token.NIL;
130        }
131        throw new IllegalActionException(
132                notSupportedMessage("addReverse", this, leftArgument));
133    }
134
135    /** Return a new token whose value is the value of this token
136     *  divided by the value of the argument token.
137     *  @param rightArgument The token to divide into this token.
138     *  @return A new token containing the result.
139     *  If either this token or the argument token is a nil token, then
140     *  {@link #NIL} is returned.
141     *  @exception IllegalActionException If the argument token and
142     *  this token are of incomparable types, or the operation does
143     *  not make sense for the given types.
144     */
145    public Token divide(Token rightArgument) throws IllegalActionException {
146        if (isNil() || rightArgument.isNil()) {
147            return Token.NIL;
148        }
149        throw new IllegalActionException(
150                notSupportedMessage("divide", this, rightArgument));
151    }
152
153    /** Return a new token whose value is the value of the argument
154     *  token divided by the value of this token.
155     *  @param leftArgument The token to be divided by the value of
156     *  this token.
157     *  @return A new token containing the result.
158     *  @exception IllegalActionException If the argument token and
159     *  this token are of incomparable types, or the operation does
160     *  not make sense for the given types.
161     */
162    public Token divideReverse(Token leftArgument)
163            throws IllegalActionException {
164        if (isNil() || leftArgument.isNil()) {
165            return Token.NIL;
166        }
167        throw new IllegalActionException(
168                notSupportedMessage("divideReverse", this, leftArgument));
169    }
170
171    /** Return the type of this token.
172     *  @return BaseType.GENERAL
173     */
174    public Type getType() {
175        return BaseType.GENERAL;
176    }
177
178    /** Test that the value of this Token is close to the argument
179     *  Token.  In this base class, we call isEqualTo().  This method
180     *  should be overridden in derived classes such as DoubleToken
181     *  and ComplexToken to provide type specific actions for
182     *  equality testing using an epsilon factor.
183     *
184     *  @see #isEqualTo
185     *  @param token The token to test closeness of this token with.
186     *  @return a boolean token that contains the value true if the
187     *  value and units of this token are close to those of the
188     *  argument token.
189     *  If either this token or the argument token is a nil token, then
190     *  a boolean token that contains the value false is returned.
191     *  @exception IllegalActionException If the argument token is not
192     *  of a type that can be compared with this token.
193     */
194    public final BooleanToken isCloseTo(Token token)
195            throws IllegalActionException {
196        return isCloseTo(token, ptolemy.math.Complex.EPSILON);
197    }
198
199    /** Test that the value of this Token is close to the first argument,
200     *  where "close" means that the distance between them is less than
201     *  or equal to the second argument.  This method only makes sense
202     *  for tokens where the distance between them is reasonably
203     *  represented as a double.
204     *  @param token The token to test closeness of this token with.
205     *  @param epsilon The value that we use to determine whether two
206     *   tokens are close.
207     *  @return A boolean token that contains the value true if the
208     *   value of this token are close to those of the
209     *   argument token.
210     *  If either this token or the argument token is a nil token, then
211     *  a boolean token that contains the value false is returned.
212     *  @exception IllegalActionException If the argument token is not
213     *   of a type that can be compared with this token.
214     */
215    public BooleanToken isCloseTo(Token token, double epsilon)
216            throws IllegalActionException {
217        if (isNil() || token.isNil()) {
218            return BooleanToken.FALSE;
219        }
220        throw new IllegalActionException(
221                notSupportedMessage("isCloseTo", this, token));
222    }
223
224    /** Test for equality of the values of this Token and the argument
225     *  Token.
226     *
227     *  @param rightArgument The token with which to test equality.
228     *  @return A BooleanToken which contains the result of the test.
229     *  If either this token or the argument token is a nil token, then
230     *  a boolean token that contains the value false is returned.
231     *  @exception IllegalActionException If the argument token is not
232     *  of a type that can be compared with this token.
233     */
234    public BooleanToken isEqualTo(Token rightArgument)
235            throws IllegalActionException {
236        if (isNil() || rightArgument.isNil()) {
237            return BooleanToken.FALSE;
238        }
239        throw new IllegalActionException(
240                notSupportedMessage("isEqualTo", this, rightArgument));
241    }
242
243    /** Return true if the token is nil, (aka null or missing).
244     *  Nil or missing tokens occur when a data source is sparsely populated.
245     *  @return True if the token is equals() to {@link #NIL}.
246     */
247    public boolean isNil() {
248        // We use a method here so that we can easily change how
249        // we determine if a token is nil without modify lots of classes.
250        // Can't use equals() here, or we'll go into an infinite loop.
251        return this.equals(NIL);
252    }
253
254    /** Return a new token whose value is the value of this token
255     *  modulo the value of the argument token.
256     *  @param rightArgument The token to divide into this token.
257     *  @return A new token containing the result.
258     *  If either this token or the argument token is a nil token, then
259     *  {@link #NIL} is returned.
260     *  @exception IllegalActionException If the argument token and
261     *  this token are of incomparable types, or the operation does
262     *  not make sense for the given types.
263     */
264    public Token modulo(Token rightArgument) throws IllegalActionException {
265        if (isNil() || rightArgument.isNil()) {
266            return Token.NIL;
267        }
268        throw new IllegalActionException(
269                notSupportedMessage("modulo", this, rightArgument));
270    }
271
272    /** Return a new token whose value is the value of the argument token
273     *  modulo the value of this token.
274     *  @param leftArgument The token to apply modulo to by the value
275     *  of this token.
276     *  If either this token or the argument token is a nil token, then
277     *  {@link #NIL} is returned.
278     *  @return A new token containing the result.
279     *  @exception IllegalActionException If the argument token and
280     *  this token are of incomparable types, or the operation does
281     *  not make sense for the given types.
282     */
283    public Token moduloReverse(Token leftArgument)
284            throws IllegalActionException {
285        if (isNil() || leftArgument.isNil()) {
286            return Token.NIL;
287        }
288        throw new IllegalActionException(
289                notSupportedMessage("moduloReverse", this, leftArgument));
290    }
291
292    /** Return a new token whose value is the value of this token
293     *  multiplied by the value of the argument token.
294     *  @param rightArgument The token to multiply this token by.
295     *  @return A new token containing the result.
296     *  If either this token or the argument token is a nil token, then
297     *  {@link #NIL} is returned.
298     *  @exception IllegalActionException If the argument token and
299     *  this token are of incomparable types, or the operation does
300     *  not make sense for the given types.
301     */
302    public Token multiply(Token rightArgument) throws IllegalActionException {
303        if (isNil() || rightArgument.isNil()) {
304            return Token.NIL;
305        }
306        throw new IllegalActionException(
307                notSupportedMessage("multiply", this, rightArgument));
308    }
309
310    /** Return a new token whose value is the value of the argument
311     *  token multiplied by the value of this token.
312     *  @param leftArgument The token to be multiplied by the value of
313     *  this token.
314     *  @return A new token containing the result.
315     *  @exception IllegalActionException If the argument token and
316     *  this token are of incomparable types, or the operation does
317     *  not make sense for the given types.
318     */
319    public Token multiplyReverse(Token leftArgument)
320            throws IllegalActionException {
321        if (isNil() || leftArgument.isNil()) {
322            return Token.NIL;
323        }
324        throw new IllegalActionException(
325                notSupportedMessage("multiplyReverse", this, leftArgument));
326    }
327
328    /** Return a string with an error message that states that
329     *  the given token cannot be converted to the given token type.
330     *  @param token The token being converted.
331     *  @param typeString A string representing the type that is being
332     *  converted to.
333     *  @return A string error message.
334     */
335    public static String notSupportedConversionMessage(Token token,
336            String typeString) {
337        // We use this method to factor out a very common message
338        return "Conversion is not supported from " + token.getClass().getName()
339                + " '" + token.toString() + "' to the type " + typeString + ".";
340    }
341
342    /** Return a string with an error message that states that
343     *  the given token cannot be converted to the given token type.
344     *  @param token The token being converted.
345     *  @param typeString A string representing the type that is being
346     *  converted to.
347     *  @return A string error message.
348     */
349    public static String notSupportedIncomparableConversionMessage(Token token,
350            String typeString) {
351        // We use this method to factor out a very common message
352        return "Conversion is not supported from " + token.getClass().getName()
353                + " '" + token.toString() + "' to the type " + typeString
354                + " because the type of the token is higher "
355                + "or incomparable with the given type.";
356    }
357
358    /** Return a string with an error message that states that the
359     *  given operation is not supported between two tokens, because
360     *  they have incomparable types and cannot be converted to the
361     *  same type.
362     *  @param operation A string naming the unsupported token
363     *  operation.
364     *  @param firstToken The first token in the message.
365     *  @param secondToken The second token in the message.
366     *  @return A string error message.
367     */
368    public static String notSupportedIncomparableMessage(String operation,
369            Token firstToken, Token secondToken) {
370        // We use this method to factor out a very common message
371        return operation + " method not supported between "
372                + firstToken.getClass().getName() + " '" + firstToken.toString()
373                + "' and " + secondToken.getClass().getName() + " '"
374                + secondToken.toString()
375                + "' because the types are incomparable.";
376    }
377
378    /** Return a string with an error message that states that the
379     *  given operation is not supported between two tokens.
380     *  @param operation A string naming the unsupported token
381     *  operation.
382     *  @param firstToken The first token in the message.
383     *  @param secondToken The second token in the message.
384     *  @return A string error message.
385     */
386    public static String notSupportedMessage(String operation, Token firstToken,
387            Token secondToken) {
388        // We use this method to factor out a very common message
389        return operation + " operation not supported between "
390                + firstToken.getClass().getName() + " '" + firstToken.toString()
391                + "' and " + secondToken.getClass().getName() + " '"
392                + secondToken.toString() + "'";
393    }
394
395    /** Returns a new Token representing the multiplicative identity.
396     *  It should be overridden in subclasses.
397     *  @exception IllegalActionException If this method is not
398     *   supported by the derived class.
399     *  @return A new Token containing the multiplicative identity.
400     *  If this token is a nil token, then {@link #NIL} is returned.
401     */
402    public Token one() throws IllegalActionException {
403        if (isNil()) {
404            return Token.NIL;
405        }
406        throw new IllegalActionException(
407                "Multiplicative identity not supported on "
408                        + this.getClass().getName() + ".");
409    }
410
411    /** Return a new token computed as follows:
412     *  <br> For positive <i>times</i> arguments, the result represents
413     *  the product of this token multiplied by itself the number of
414     *  times given by the argument.
415     *  <br> For negative <i>times</i> arguments, the result
416     *  represents the multiplicative inverse of the product of this
417     *  token multiplied by itself the number of times given by the
418     *  absolute value of the argument.
419     *  <br> More succinctly: one().divide(pow(-times))
420     *  <br> If the argument is zero, then the result is defined to be
421     *  the result of applying the one() method to this token.
422     *
423     *  <p>The token type returned by this method is the same as
424     *  the type of this token.  Note that the method is different
425     *  from java.lang.Math.pow(), since it returns an integer given
426     *  an integer token type, and is also well defined for matrix
427     *  types.
428     *  @param times The number of times to multiply.
429     *  @return The power.
430     *  If this token is a nil token, then {@link #NIL} is returned.
431     *  @exception IllegalActionException If the token is not
432     *  compatible for this operation.  Specifically, if the Token
433     *  type does not support division (for example matrices) then
434     *  using a negative <i>times</i> argument may throw an exception.
435     */
436    public ptolemy.data.Token pow(int times) throws IllegalActionException {
437        if (times == 0) {
438            // anything to the zero is one.
439            return one();
440        } else if (times < 0) {
441            ptolemy.data.Token result = this;
442
443            for (int k = times; k < -1; k++) {
444                result = result.multiply(this);
445            }
446
447            return one().divide(result);
448        } else {
449            ptolemy.data.Token result = this;
450
451            for (int k = 0; k < times - 1; k++) {
452                result = result.multiply(this);
453            }
454
455            return result;
456        }
457    }
458
459    /** Return a new token whose value is the value of the argument token
460     *  subtracted from the value of this token.
461     *  @param rightArgument The token to subtract from this token.
462     *  @return A new token containing the result.
463     *  If either this token or the argument token is a nil token, then
464     *  {@link #NIL} is returned.
465     *  @exception IllegalActionException If the argument token and
466     *  this token are of incomparable types, or the operation does
467     *  not make sense for the given types.
468     */
469    public Token subtract(Token rightArgument) throws IllegalActionException {
470        if (isNil() || rightArgument.isNil()) {
471            return Token.NIL;
472        }
473        throw new IllegalActionException(
474                notSupportedMessage("subtract", this, rightArgument));
475    }
476
477    /** Return a new token whose value is the value of this token
478     *  subtracted from the value of the argument token.
479     *  @param leftArgument The token to subtract this token from.
480     *  @return A new token containing the result.
481     *  If either this token or the argument token is a nil token, then
482     *  {@link #NIL} is returned.
483     *  @exception IllegalActionException If the argument token and
484     *  this token are of incomparable types, or the operation does
485     *  not make sense for the given types.
486     */
487    public Token subtractReverse(Token leftArgument)
488            throws IllegalActionException {
489        if (isNil() || leftArgument.isNil()) {
490            return Token.NIL;
491        }
492        throw new IllegalActionException(
493                notSupportedMessage("subtractReverse", this, leftArgument));
494    }
495
496    /** Return the value of this token as a string that can be parsed
497     *  by the expression language to recover a token with the same value.
498     *  This method should be overridden by derived classes.
499     *  In this base class, return the String "present" to indicate
500     *  that an event is present. If this token is {@link #NIL} then
501     *  return "nil"
502     *  @return The String "present", unless this token is {@link #NIL},
503     *  in which case return the String "nil".
504     */
505    @Override
506    public String toString() {
507        if (isNil()) {
508            return "nil";
509        }
510        return "present";
511    }
512
513    /** Returns a new token representing the additive identity.
514     *  It should be overridden in subclasses.
515     *  @return A new Token containing the additive identity.
516     *  If this token is a nil token, then {@link #NIL} is returned.
517     *  @exception IllegalActionException If this method is not
518     *  supported by the derived class.
519     */
520    public Token zero() throws IllegalActionException {
521        if (isNil()) {
522            return Token.NIL;
523        }
524        throw new IllegalActionException("Additive identity not supported on "
525                + this.getClass().getName() + ".");
526    }
527
528    /** Return the (exact) return type of the zero function above.
529     *  The argument type is always returned
530     *  @param type The type of the argument to the corresponding function.
531     *  @return The type of the value returned from the corresponding function.
532     */
533    public static Type zeroReturnType(Type type) {
534        return type;
535    }
536
537    ///////////////////////////////////////////////////////////////////
538    ////                         public variables                  ////
539
540    /** A token that represents a missing value.
541     *  Null or missing tokens are common in analytical systems
542     *  like R and SAS where they are used to handle sparsely populated data
543     *  sources.  In database parlance, missing tokens are sometimes called
544     *  null tokens.  Since null is a Java keyword, we use the term "nil".
545     *  The toString() method on a nil token returns the string "nil".
546     */
547    public static final Token NIL = new Token() {
548        /** Return the type of this token.
549         *  @return BaseType.NIL.
550         */
551        @Override
552        public Type getType() {
553            return BaseType.NIL;
554        }
555    };
556
557    ///////////////////////////////////////////////////////////////////
558    ////                         protected methods                 ////
559
560    /** Return a string with an error message that states that
561     *  the token cannot be created with a string that is null
562     *  or the value of the init parameter.
563     *  @param type The type we are trying to create.
564     *  @param init The initialization string.
565     *  @return A string error message.
566     */
567    public static String notSupportedNullNilStringMessage(String type,
568            String init) {
569        return "Creating a nil token with " + type + "("
570                + (init == null ? "null" : "\"" + init + "\"")
571                + ") is not supported.  Use " + type + ".NIL, or"
572                + " the nil Constant.";
573    }
574}