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}