001/* A token that contains a string. 002 003 Copyright (c) 1997-2018 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.Type; 033import ptolemy.data.type.TypeLattice; 034import ptolemy.graph.CPO; 035import ptolemy.kernel.util.IllegalActionException; 036import ptolemy.util.StringUtilities; 037 038/////////////////////////////////////////////////////////////////// 039//// StringToken 040 041/** 042 A token that contains a string, or more specifically, a reference 043 to an instance of String. The reference is never null, although it may 044 be an empty string (""). 045 Note that when this token is cloned, the clone will refer to exactly 046 the same String object. However, a String object in Java is immutable, 047 so there is no risk when two tokens refer to the same string that 048 one of the strings will be changed. 049 050 @author Edward A. Lee, Neil Smyth, Steve Neuendorffer, contributor: Christopher Brooks 051 @version $Id$ 052 @since Ptolemy II 0.2 053 @Pt.ProposedRating Yellow (cxh) 054 @Pt.AcceptedRating Red (cxh) nil token code 055 */ 056public class StringToken extends AbstractConvertibleToken { 057 /** Construct a token with an empty string. 058 */ 059 public StringToken() { 060 this(""); 061 } 062 063 /** Construct a token with the specified string. 064 * If the value argument is null then the empty string is created. 065 * If the value argument is the string "nil", then the Token is not a 066 * nil token it is the string "nil". 067 * @param value The specified string. 068 */ 069 public StringToken(String value) { 070 if (value == null) { 071 _value = ""; 072 } else { 073 _value = value; 074 } 075 _toString = "\"" + StringUtilities.escapeString(_value) + "\""; 076 077 /* The following logic resulted in a bit too much "smarts" 078 * and toString() would not exactly get reversed by the expression 079 * parser. EAL 2/1/07. 080 // If a String token is "has an embedded " quote", then 081 // toString() should return "has an embedded \" quote" 082 if (_value.indexOf('"') == -1) { 083 _toString = "\"" + _value + "\""; 084 } else { 085 if (_value.indexOf("\\\"") == -1) { 086 // Note that using backslashes in regexs results in 087 // onset of psychosis. If you change this, be sure to 088 // test your changes. We used to use the 089 // StringUtilities.substitute() method, but this is 090 // much faster for large strings. 091 _toString = "\"" + _value.replaceAll("\\\"", "\\\\\"") + "\""; 092 } else { 093 // The string already has a \" in it. 094 // 1. Substitute a special word for every instance of \" 095 String backslashed = _value.replaceAll("\\\\\"", 096 "MaGiCBakSlash"); 097 098 // 2. Substitute \" for every remaining " 099 String backslashed2 = backslashed.replaceAll("\"", "\\\\\""); 100 101 // 3. Add the leading and trailing " and substitute 102 // \" for every instance of the special word 103 _toString = "\"" 104 + backslashed2.replaceAll("MaGiCBakSlash", "\\\\\"") 105 + "\""; 106 } 107 } 108 */ 109 } 110 111 /////////////////////////////////////////////////////////////////// 112 //// public methods //// 113 114 /** Convert the specified token into an instance of StringToken. 115 * This method does lossless conversion. 116 * If the argument is already an instance of StringToken, 117 * it is returned without any change. 118 * If the argument is a nil token, then 119 * {@link #NIL} is returned. 120 * Otherwise, if the argument is below StringToken in the type 121 * hierarchy, it is converted to an instance of StringToken or 122 * one of the subclasses of StringToken and returned. If none of 123 * the above condition is met, an exception is thrown. 124 * @param token The token to be converted to a StringToken. 125 * @return A StringToken 126 * @exception IllegalActionException If the conversion cannot 127 * be carried out. 128 */ 129 public static StringToken convert(Token token) 130 throws IllegalActionException { 131 if (token instanceof StringToken) { 132 return (StringToken) token; 133 } 134 135 if (token == null || token.isNil()) { 136 return StringToken.NIL; 137 } 138 139 int compare = TypeLattice.compare(BaseType.STRING, token); 140 141 if (compare == CPO.LOWER || compare == CPO.INCOMPARABLE) { 142 throw new IllegalActionException( 143 notSupportedIncomparableConversionMessage(token, "string")); 144 } 145 146 if (token instanceof MatrixToken || token instanceof ScalarToken 147 || token instanceof BooleanToken || token instanceof FixToken 148 || token instanceof RecordToken || token instanceof DateToken) { 149 String str = token.toString(); 150 return new StringToken(str); 151 } 152 153 // The argument is below StringToken in the type hierarchy, 154 // but I don't recognize it. 155 throw new IllegalActionException( 156 notSupportedConversionMessage(token, "string")); 157 } 158 159 /** Return true if the argument is an instance of StringToken with the 160 * same value. 161 * @param object An instance of Object. 162 * @return True if the argument is an IntToken with the same 163 * value. If either this object or the argument is nil, return 164 * false. 165 */ 166 @Override 167 public boolean equals(Object object) { 168 if (object == null) { 169 return false; 170 } 171 // This test rules out subclasses. 172 if (object.getClass() != getClass()) { 173 return false; 174 } 175 176 if (isNil() || ((StringToken) object).isNil()) { 177 return false; 178 } 179 180 if (((StringToken) object).stringValue().equals(_value)) { 181 return true; 182 } 183 184 return false; 185 } 186 187 /** Return the type of this token. 188 * @return BaseType.STRING 189 */ 190 @Override 191 public Type getType() { 192 return BaseType.STRING; 193 } 194 195 /** Return a hash code value for this token. This method returns the 196 * hash code of the contained string. 197 * @return A hash code value for this token. 198 */ 199 @Override 200 public int hashCode() { 201 return _value.hashCode(); 202 } 203 204 /** Return true if the token is nil, (aka null or missing). 205 * Nil or missing tokens occur when a data source is sparsely populated. 206 * @return True if the token is the {@link #NIL} token. 207 */ 208 @Override 209 public boolean isNil() { 210 // We use a method here so that we can easily change how 211 // we determine if a token is nil without modify lots of classes. 212 // Can't use equals() here, or we'll go into an infinite loop. 213 return this == StringToken.NIL; 214 } 215 216 /** Return the string that this token contains. Note that this is 217 * different than the toString method, which returns a string expression 218 * that has double quotes around it. 219 * @return The contained string. 220 */ 221 public String stringValue() { 222 if (isNil()) { 223 return super.toString(); 224 } 225 return _value; 226 } 227 228 /** Return the value of this Token as a string. If the value of 229 * the Token contains double quotes, then a backslash is inserted 230 * before each double quote and then double quotes are added to 231 * the beginning and the end, indicating a string constant in the 232 * expression language. 233 * @return A String. 234 */ 235 @Override 236 public String toString() { 237 if (isNil()) { 238 return super.toString(); 239 } 240 return _toString; 241 } 242 243 /** Return a StringToken containing an empty string, which is considered 244 * as the additive identity of string. 245 * @return A new StringToken containing an empty string. 246 */ 247 @Override 248 public Token zero() { 249 return new StringToken(""); 250 } 251 252 /////////////////////////////////////////////////////////////////// 253 //// public variables //// 254 255 /** A token that represents a missing value. 256 * Null or missing tokens are common in analytical systems 257 * like R and SAS where they are used to handle sparsely populated data 258 * sources. In database parlance, missing tokens are sometimes called 259 * null tokens. Since null is a Java keyword, we use the term "nil". 260 * The toString() method on a nil token returns the string "nil". 261 */ 262 public static final StringToken NIL = new StringToken("nil"); 263 264 /////////////////////////////////////////////////////////////////// 265 //// protected methods //// 266 267 /** Return a new token whose value is the value of the 268 * argument Token added to the value of this Token. It is assumed 269 * that the type of the argument is StringToken. 270 * @param rightArgument The token whose value we add to the value of 271 * this token. 272 * @exception IllegalActionException If this method is not 273 * supported by the derived class. 274 * @return A new Token containing the result. 275 */ 276 @Override 277 protected Token _add(Token rightArgument) throws IllegalActionException { 278 String result = _value + ((StringToken) rightArgument).stringValue(); 279 return new StringToken(result); 280 } 281 282 /** Return a new token whose value is the value of this token 283 * divided by the value of the argument token. It is assumed 284 * that the type of the argument is StringToken. 285 * @param rightArgument The token to divide this token by. 286 * @exception IllegalActionException If this method is not 287 * supported by the derived class. 288 * @return A new Token containing the result that is of the same class 289 * as this token. 290 */ 291 @Override 292 protected Token _divide(Token rightArgument) throws IllegalActionException { 293 throw new IllegalActionException( 294 notSupportedMessage("divide", this, rightArgument)); 295 } 296 297 /** Test for closeness of the values of this Token and the argument 298 * Token. It is assumed that the type of the argument is 299 * StringToken. 300 * @param rightArgument The token to add to this token. 301 * @param epsilon The value that we use to determine whether two 302 * tokens are close. This parameter is ignored by this class. 303 * @return A BooleanToken containing the result. 304 * @exception IllegalActionException If this method is not 305 * supported by the derived class. 306 */ 307 @Override 308 protected BooleanToken _isCloseTo(Token rightArgument, double epsilon) 309 throws IllegalActionException { 310 return _isEqualTo(rightArgument); 311 } 312 313 /** Test for equality of the values of this Token and the argument 314 * Token. It is assumed that the type of the argument is 315 * StringToken. 316 * @param rightArgument The token to add to this token. 317 * @return A BooleanToken containing the result. 318 * @exception IllegalActionException If this method is not 319 * supported by the derived class. 320 */ 321 @Override 322 protected BooleanToken _isEqualTo(Token rightArgument) 323 throws IllegalActionException { 324 StringToken convertedArgument = (StringToken) rightArgument; 325 return BooleanToken.getInstance( 326 toString().compareTo(convertedArgument.toString()) == 0); 327 } 328 329 /** Return a new token whose value is the value of this token 330 * modulo the value of the argument token. It is assumed 331 * that the type of the argument is StringToken. 332 * @param rightArgument The token to modulo this token by. 333 * @exception IllegalActionException If this method is not 334 * supported by the derived class. 335 * @return A new Token containing the result that is of the same 336 * class as this token. 337 */ 338 @Override 339 protected Token _modulo(Token rightArgument) throws IllegalActionException { 340 throw new IllegalActionException( 341 notSupportedMessage("modulo", this, rightArgument)); 342 } 343 344 /** Return a new token whose value is the value of this token 345 * multiplied by the value of the argument token. It is assumed 346 * that the type of the argument is StringToken. 347 * classes to provide type specific actions for multiply. 348 * @param rightArgument The token to multiply this token by. 349 * @exception IllegalActionException If this method is not 350 * supported by the derived class. 351 * @return A new Token containing the result that is of the same class 352 * as this token. 353 */ 354 @Override 355 protected Token _multiply(Token rightArgument) 356 throws IllegalActionException { 357 throw new IllegalActionException( 358 notSupportedMessage("multiply", this, rightArgument)); 359 } 360 361 /** Return a new token whose value is the value of the argument token 362 * subtracted from the value of this token. It is assumed 363 * that the type of the argument is StringToken. 364 * @param rightArgument The token to subtract from this token. 365 * @exception IllegalActionException If this method is not 366 * supported by the derived class. 367 * @return A new Token containing the result that is of the same class 368 * as this token. 369 */ 370 @Override 371 protected Token _subtract(Token rightArgument) 372 throws IllegalActionException { 373 throw new IllegalActionException( 374 notSupportedMessage("subtract", this, rightArgument)); 375 } 376 377 /////////////////////////////////////////////////////////////////// 378 //// private variables //// 379 // The string contained in this token. 380 private String _value; 381 382 // The string contained in this token, with double quotes on either side. 383 private String _toString; 384}