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}