KickJava   Java API By Example, From Geeks To Geeks.

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


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.Token;
6 import net.sf.saxon.expr.XPathContext;
7 import net.sf.saxon.trans.DynamicError;
8 import net.sf.saxon.trans.XPathException;
9 import net.sf.saxon.type.*;
10
11 import java.math.BigDecimal JavaDoc;
12 import java.math.BigInteger JavaDoc;
13
14 /**
15  * An integer value: note this is a subtype of decimal in XML Schema, not a primitive type.
16  * This class also supports the built-in subtypes of xs:integer.
17  * Actually supports a value in the range permitted by a Java "long"
18  */

19
20 public final class IntegerValue extends NumericValue {
21
22     /**
23      * IntegerValue representing the value -1
24      */

25     public static final IntegerValue MINUS_ONE = new IntegerValue(-1);
26     /**
27      * IntegerValue representing the value zero
28      */

29     public static final IntegerValue ZERO = new IntegerValue(0);
30     /**
31      * IntegerValue representing the value +1
32      */

33     public static final IntegerValue PLUS_ONE = new IntegerValue(+1);
34     /**
35      * IntegerValue representing the maximum value for this class
36      */

37     public static final IntegerValue MAX_LONG = new IntegerValue(Long.MAX_VALUE);
38     /**
39      * IntegerValue representing the minimum value for this class
40      */

41     public static final IntegerValue MIN_LONG = new IntegerValue(Long.MIN_VALUE);
42
43     private long value;
44     private ItemType type;
45
46     /**
47      * Constructor supplying a long
48      *
49      * @param value the value of the IntegerValue
50      */

51
52     public IntegerValue(long value) {
53         this.value = value;
54         this.type = Type.INTEGER_TYPE;
55     }
56
57     /**
58      * Constructor for a subtype, supplying an integer
59      *
60      * @param val The supplied value, as an integer
61      * @param type the required item type, a subtype of xs:integer
62      * @exception DynamicError if the supplied value is out of range for the
63      * target type
64      */

65
66     public IntegerValue(long val, AtomicType type) throws DynamicError {
67         this.value = val;
68         this.type = type;
69         if (!checkRange(value, type)) {
70             DynamicError err = new DynamicError("Integer value " + val +
71                     " is out of range for the requested type " + type.getDescription());
72             err.setErrorCode("FORG0001");
73             throw err;
74         };
75     }
76
77     /**
78      * Convert the value to a subtype of xs:integer
79      * @param subtype the target subtype
80      * @param validate true if validation is required; false if the caller already knows that the value is valid
81      * @return null if the conversion succeeds; a ValidationException describing the failure if it fails. Note
82      * that the exception is returned, not thrown.
83      */

84
85     public ValidationException convertToSubtype(AtomicType subtype, boolean validate) {
86         if (!validate) {
87             setSubType(subtype);
88             return null;
89         } else if (checkRange(subtype)) {
90             return null;
91         } else {
92             ValidationException err = new ValidationException("String " + value +
93                     " cannot be converted to integer subtype " + subtype.getDescription());
94             err.setErrorCode("FORG0001");
95             return err;
96         }
97     }
98
99     /**
100      * This class allows subtypes of xs:integer to be held, as well as xs:integer values.
101      * This method sets the required type label. It is the caller's responsibility to check that
102      * the value is within range.
103      */

104     public void setSubType(AtomicType type) {
105         this.type = type;
106     }
107
108     /**
109      * This class allows subtypes of xs:integer to be held, as well as xs:integer values.
110      * This method checks that the value is within range, and also sets the type label.
111      * @param type the subtype of integer required
112      * @return true if successful, false if value is out of range for the subtype
113      */

114     public boolean checkRange(AtomicType type) {
115         this.type = type;
116         return checkRange(value, type);
117     }
118
119     /**
120      * Static factory method to convert strings to integers.
121      * @param s the String to be converted
122      * @return either an IntegerValue or a BigIntegerValue representing the value of the String, or
123      * an ErrorValue encapsulating an Exception if the value cannot be converted.
124      */

125
126     public static AtomicValue stringToInteger(CharSequence JavaDoc s) {
127
128         int len = s.length();
129         int start = 0;
130         while (start < len && s.charAt(start) <= 0x20) {
131             start++;
132         }
133         int last = len - 1;
134         while (last > start && s.charAt(last) <= 0x20) {
135             last--;
136         }
137         if (start > last) {
138             return numericError("Cannot convert zero-length string to an integer");
139         }
140         if (last - start < 16) {
141             // for short numbers, we do the conversion ourselves, to avoid throwing unnecessary exceptions
142
boolean negative = false;
143             long value = 0;
144             int i=start;
145             if (s.charAt(i) == '+') {
146                 i++;
147             } else if (s.charAt(i) == '-') {
148                 negative = true;
149                 i++;
150             }
151             if (i > last) {
152                 return numericError("Cannot convert string " + Err.wrap(s, Err.VALUE) +
153                         " to integer: no digits after the sign");
154             }
155             while (i <= last) {
156                 char d = s.charAt(i++);
157                 if (d >= '0' && d <= '9') {
158                     value = 10*value + (d-'0');
159                 } else {
160                     return numericError("Cannot convert string " + Err.wrap(s, Err.VALUE) + " to an integer");
161                 }
162             }
163             return new IntegerValue((negative ? -value : value));
164         } else {
165             // for longer numbers, rely on library routines
166
try {
167                 CharSequence JavaDoc t = trimWhitespace(s);
168                 if (t.charAt(0) == '+') {
169                     t = t.subSequence(1, t.length());
170                 }
171                 if (t.length() < 16) {
172                     return new IntegerValue(Long.parseLong(t.toString()));
173                 } else {
174                     return new BigIntegerValue(new BigInteger JavaDoc(t.toString()));
175                 }
176             } catch (NumberFormatException JavaDoc err) {
177                 return numericError("Cannot convert string " + Err.wrap(s, Err.VALUE) + " to an integer");
178             }
179         }
180     }
181
182     /**
183      * Helper method to handle errors converting a string to a number
184      * @param message error message
185      * @return an ErrorValue encapsulating an Exception describing the error
186      */

187     private static ValidationErrorValue numericError(String JavaDoc message) {
188         ValidationException err = new ValidationException(message);
189         err.setErrorCode("FORG0001");
190         return new ValidationErrorValue(err);
191     }
192
193     /**
194      * Check that a value is in range for the specified subtype of xs:integer
195      *
196      * @param value the value to be checked
197      * @param type the required item type, a subtype of xs:integer
198      * @return true if successful, false if value is out of range for the subtype
199      */

200
201     static boolean checkRange(long value, AtomicType type) {
202         for (int i = 0; i < ranges.length; i += 3) {
203             if (ranges[i] == type.getFingerprint()) {
204                 long min = ranges[i+1];
205                 if (min != NO_LIMIT && value < min) {
206                     return false;
207                 }
208                 long max = ranges[i+2];
209                 if (max != NO_LIMIT && max != MAX_UNSIGNED_LONG && value > max) {
210                     return false;
211                 }
212                 return true;
213             }
214         }
215         throw new IllegalArgumentException JavaDoc(
216                 "No range information found for integer subtype " + type.getDescription());
217     }
218
219     /**
220      * Check that a BigInteger is within the required range for a given integer subtype.
221      * This method is expensive, so it should not be used unless the BigInteger is outside the range of a long.
222      */

223
224     static boolean checkBigRange(BigInteger JavaDoc big, AtomicType type) {
225
226         for (int i = 0; i < ranges.length; i += 3) {
227             if (ranges[i] == type.getFingerprint()) {
228                 long min = ranges[i+1];
229                 if (min != NO_LIMIT && BigInteger.valueOf(min).compareTo(big) > 0) {
230                     return false;
231                 }
232                 long max = ranges[i+2];
233                 if (max == NO_LIMIT) {
234                     return true;
235                 } else if (max == MAX_UNSIGNED_LONG) {
236                     return BigIntegerValue.MAX_UNSIGNED_LONG.compareTo(big) >= 0;
237                 } else {
238                     return BigInteger.valueOf(max).compareTo(big) >= 0;
239                 }
240             }
241         }
242         throw new IllegalArgumentException JavaDoc(
243                 "No range information found for integer subtype " + type.getDescription());
244     }
245
246     /**
247      * Static data identifying the min and max values for each built-in subtype of xs:integer.
248      * This is a sequence of triples, each holding the fingerprint of the type, the minimum
249      * value, and the maximum value. The special value NO_LIMIT indicates that there is no
250      * minimum (or no maximum) for this type. The special value MAX_UNSIGNED_LONG represents the
251      * value 2^64-1
252      */

253     private static long NO_LIMIT = -9999;
254     private static long MAX_UNSIGNED_LONG = -9998;
255     private static long[] ranges = {
256         Type.INTEGER, NO_LIMIT, NO_LIMIT,
257         Type.NON_POSITIVE_INTEGER, NO_LIMIT, 0,
258         Type.NEGATIVE_INTEGER, NO_LIMIT, -1,
259         Type.LONG, Long.MIN_VALUE, Long.MAX_VALUE,
260         Type.INT, Integer.MIN_VALUE, Integer.MAX_VALUE,
261         Type.SHORT, Short.MIN_VALUE, Short.MAX_VALUE,
262         Type.BYTE, Byte.MIN_VALUE, Byte.MAX_VALUE,
263         Type.NON_NEGATIVE_INTEGER, 0, NO_LIMIT,
264         Type.POSITIVE_INTEGER, 1, NO_LIMIT,
265         Type.UNSIGNED_LONG, 0, MAX_UNSIGNED_LONG,
266         Type.UNSIGNED_INT, 0, 4294967295L,
267         Type.UNSIGNED_SHORT, 0, 65535,
268         Type.UNSIGNED_BYTE, 0, 255};
269
270
271     /**
272      * Get the hashCode. This must conform to the rules for other NumericValue hashcodes
273      * @see NumericValue#hashCode
274      */

275
276     public int hashCode() {
277         if (value > Integer.MIN_VALUE && value < Integer.MAX_VALUE) {
278             return (int) value;
279         } else {
280             return new Double JavaDoc(this.getDoubleValue()).hashCode();
281         }
282     }
283
284     /**
285      * Get the value
286      * @return the value of the xs:integer, as a Java long
287      */

288
289     public long longValue() {
290         return value;
291     }
292
293     /**
294      * Return the effective boolean value of this integer
295      * @param context The dynamic evaluation context; ignored in this
296      * implementation of the method
297      * @return false if the integer is zero, otherwise true
298      */

299     public boolean effectiveBooleanValue(XPathContext context) {
300         return value != 0;
301     }
302
303     /**
304      * Compare the value to another numeric value
305      * @param other the numeric value to be compared to this value
306      * @return -1 if this value is less than the other, 0 if they are equal,
307      * +1 if this value is greater
308      */

309
310     public int compareTo(Object JavaDoc other) {
311         if (other instanceof IntegerValue) {
312             long val2 = ((IntegerValue) other).value;
313             if (value == val2) return 0;
314             if (value < val2) return -1;
315             return 1;
316         } else if (other instanceof BigIntegerValue) {
317             return new BigIntegerValue(value).compareTo(other);
318         } else {
319             return super.compareTo(other);
320         }
321     }
322
323     /**
324      * Compare two values for equality. This supports identity constraints in XML Schema,
325      * which allow list-valued elements and attributes to participate in key and uniqueness constraints.
326      * This method returns false if any error occurs during the comparison, or if any of the items
327      * in either sequence is a node rather than an atomic value. The default implementation of
328      * schemaEquals() is the same as equals(), but subclasses can override this.
329      */

330
331     public boolean schemaEquals(Value obj) {
332         if (obj instanceof AtomicValue) {
333             obj = ((AtomicValue)obj).getPrimitiveValue();
334         }
335         if (obj instanceof DecimalValue) {
336             return obj.schemaEquals(this);
337         } else if (obj instanceof IntegerValue) {
338             return value == ((IntegerValue)obj).value;
339         } else if (obj instanceof BigIntegerValue) {
340             return equals(obj);
341         } else {
342             return false;
343         }
344     }
345
346     /**
347      * Convert to target data type
348      *
349      * @param requiredType an integer identifying the required atomic type
350      * @param conversion
351      * @return an AtomicValue, a value of the required type
352      */

353
354     public AtomicValue convertPrimitive(BuiltInAtomicType requiredType, boolean validate, ConversionContext conversion) {
355         switch (requiredType.getFingerprint()) {
356             case Type.BOOLEAN:
357                 return BooleanValue.get(value != 0);
358
359             case Type.NUMBER:
360             case Type.INTEGER:
361             case Type.ATOMIC:
362             case Type.ITEM:
363                 return this;
364
365             case Type.NON_POSITIVE_INTEGER:
366             case Type.NEGATIVE_INTEGER:
367             case Type.LONG:
368             case Type.INT:
369             case Type.SHORT:
370             case Type.BYTE:
371             case Type.NON_NEGATIVE_INTEGER:
372             case Type.POSITIVE_INTEGER:
373             case Type.UNSIGNED_LONG:
374             case Type.UNSIGNED_INT:
375             case Type.UNSIGNED_SHORT:
376             case Type.UNSIGNED_BYTE:
377                 IntegerValue val = new IntegerValue(value);
378                 ValidationException err = val.convertToSubtype(requiredType, validate);
379                 if (err != null) {
380                     return new ValidationErrorValue(err);
381                 }
382                 return val;
383
384             case Type.DOUBLE:
385                 return new DoubleValue((double) value);
386
387             case Type.FLOAT:
388                 return new FloatValue((float) value);
389
390             case Type.DECIMAL:
391                 return new DecimalValue(value);
392
393             case Type.STRING:
394                 return new StringValue(getStringValue());
395
396             case Type.UNTYPED_ATOMIC:
397                 return new UntypedAtomicValue(getStringValue());
398
399             default:
400                 ValidationException err2 = new ValidationException("Cannot convert integer to " +
401                         requiredType.getDisplayName());
402                 //err.setXPathContext(context);
403
err2.setErrorCode("FORG0001");
404                 return new ValidationErrorValue(err2);
405         }
406     }
407
408     /**
409      * Get the value as a String
410      *
411      * @return a String representation of the value
412      */

413
414     public String JavaDoc getStringValue() {
415         return value + "";
416     }
417
418     /**
419      * Negate the value
420      *
421      * @return the result of inverting the sign of the value
422      */

423
424     public NumericValue negate() {
425         return new IntegerValue(-value);
426     }
427
428     /**
429      * Implement the XPath floor() function
430      * @return the integer value, unchanged
431      */

432
433     public NumericValue floor() {
434         return this;
435     }
436
437     /**
438      * Implement the XPath ceiling() function
439      * @return the integer value, unchanged
440      */

441
442     public NumericValue ceiling() {
443         return this;
444     }
445
446     /**
447      * Implement the XPath round() function
448      * @return the integer value, unchanged
449      */

450
451     public NumericValue round() {
452         return this;
453     }
454
455     /**
456      * Implement the XPath round-to-half-even() function
457      *
458      * @param scale number of digits required after the decimal point; the
459      * value -2 (for example) means round to a multiple of 100
460      * @return if the scale is >=0, return this value unchanged. Otherwise
461      * round it to a multiple of 10**-scale
462      */

463
464     public NumericValue roundToHalfEven(int scale) {
465         long absolute = Math.abs(value);
466         if (scale >= 0) {
467             return this;
468         } else {
469             if (scale < -15) {
470                 return new BigIntegerValue(value).roundToHalfEven(scale);
471             }
472             long factor = 1;
473             for (long i = 1; i <= -scale; i++) {
474                 factor *= 10;
475             }
476             long modulus = absolute % factor;
477             long rval = absolute - modulus;
478             long d = modulus * 2;
479             if (d > factor) {
480                 rval += factor;
481             } else if (d < factor) {
482                 // no-op
483
} else {
484                 // round to even
485
if (rval % (2 * factor) == 0) {
486                     // no-op
487
} else {
488                     rval += factor;
489                 }
490             }
491             if (value < 0) rval = -rval;
492             return new IntegerValue(rval);
493         }
494     }
495
496     /**
497      * Determine whether the value is negative, zero, or positive
498      * @return -1 if negative, 0 if zero, +1 if positive, NaN if NaN
499      */

500
501     public double signum() {
502         if (value > 0) return +1;
503         if (value == 0) return 0;
504         return -1;
505     }
506
507     /**
508      * Determine whether the value is a whole number, that is, whether it compares
509      * equal to some integer
510      *
511      * @return always true for this implementation
512      */

513
514     public boolean isWholeNumber() {
515         return true;
516     }
517
518     /**
519      * Evaluate a binary arithmetic operator.
520      *
521      * @param operator the operator to be applied, identified by a constant in
522      * the Tokenizer class
523      * @param other the other operand of the arithmetic expression
524      * @exception XPathException if an arithmetic failure occurs, e.g. divide
525      * by zero
526      * @return the result of performing the arithmetic operation
527      */

528
529     public NumericValue arithmetic(int operator, NumericValue other, XPathContext context) throws XPathException {
530
531         if (other instanceof IntegerValue) {
532             // if either of the values is large, we use BigInteger arithmetic to be on the safe side
533
if (value >= Integer.MAX_VALUE || value <= Integer.MIN_VALUE ||
534                     ((IntegerValue) other).value >= Integer.MAX_VALUE ||
535                     ((IntegerValue) other).value <= Integer.MIN_VALUE) {
536                 return new BigIntegerValue(value).arithmetic(operator, other, context);
537             }
538             switch (operator) {
539                 case Token.PLUS:
540                     return new IntegerValue(value + ((IntegerValue) other).value);
541                 case Token.MINUS:
542                     return new IntegerValue(value - ((IntegerValue) other).value);
543                 case Token.MULT:
544                     return new IntegerValue(value * ((IntegerValue) other).value);
545                 case Token.IDIV:
546                     try {
547                         return new IntegerValue(value / ((IntegerValue) other).value);
548                     } catch (ArithmeticException JavaDoc err) {
549                         DynamicError e;
550                         if ("/ by zero".equals(err.getMessage())) {
551                             e = new DynamicError("Integer division by zero");
552                             e.setErrorCode("FOAR0001");
553                         } else {
554                             e = new DynamicError("Integer division failure", err);
555                         }
556                         e.setXPathContext(context);
557                         throw e;
558                     }
559                 case Token.DIV:
560                     // the result of dividing two integers is a decimal; but if
561
// one divides exactly by the other, we implement it as an integer
562
long quotient = ((IntegerValue) other).value;
563                     if (quotient == 0) {
564                         DynamicError err = new DynamicError("Integer division by zero");
565                         err.setXPathContext(context);
566                         err.setErrorCode("FOAR0001");
567                         throw err;
568                     }
569                     if (value % quotient == 0) {
570                         return new IntegerValue(value / quotient);
571                     }
572                     return new DecimalValue(value).arithmetic(Token.DIV,
573                             new DecimalValue(quotient), context);
574                 case Token.MOD:
575                     return new IntegerValue(value % ((IntegerValue) other).value);
576                 default:
577                     throw new UnsupportedOperationException JavaDoc("Unknown operator");
578             }
579         } else if (other instanceof BigIntegerValue) {
580             return new BigIntegerValue(value).arithmetic(operator, other, context);
581         } else {
582             NumericValue v = (NumericValue) convert(other.getItemType().getPrimitiveType(), context);
583             return v.arithmetic(operator, other, context);
584         }
585     }
586
587     /**
588      * Determine the data type of the expression
589      * @return the actual data type
590      */

591
592     public ItemType getItemType() {
593         return type;
594     }
595
596     /**
597      * Get conversion preference for this value to a Java class.
598      *
599      * @param required the Java class to which conversion is required
600      * @return the conversion preference. A low result indicates higher
601      * preference.
602      */

603
604     // Note: this table gives java Long preference over Integer, even if the
605
// XML Schema type is xs:int
606

607     /**
608      * Convert to Java object (for passing to external functions)
609      *
610      * @param target The Java class to which conversion is required
611      * @exception XPathException if conversion is not possible, or fails
612      * @return the Java object that results from the conversion; always an
613      * instance of the target class
614      */

615
616     public Object JavaDoc convertToJava(Class JavaDoc target, XPathContext context) throws XPathException {
617         if (target == Object JavaDoc.class) {
618             return new Long JavaDoc(value);
619         } else if (target.isAssignableFrom(IntegerValue.class)) {
620             return this;
621         } else if (target == boolean.class) {
622             BooleanValue bval = (BooleanValue) convert(Type.BOOLEAN, context);
623             return Boolean.valueOf(bval.getBooleanValue());
624         } else if (target == Boolean JavaDoc.class) {
625             BooleanValue bval = (BooleanValue) convert(Type.BOOLEAN, context);
626             return Boolean.valueOf(bval.getBooleanValue());
627         } else if (target == String JavaDoc.class || target == CharSequence JavaDoc.class) {
628             return getStringValue();
629         } else if (target == double.class || target == Double JavaDoc.class) {
630             return new Double JavaDoc(value);
631         } else if (target == float.class || target == Float JavaDoc.class) {
632             return new Float JavaDoc(value);
633         } else if (target == long.class || target == Long JavaDoc.class) {
634             return new Long JavaDoc(value);
635         } else if (target == int.class || target == Integer JavaDoc.class) {
636             return new Integer JavaDoc((int) value);
637         } else if (target == short.class || target == Short JavaDoc.class) {
638             return new Short JavaDoc((short) value);
639         } else if (target == byte.class || target == Byte JavaDoc.class) {
640             return new Byte JavaDoc((byte) value);
641         } else if (target == char.class || target == Character JavaDoc.class) {
642             return new Character JavaDoc((char) value);
643         } else if (target == BigInteger JavaDoc.class) {
644             return BigInteger.valueOf(value);
645         } else if (target == BigDecimal JavaDoc.class) {
646             return BigDecimal.valueOf(value);
647         } else {
648             Object JavaDoc o = super.convertToJava(target, context);
649             if (o == null) {
650                 throw new DynamicError("Conversion of integer to " + target.getName() +
651                         " is not supported");
652             }
653             return o;
654         }
655     }
656
657 }
658
659 //
660
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
661
// you may not use this file except in compliance with the License. You may obtain a copy of the
662
// License at http://www.mozilla.org/MPL/
663
//
664
// Software distributed under the License is distributed on an "AS IS" basis,
665
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
666
// See the License for the specific language governing rights and limitations under the License.
667
//
668
// The Original Code is: all this file except the asStringXT() and zeros() methods (not currently used).
669
//
670
// The Initial Developer of the Original Code is Michael H. Kay.
671
//
672
// Portions created by (xt) are Copyright (C) (James Clark). All Rights Reserved.
673
//
674
// Contributor(s): none.
675
//
676

677
Popular Tags