KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > value > StringValue


1 package net.sf.saxon.value;
2
3 import net.sf.saxon.Err;
4 import net.sf.saxon.ConversionContext;
5 import net.sf.saxon.expr.XPathContext;
6 import net.sf.saxon.om.Item;
7 import net.sf.saxon.om.SequenceIterator;
8 import net.sf.saxon.om.FastStringBuffer;
9 import net.sf.saxon.om.XMLChar;
10 import net.sf.saxon.trans.DynamicError;
11 import net.sf.saxon.trans.XPathException;
12 import net.sf.saxon.type.*;
13
14
15 /**
16  * An atomic value of type xs:string
17  */

18
19 public class StringValue extends AtomicValue {
20
21     public static final StringValue EMPTY_STRING = new StringValue("");
22     public static final StringValue SINGLE_SPACE = new StringValue(" ");
23
24     // We hold the value as a CharSequence (it may be a StringBuffer rather than a string)
25
// But the first time this is converted to a string, we keep it as a string
26

27     protected CharSequence JavaDoc value; // may be zero-length, will never be null
28
protected int length = -1; // the length in XML characters - not necessarily the same as the Java length
29

30     /**
31      * Protected constructor for use by subtypes
32      */

33
34     protected StringValue() {
35         value = "";
36     }
37
38     /**
39      * Constructor. Note that although a StringValue may wrap any kind of CharSequence
40      * (usually a String, but it can also be, for example, a StringBuffer), the caller
41      * is responsible for ensuring that the value is immutable.
42      * @param value the String value. Null is taken as equivalent to "".
43      */

44
45     public StringValue(CharSequence JavaDoc value) {
46         this.value = (value == null ? "" : value);
47     }
48
49     /**
50      * Factory method. Unlike the constructor, this avoids creating a new StringValue in the case
51      * of a zero-length string (and potentially other strings, in future)
52      * @param value the String value. Null is taken as equivalent to "".
53      * @return the corresponding StringValue
54      */

55
56     public static StringValue makeStringValue(CharSequence JavaDoc value) {
57         if (value == null || value.length() == 0) {
58             return StringValue.EMPTY_STRING;
59         } else {
60             return new StringValue(value);
61         }
62     }
63
64     /**
65      * Get the string value as a String
66      */

67
68     public final String JavaDoc getStringValue() {
69         return (String JavaDoc) (value = value.toString());
70     }
71
72     /**
73      * Get the value of the item as a CharSequence. This is in some cases more efficient than
74      * the version of the method that returns a String.
75      */

76
77     public final CharSequence JavaDoc getStringValueCS() {
78         return value;
79     }
80
81     /**
82      * Convert a value to another primitive data type, with control over how validation is
83      * handled.
84      * @param requiredType type code of the required atomic type
85      * @param validate true if validation is required. If set to false, the caller guarantees that
86      * the value is valid for the target data type, and that further validation is therefore not required.
87      * Note that a validation failure may be reported even if validation was not requested.
88      * @param conversion
89      * @return the result of the conversion, if successful. If unsuccessful, the value returned
90      * will be a ValidationErrorValue. The caller must check for this condition. No exception is thrown, instead
91      * the exception will be encapsulated within the ErrorValue.
92      */

93
94     public AtomicValue convertPrimitive(BuiltInAtomicType requiredType, boolean validate, ConversionContext conversion) {
95         int req = requiredType.getFingerprint();
96         if (req == Type.STRING || req == Type.ATOMIC || req == Type.ITEM) {
97             return this;
98         }
99         return convertStringToBuiltInType(value, requiredType, validate);
100     }
101
102     /**
103      * Convert a string value to another built-in data type, with control over how validation is
104      * handled.
105      * @param value the value to be converted
106      * @param requiredType the required atomic type
107      * @param validate true if validation is required. If set to false, the caller guarantees that
108      * the value is valid for the target data type, and that further validation is therefore not required.
109      * Note that a validation failure may be reported even if validation was not requested.
110      * @return the result of the conversion, if successful. If unsuccessful, the value returned
111      * will be a {@link ValidationErrorValue}. The caller must check for this condition. No exception is thrown, instead
112      * the exception will be encapsulated within the ValidationErrorValue.
113      */

114
115     public static AtomicValue convertStringToBuiltInType(CharSequence JavaDoc value, BuiltInAtomicType requiredType,
116                                             boolean validate) {
117         try {
118             switch (requiredType.getFingerprint()) {
119                 case Type.BOOLEAN: {
120                     return BooleanValue.fromString(value);
121                 }
122                 case Type.NUMBER:
123                 case Type.DOUBLE:
124                     return new DoubleValue(value);
125
126                 case Type.INTEGER:
127                     return IntegerValue.stringToInteger(value);
128
129
130                 case Type.UNSIGNED_LONG:
131                 case Type.UNSIGNED_INT:
132                 case Type.UNSIGNED_SHORT:
133                 case Type.UNSIGNED_BYTE:
134                     if (validate) {
135                         for (int c=0; c<value.length(); c++) {
136                             if (value.charAt(c) == '+') {
137                                 ValidationException err = new ValidationException(
138                                         "An unsigned number must not contain a plus sign");
139                                 return new ValidationErrorValue(err);
140                             }
141                         }
142                     }
143                     // fall through
144
case Type.NON_POSITIVE_INTEGER:
145                 case Type.NEGATIVE_INTEGER:
146                 case Type.LONG:
147                 case Type.INT:
148                 case Type.SHORT:
149                 case Type.BYTE:
150                 case Type.NON_NEGATIVE_INTEGER:
151                 case Type.POSITIVE_INTEGER:
152                     AtomicValue iv = IntegerValue.stringToInteger(value);
153                     if (iv instanceof ValidationErrorValue) {
154                         // indicates that the conversion failed
155
return iv;
156                     }
157                     ValidationException err;
158                     if (iv instanceof IntegerValue) {
159                         err = ((IntegerValue)iv).convertToSubtype(requiredType, validate);
160                     } else {
161                         err = ((BigIntegerValue)iv).convertToSubType(requiredType, validate);
162                     }
163                     return (err == null ? iv : new ValidationErrorValue(err));
164                 case Type.DECIMAL:
165                     return DecimalValue.makeDecimalValue(value, validate);
166                 case Type.FLOAT:
167                     return new FloatValue(value);
168                 case Type.DATE:
169                     return new DateValue(value);
170                 case Type.DATE_TIME:
171                     return new DateTimeValue(value);
172                 case Type.TIME:
173                     return new TimeValue(value);
174                 case Type.G_YEAR:
175                     return new GYearValue(value);
176                 case Type.G_YEAR_MONTH:
177                     return new GYearMonthValue(value);
178                 case Type.G_MONTH:
179                     return new GMonthValue(value);
180                 case Type.G_MONTH_DAY:
181                     return new GMonthDayValue(value);
182                 case Type.G_DAY:
183                     return new GDayValue(value);
184                 case Type.DURATION:
185                     return new DurationValue(value);
186                 case Type.YEAR_MONTH_DURATION:
187                     return new MonthDurationValue(value);
188                 case Type.DAY_TIME_DURATION:
189                     return new SecondsDurationValue(value);
190                 case Type.UNTYPED_ATOMIC:
191                 case Type.ANY_SIMPLE_TYPE:
192                     return new UntypedAtomicValue(value);
193                 case Type.STRING:
194                 case Type.ATOMIC:
195                 case Type.ITEM:
196                     return makeStringValue(value);
197                 case Type.NORMALIZED_STRING:
198                 case Type.TOKEN:
199                 case Type.LANGUAGE:
200                 case Type.NAME:
201                 case Type.NCNAME:
202                 case Type.ID:
203                 case Type.IDREF:
204                 case Type.ENTITY:
205                 case Type.NMTOKEN:
206                     return RestrictedStringValue.makeRestrictedString(value, requiredType.getFingerprint(), validate);
207                 case Type.ANY_URI:
208                     if (AnyURIValue.isValidURI(value)) {
209                         return new AnyURIValue(value);
210                     } else {
211                         throw new ValidationException("Invalid URI: " + value.toString());
212                     }
213                 case Type.HEX_BINARY:
214                     return new HexBinaryValue(value);
215                 case Type.BASE64_BINARY:
216                     return new Base64BinaryValue(value);
217                 default:
218                     throw new ValidationException("Cannot convert string to type " +
219                             Err.wrap(requiredType.getDisplayName()));
220             }
221         } catch (ValidationException err) {
222             if (err.getErrorCodeLocalPart() == null) {
223                 err.setErrorCode("FORG0001");
224             }
225             return new ValidationErrorValue(err);
226         } catch (XPathException err) {
227             if (err.getErrorCodeLocalPart() == null) {
228                 err.setErrorCode("FORG0001");
229             }
230             ValidationException ve = new ValidationException(err.getMessage());
231             if (err.getErrorCodeLocalPart() == null) {
232                 ve.setErrorCode("FORG0001");
233             } else {
234                 ve.setErrorCode(err.getErrorCodeLocalPart());
235             }
236             return new ValidationErrorValue(ve);
237         }
238     }
239
240     /**
241      * Convert the value to a given type. The result of the conversion will be
242      * an atomic value of the required type. This method works where the target
243      * type is a built-in atomic type and also where it is a user-defined atomic
244      * type.
245      *
246      * @param targetType the type to which the value is to be converted
247      * @param validate true if validation is required, false if the caller already knows that the
248      * value is valid
249      * @return the value after conversion if successful; or a {@link ValidationErrorValue} if conversion failed. The
250      * caller must check for this condition. Validation may fail even if validation was not requested.
251      */

252
253     public static AtomicValue convertStringToAtomicType(
254             CharSequence JavaDoc value, AtomicType targetType, boolean validate) {
255         if (targetType instanceof BuiltInAtomicType) {
256             return convertStringToBuiltInType(value, (BuiltInAtomicType)targetType, validate);
257         } else {
258             AtomicValue v = convertStringToBuiltInType(
259                     value,
260                     (BuiltInAtomicType)targetType.getPrimitiveItemType(),
261                     validate);
262             if (v instanceof ValidationErrorValue) {
263                 // conversion has failed
264
return v;
265             }
266             return targetType.makeDerivedValue(v, value, validate);
267         }
268     }
269
270
271     /**
272      * Return the type of the expression
273      * @return Type.STRING (always)
274      */

275
276     public ItemType getItemType() {
277         return Type.STRING_TYPE;
278     }
279
280     /**
281      * Get the length of this string, as defined in XPath. This is not the same as the Java length,
282      * as a Unicode surrogate pair counts as a single character
283      */

284
285     public int getStringLength() {
286         // memo function; only compute it the first time
287
if (length == -1) {
288             length = getStringLength(value);
289         }
290         return length;
291     }
292
293     /**
294      * Get the length of a string, as defined in XPath. This is not the same as the Java length,
295      * as a Unicode surrogate pair counts as a single character.
296      * @param s The string whose length is required
297      */

298
299     public static int getStringLength(CharSequence JavaDoc s) {
300         int n = 0;
301         for (int i = 0; i < s.length(); i++) {
302             int c = (int) s.charAt(i);
303             if (c < 55296 || c > 56319) n++; // don't count high surrogates, i.e. D800 to DBFF
304
}
305         return n;
306     }
307
308     /**
309      * Iterate over a string, returning a sequence of integers representing the Unicode code-point values
310      */

311
312     public SequenceIterator iterateCharacters() {
313         return new CharacterIterator();
314     }
315
316     /**
317      * Expand a string containing surrogate pairs into an array of 32-bit characters
318      */

319
320     public static int[] expand(CharSequence JavaDoc s) {
321         int[] array = new int[getStringLength(s)];
322         int o = 0;
323         for (int i = 0; i < s.length(); i++) {
324             int charval;
325             int c = s.charAt(i);
326             if (c >= 55296 && c <= 56319) {
327                 // we'll trust the data to be sound
328
charval = ((c - 55296) * 1024) + ((int) s.charAt(i + 1) - 56320) + 65536;
329                 i++;
330             } else {
331                 charval = c;
332             }
333             array[o++] = charval;
334         }
335         return array;
336     }
337
338     /**
339      * Contract an array of integers containing Unicode codepoints into a Java string
340      */

341
342     public static CharSequence JavaDoc contract(int[] codes, int used) {
343         FastStringBuffer sb = new FastStringBuffer(codes.length);
344         for (int i=0; i<used; i++) {
345             if (codes[i]<65536) {
346                 sb.append((char)codes[i]);
347             }
348             else { // output a surrogate pair
349
sb.append(XMLChar.highSurrogate(codes[i]));
350                 sb.append(XMLChar.lowSurrogate(codes[i]));
351             }
352         }
353         return sb;
354     }
355
356     /**
357      * Determine if two StringValues are equal, according to XML Schema rules. (This method
358      * is not used for XPath comparisons, which are always under the control of a collation.)
359      * @throws ClassCastException if the values are not comparable
360      */

361
362     public boolean equals(Object JavaDoc other) {
363         // For XML Schema purposes a String is never equal to a URI
364
if (other instanceof AnyURIValue) {
365             throw new ClassCastException JavaDoc("Cannot compare string to anyURI");
366         }
367         // Force a ClassCastException if the other value isn't a string or derived from string
368
StringValue otherVal = (StringValue) ((AtomicValue) other).getPrimitiveValue();
369         // cannot use equals() directly on two unlike CharSequences
370
return getStringValue().equals(otherVal.getStringValue());
371     }
372
373     public int hashCode() {
374         return getStringValue().hashCode();
375     }
376
377     /**
378      * Compare two values for equality. This supports identity constraints in XML Schema,
379      * which allow list-valued elements and attributes to participate in key and uniqueness constraints.
380      * This method returns false if any error occurs during the comparison, or if any of the items
381      * in either sequence is a node rather than an atomic value. The default implementation of
382      * schemaEquals() is the same as equals(), but subclasses can override this.
383      */

384
385     public boolean schemaEquals(Value obj) {
386         if (obj instanceof AtomicValue) {
387             obj = ((AtomicValue)obj).getPrimitiveValue();
388         }
389         if (obj instanceof StringValue) {
390             return value.toString().equals(((StringValue)obj).value.toString());
391         } else {
392             return false;
393         }
394     }
395
396     public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
397         return value.length() > 0;
398     }
399
400
401     /**
402      * Convert to Java object (for passing to external functions)
403      */

404
405     public Object JavaDoc convertToJava(Class JavaDoc target, XPathContext context) throws XPathException {
406         if (target == Object JavaDoc.class) {
407             return value;
408         } else if (target.isAssignableFrom(StringValue.class)) {
409             return this;
410         } else if (target == String JavaDoc.class || target == CharSequence JavaDoc.class) {
411             return getStringValue();
412         } else if (target == boolean.class) {
413             BooleanValue bval = (BooleanValue) convert(Type.BOOLEAN, context);
414             return Boolean.valueOf(bval.getBooleanValue());
415         } else if (target == Boolean JavaDoc.class) {
416             BooleanValue bval = (BooleanValue) convert(Type.BOOLEAN, context);
417             return Boolean.valueOf(bval.getBooleanValue());
418         } else if (target == double.class) {
419             DoubleValue dval = (DoubleValue) convert(Type.DOUBLE, context);
420             return new Double JavaDoc(dval.getDoubleValue());
421         } else if (target == Double JavaDoc.class) {
422             DoubleValue dval = (DoubleValue) convert(Type.DOUBLE, context);
423             return new Double JavaDoc(dval.getDoubleValue());
424         } else if (target == float.class) {
425             DoubleValue dval = (DoubleValue) convert(Type.DOUBLE, context);
426             return new Float JavaDoc(dval.getDoubleValue());
427         } else if (target == Float JavaDoc.class) {
428             DoubleValue dval = (DoubleValue) convert(Type.DOUBLE, context);
429             return new Float JavaDoc(dval.getDoubleValue());
430         } else if (target == long.class) {
431             IntegerValue dval = (IntegerValue) convert(Type.INTEGER, context);
432             return new Long JavaDoc(dval.longValue());
433         } else if (target == Long JavaDoc.class) {
434             IntegerValue dval = (IntegerValue) convert(Type.INTEGER, context);
435             return new Long JavaDoc(dval.longValue());
436         } else if (target == int.class) {
437             IntegerValue dval = (IntegerValue) convert(Type.INTEGER, context);
438             return new Integer JavaDoc((int) dval.longValue());
439         } else if (target == Integer JavaDoc.class) {
440             IntegerValue dval = (IntegerValue) convert(Type.INTEGER, context);
441             return new Integer JavaDoc((int) dval.longValue());
442         } else if (target == short.class) {
443             IntegerValue dval = (IntegerValue) convert(Type.INTEGER, context);
444             return new Short JavaDoc((short) dval.longValue());
445         } else if (target == Short JavaDoc.class) {
446             IntegerValue dval = (IntegerValue) convert(Type.INTEGER, context);
447             return new Short JavaDoc((short) dval.longValue());
448         } else if (target == byte.class) {
449             IntegerValue dval = (IntegerValue) convert(Type.INTEGER, context);
450             return new Byte JavaDoc((byte) dval.longValue());
451         } else if (target == Byte JavaDoc.class) {
452             IntegerValue dval = (IntegerValue) convert(Type.INTEGER, context);
453             return new Byte JavaDoc((byte) dval.longValue());
454         } else if (target == char.class || target == Character JavaDoc.class) {
455             if (value.length() == 1) {
456                 return new Character JavaDoc(value.charAt(0));
457             } else {
458                 DynamicError de = new DynamicError("Cannot convert string to Java char unless length is 1");
459                 de.setXPathContext(context);
460                 de.setErrorCode("SAXON:0000");
461                 throw de;
462             }
463         } else {
464             Object JavaDoc o = super.convertToJava(target, context);
465             if (o == null) {
466                 DynamicError err = new DynamicError(
467                         "Conversion of string to " + target.getName() + " is not supported");
468                 err.setXPathContext(context);
469                 err.setErrorCode("SAXON:0000");
470                 throw err;
471             }
472             return o;
473         }
474     }
475
476     public String JavaDoc toString() {
477         return "\"" + value + '\"';
478     }
479
480
481     /**
482      * CharacterIterator is used to iterate over the characters in a string,
483      * returning them as integers representing the Unicode code-point.
484      */

485
486
487     public final class CharacterIterator implements SequenceIterator {
488
489         int inpos = 0; // 0-based index of the current Java char
490
int outpos = 0; // 1-based value of position() function
491
int current = -1; // Unicode codepoint most recently returned
492

493         /**
494          * Create an iterator over a string
495          */

496
497         public CharacterIterator() {
498         }
499
500         public Item next() {
501             if (inpos < value.length()) {
502                 int c = value.charAt(inpos++);
503                 if (c >= 55296 && c <= 56319) {
504                     // we'll trust the data to be sound
505
current = ((c - 55296) * 1024) + ((int) value.charAt(inpos++) - 56320) + 65536;
506                 } else {
507                     current = c;
508                 }
509                 outpos++;
510                 return new IntegerValue(current);
511             } else {
512                 outpos = -1;
513                 return null;
514             }
515         }
516
517         public Item current() {
518             if (outpos < 1) {
519                 return null;
520             }
521             return new IntegerValue(current);
522         }
523
524         public int position() {
525             return outpos;
526         }
527
528         public SequenceIterator getAnother() {
529             return new CharacterIterator();
530         }
531
532         /**
533          * Get properties of this iterator, as a bit-significant integer.
534          *
535          * @return the properties of this iterator. This will be some combination of
536          * properties such as {@link GROUNDED} and {@link LAST_POSITION_FINDER}. It is always
537          * acceptable to return the value zero, indicating that there are no known special properties.
538          */

539
540         public int getProperties() {
541             return 0;
542         }
543     }
544
545
546 }
547
548 //
549
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
550
// you may not use this file except in compliance with the License. You may obtain a copy of the
551
// License at http://www.mozilla.org/MPL/
552
//
553
// Software distributed under the License is distributed on an "AS IS" basis,
554
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
555
// See the License for the specific language governing rights and limitations under the License.
556
//
557
// The Original Code is: all this file.
558
//
559
// The Initial Developer of the Original Code is Michael H. Kay.
560
//
561
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
562
//
563
// Contributor(s): none.
564
//
565

566
Popular Tags