001: package net.sf.saxon.value;
002:
003: import net.sf.saxon.expr.Token;
004: import net.sf.saxon.expr.XPathContext;
005: import net.sf.saxon.trans.DynamicError;
006: import net.sf.saxon.trans.XPathException;
007: import net.sf.saxon.type.*;
008:
009: import java.math.BigDecimal;
010: import java.math.BigInteger;
011:
012: /**
013: * An integer value: note this is a subtype of decimal in XML Schema, not a primitive type.
014: * The class IntegerValue is used to represent xs:integer values that fit comfortably
015: * in a Java long; this class is used to represent xs:integer values outside this range,
016: * including the built-in subtype xs:unsignedLong
017: */
018:
019: public final class BigIntegerValue extends NumericValue {
020:
021: private BigInteger value;
022: private ItemType type;
023:
024: private static final BigInteger MAX_INT = BigInteger
025: .valueOf(Integer.MAX_VALUE);
026: private static final BigInteger MIN_INT = BigInteger
027: .valueOf(Integer.MIN_VALUE);
028: private static final BigInteger MAX_LONG = BigInteger
029: .valueOf(Long.MAX_VALUE);
030: private static final BigInteger MIN_LONG = BigInteger
031: .valueOf(Long.MIN_VALUE);
032: public static final BigInteger MAX_UNSIGNED_LONG = new BigInteger(
033: "18446744073709551615");
034: public static final BigIntegerValue ZERO = new BigIntegerValue(
035: BigInteger.ZERO);
036:
037: public BigIntegerValue(BigInteger value) {
038: this .value = value;
039: this .type = Type.INTEGER_TYPE;
040: }
041:
042: public BigIntegerValue(long value) {
043: this .value = BigInteger.valueOf(value);
044: this .type = Type.INTEGER_TYPE;
045: }
046:
047: /**
048: * This class allows subtypes of xs:integer to be held, as well as xs:integer values.
049: * This method sets the required type label. This method should only be used for values that
050: * @param type the subtype of integer required
051: * @return null if the operation succeeds, or a ValidationException if the value is out of range
052: */
053:
054: public ValidationException convertToSubType(AtomicType type,
055: boolean validate) {
056: if (!validate) {
057: this .type = type;
058: return null;
059: }
060: if (IntegerValue.checkBigRange(value, type)) {
061: this .type = type;
062: return null;
063: } else {
064: ValidationException err = new ValidationException(
065: "Integer value is out of range for subtype "
066: + type.getDisplayName());
067: err.setErrorCode("FORG0001");
068: return err;
069: }
070: }
071:
072: /**
073: * Factory method: makes either an IntegerValue or a BigIntegerValue depending on the value supplied
074: */
075:
076: public static NumericValue makeValue(BigInteger value) {
077: if (value.compareTo(MAX_LONG) > 0
078: || value.compareTo(MIN_LONG) < 0) {
079: return new BigIntegerValue(value);
080: } else {
081: return new IntegerValue(value.longValue());
082: }
083: }
084:
085: /**
086: * Get the hashCode. This must conform to the rules for other NumericValue hashcodes
087: * @see NumericValue#hashCode
088: */
089:
090: public int hashCode() {
091: if (value.compareTo(MIN_INT) >= 0
092: && value.compareTo(MAX_INT) <= 0) {
093: return value.intValue();
094: } else {
095: return new Double(this .getDoubleValue()).hashCode();
096: }
097: }
098:
099: /**
100: * Get the value as a long
101: *
102: * @return the value of the xs:integer, as a Java long
103: */
104:
105: public long longValue() {
106: return value.longValue();
107: }
108:
109: /**
110: * Get the value as a BigInteger
111: * @return teh value of the xs:integer as a Java BigInteger
112: */
113:
114: public BigInteger getBigInteger() {
115: return value;
116: }
117:
118: public boolean isWithinLongRange() {
119: return value.compareTo(MIN_LONG) >= 0
120: && value.compareTo(MAX_LONG) <= 0;
121: }
122:
123: public BigDecimal asDecimal() {
124: return new BigDecimal(value);
125: }
126:
127: /**
128: * Return the effective boolean value of this integer
129: *
130: * @param context The dynamic evaluation context; ignored in this
131: * implementation of the method
132: * @return false if the integer is zero, otherwise true
133: */
134: public boolean effectiveBooleanValue(XPathContext context) {
135: return value.compareTo(BigInteger.ZERO) != 0;
136: }
137:
138: /**
139: * Compare the value to another numeric value
140: *
141: * @param other the numeric value to be compared to this value
142: * @return -1 if this value is less than the other, 0 if they are equal,
143: * +1 if this value is greater
144: */
145:
146: public int compareTo(Object other) {
147: if (other instanceof BigIntegerValue) {
148: return value.compareTo(((BigIntegerValue) other).value);
149: } else if (other instanceof DecimalValue) {
150: return asDecimal().compareTo(
151: ((DecimalValue) other).getValue());
152: } else {
153: return super .compareTo(other);
154: }
155: }
156:
157: /**
158: * Compare two values for equality. This supports identity constraints in XML Schema,
159: * which allow list-valued elements and attributes to participate in key and uniqueness constraints.
160: * This method returns false if any error occurs during the comparison, or if any of the items
161: * in either sequence is a node rather than an atomic value. The default implementation of
162: * schemaEquals() is the same as equals(), but subclasses can override this.
163: */
164:
165: public boolean schemaEquals(Value obj) {
166: if (obj instanceof AtomicValue) {
167: obj = ((AtomicValue) obj).getPrimitiveValue();
168: }
169: if (obj instanceof DecimalValue) {
170: return obj.schemaEquals(this );
171: } else if (obj instanceof BigIntegerValue) {
172: return value.equals(((BigIntegerValue) obj).value);
173: } else if (obj instanceof IntegerValue) {
174: return equals(obj);
175: } else {
176: return false;
177: }
178: }
179:
180: /**
181: * Convert to target data type
182: *
183: * @param requiredType an integer identifying the required atomic type
184: * @param context
185: * @return an AtomicValue, a value of the required type; or an ErrorValue
186: */
187:
188: public AtomicValue convertPrimitive(BuiltInAtomicType requiredType,
189: boolean validate, XPathContext context) {
190: switch (requiredType.getFingerprint()) {
191: case Type.BOOLEAN:
192: return BooleanValue.get(effectiveBooleanValue(null));
193:
194: case Type.NUMBER:
195: case Type.INTEGER:
196: case Type.LONG:
197: case Type.ANY_ATOMIC:
198: case Type.ITEM:
199: return this ;
200:
201: case Type.NON_POSITIVE_INTEGER:
202: case Type.NEGATIVE_INTEGER:
203: case Type.INT:
204: case Type.SHORT:
205: case Type.BYTE:
206: case Type.NON_NEGATIVE_INTEGER:
207: case Type.POSITIVE_INTEGER:
208: case Type.UNSIGNED_INT:
209: case Type.UNSIGNED_SHORT:
210: case Type.UNSIGNED_BYTE:
211: if (isWithinLongRange()) {
212: IntegerValue val = new IntegerValue(longValue());
213: ValidationException err = val.convertToSubtype(
214: requiredType, validate);
215: if (err == null) {
216: return val;
217: } else {
218: return new ValidationErrorValue(err);
219: }
220: } else {
221: BigIntegerValue val = new BigIntegerValue(value);
222: ValidationException err = val.convertToSubType(
223: requiredType, validate);
224: if (err == null) {
225: return val;
226: } else {
227: return new ValidationErrorValue(err);
228: }
229: }
230:
231: case Type.UNSIGNED_LONG:
232: if (value.signum() < 0 || value.bitLength() > 64) {
233: ValidationException err = new ValidationException(
234: "Integer value is out of range for type "
235: + requiredType.getDisplayName());
236: err.setErrorCode("FORG0001");
237: //err.setXPathContext(context);
238: return new ValidationErrorValue(err);
239: } else if (isWithinLongRange()) {
240: IntegerValue val = new IntegerValue(longValue());
241: ValidationException err = val.convertToSubtype(
242: requiredType, validate);
243: if (err != null) {
244: return new ValidationErrorValue(err);
245: }
246: return val;
247: } else {
248: BigIntegerValue nv = new BigIntegerValue(value);
249: ValidationException err = nv.convertToSubType(
250: requiredType, validate);
251: if (err != null) {
252: return new ValidationErrorValue(err);
253: }
254: return nv;
255: }
256:
257: case Type.DOUBLE:
258: return new DoubleValue(value.doubleValue());
259:
260: case Type.FLOAT:
261: return new FloatValue(value.floatValue());
262:
263: case Type.DECIMAL:
264: return new DecimalValue(new BigDecimal(value));
265:
266: case Type.STRING:
267: return new StringValue(getStringValueCS());
268:
269: case Type.UNTYPED_ATOMIC:
270: return new UntypedAtomicValue(getStringValueCS());
271:
272: default:
273: ValidationException err = new ValidationException(
274: "Cannot convert integer to "
275: + requiredType.getDisplayName());
276: err.setErrorCode("XPTY0004");
277: err.setIsTypeError(true);
278: return new ValidationErrorValue(err);
279: }
280: }
281:
282: /**
283: * Get the value as a String
284: * @return a String representation of the value
285: */
286:
287: public String getStringValue() {
288: return value.toString();
289: }
290:
291: /**
292: * Negate the value
293: * @return the result of inverting the sign of the value
294: */
295:
296: public NumericValue negate() {
297: return new BigIntegerValue(value.negate());
298: }
299:
300: /**
301: * Implement the XPath floor() function
302: * @return the integer value, unchanged
303: */
304:
305: public NumericValue floor() {
306: return this ;
307: }
308:
309: /**
310: * Implement the XPath ceiling() function
311: * @return the integer value, unchanged
312: */
313:
314: public NumericValue ceiling() {
315: return this ;
316: }
317:
318: /**
319: * Implement the XPath round() function
320: * @return the integer value, unchanged
321: */
322:
323: public NumericValue round() {
324: return this ;
325: }
326:
327: /**
328: * Implement the XPath round-to-half-even() function
329: *
330: * @param scale number of digits required after the decimal point; the
331: * value -2 (for example) means round to a multiple of 100
332: * @return if the scale is >=0, return this value unchanged. Otherwise
333: * round it to a multiple of 10**-scale
334: */
335:
336: public NumericValue roundToHalfEven(int scale) {
337: if (scale >= 0) {
338: return this ;
339: } else {
340: BigInteger factor = BigInteger.valueOf(10).pow(-scale);
341: BigInteger[] pair = value.divideAndRemainder(factor);
342: int up = pair[1].compareTo(factor.divide(BigInteger
343: .valueOf(2)));
344: if (up > 0) {
345: // remainder is > .5
346: pair[0] = pair[0].add(BigInteger.valueOf(1));
347: } else if (up == 0) {
348: // remainder == .5
349: if (pair[0].mod(BigInteger.valueOf(2)).signum() != 0) {
350: // last digit of quotient is odd: make it even
351: pair[0] = pair[0].add(BigInteger.valueOf(1));
352: }
353: }
354: return makeValue(pair[0].multiply(factor));
355: }
356: }
357:
358: /**
359: * Determine whether the value is negative, zero, or positive
360: * @return -1 if negative, 0 if zero, +1 if positive, NaN if NaN
361: */
362:
363: public double signum() {
364: return value.signum();
365: }
366:
367: /**
368: * Determine whether the value is a whole number, that is, whether it compares
369: * equal to some integer
370: *
371: * @return always true for this implementation
372: */
373:
374: public boolean isWholeNumber() {
375: return true;
376: }
377:
378: /**
379: * Evaluate a binary arithmetic operator.
380: *
381: * @param operator the operator to be applied, identified by a constant in
382: * the Tokenizer class
383: * @param other the other operand of the arithmetic expression
384: * @exception XPathException if an arithmetic failure occurs, e.g. divide
385: * by zero
386: * @return the result of performing the arithmetic operation
387: */
388:
389: public NumericValue arithmetic(int operator, NumericValue other,
390: XPathContext context) throws XPathException {
391: if (other instanceof BigIntegerValue) {
392: BigInteger that = ((BigIntegerValue) other).value;
393: switch (operator) {
394: case Token.PLUS:
395: return makeValue(value.add(that));
396: case Token.MINUS:
397: return makeValue(value.subtract(that));
398: case Token.MULT:
399: return makeValue(value.multiply(that));
400: case Token.IDIV:
401: try {
402: return makeValue(value.divide(that));
403: } catch (ArithmeticException err) {
404: DynamicError e;
405: if ("/ by zero".equals(err.getMessage())) {
406: e = new DynamicError("Integer division by zero");
407: e.setErrorCode("FOAR0001");
408: } else {
409: e = new DynamicError(
410: "Integer division failure", err);
411: }
412: e.setXPathContext(context);
413: throw e;
414: }
415: case Token.DIV:
416: DecimalValue a = new DecimalValue(new BigDecimal(value));
417: DecimalValue b = new DecimalValue(new BigDecimal(that));
418: return a.arithmetic(operator, b, context);
419: case Token.MOD:
420: return makeValue(value.remainder(that));
421: default:
422: throw new UnsupportedOperationException(
423: "Unknown operator");
424: }
425: } else if (other instanceof IntegerValue) {
426: final BigIntegerValue val = new BigIntegerValue(other
427: .longValue());
428: return arithmetic(operator, val, context);
429: } else {
430: final TypeHierarchy th = context.getNamePool()
431: .getTypeHierarchy();
432: final NumericValue v = (NumericValue) convert(other
433: .getItemType(th).getPrimitiveType(), context);
434: return v.arithmetic(operator, other, context);
435: }
436: }
437:
438: /**
439: * Determine the data type of the expression
440: *
441: * @return the actual data type
442: * @param th
443: */
444:
445: public ItemType getItemType(TypeHierarchy th) {
446: return type;
447: }
448:
449: /**
450: * Convert to Java object (for passing to external functions)
451: *
452: * @param target The Java class to which conversion is required
453: * @exception XPathException if conversion is not possible, or fails
454: * @return the Java object that results from the conversion; always an
455: * instance of the target class
456: */
457:
458: public Object convertToJava(Class target, XPathContext context)
459: throws XPathException {
460: if (isWithinLongRange()) {
461: IntegerValue val = new IntegerValue(longValue());
462: return val.convertToJava(target, context);
463: } else if (target.isAssignableFrom(IntegerValue.class)) {
464: return this ;
465: } else if (target == BigInteger.class) {
466: return value;
467: } else {
468: return convert(Type.DECIMAL, context).convertToJava(target,
469: context);
470: }
471: }
472:
473: }
474:
475: //
476: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
477: // you may not use this file except in compliance with the License. You may obtain a copy of the
478: // License at http://www.mozilla.org/MPL/
479: //
480: // Software distributed under the License is distributed on an "AS IS" basis,
481: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
482: // See the License for the specific language governing rights and limitations under the License.
483: //
484: // The Original Code is: all this file except the asStringXT() and zeros() methods (not currently used).
485: //
486: // The Initial Developer of the Original Code is Michael H. Kay.
487: //
488: // Portions created by (xt) are Copyright (C) (James Clark). All Rights Reserved.
489: //
490: // Contributor(s): none.
491: //
|