001: package net.sf.saxon.value;
002:
003: import net.sf.saxon.expr.XPathContext;
004: import net.sf.saxon.trans.XPathException;
005: import net.sf.saxon.type.ItemType;
006: import net.sf.saxon.type.Type;
007: import net.sf.saxon.type.TypeHierarchy;
008:
009: /**
010: * NumericValue is an abstract superclass for IntegerValue, DecimalValue,
011: * FloatValue, and DoubleValue
012: */
013:
014: public abstract class NumericValue extends AtomicValue implements
015: Comparable {
016:
017: /**
018: * Get a numeric value by parsing a string; the type of numeric value depends
019: * on the lexical form of the string, following the rules for XPath numeric
020: * literals.
021: * @param in the input string
022: * @return a NumericValue representing the value of the string. Returns Double.NaN if the
023: * value cannot be parsed as a string.
024: */
025:
026: public static NumericValue parseNumber(String in) {
027: if (in.indexOf('e') >= 0 || in.indexOf('E') >= 0) {
028: try {
029: return new DoubleValue(Double.parseDouble(in));
030: } catch (NumberFormatException e) {
031: return DoubleValue.NaN;
032: }
033: } else if (in.indexOf('.') >= 0) {
034: AtomicValue v = DecimalValue.makeDecimalValue(in, true);
035: if (v instanceof ValidationErrorValue) {
036: return DoubleValue.NaN;
037: } else {
038: return (NumericValue) v;
039: }
040: } else {
041: AtomicValue v = IntegerValue.stringToInteger(in);
042: if (v instanceof ValidationErrorValue) {
043: return DoubleValue.NaN;
044: } else {
045: return (NumericValue) v;
046: }
047: }
048: }
049:
050: /**
051: * Get the numeric value as a double
052: *
053: * @return A double representing this numeric value; NaN if it cannot be
054: * converted
055: */
056: public double getDoubleValue() {
057: try {
058: return ((DoubleValue) convert(Type.DOUBLE, null))
059: .getDoubleValue();
060: } catch (XPathException err) {
061: return Double.NaN;
062: }
063: }
064:
065: /**
066: * Test whether the value is the double/float value NaN
067: */
068:
069: public boolean isNaN() {
070: return false;
071: }
072:
073: /**
074: * Test whether the value is an integer (an instance of a subtype of xs:integer)
075: */
076:
077: public static boolean isInteger(AtomicValue value) {
078: if (value instanceof IntegerValue) {
079: return true;
080: } else if (value instanceof BigIntegerValue) {
081: return true;
082: } else if (!value.hasBuiltInType()
083: && NumericValue.isInteger(value.getPrimitiveValue())) {
084: return true;
085: }
086: return false;
087: }
088:
089: /**
090: * Return the numeric value as a Java long.
091: *
092: * @exception net.sf.saxon.trans.XPathException if the value cannot be converted
093: * @return the numeric value as a Java long. This performs truncation
094: * towards zero.
095: */
096: public long longValue() throws XPathException {
097: return ((IntegerValue) convert(Type.INTEGER, null)).longValue();
098: }
099:
100: /**
101: * Change the sign of the number
102: *
103: * @return a value, of the same type as the original, with its sign
104: * inverted
105: */
106:
107: public abstract NumericValue negate();
108:
109: /**
110: * Implement the XPath floor() function
111: *
112: * @return a value, of the same type as that supplied, rounded towards
113: * minus infinity
114: */
115:
116: public abstract NumericValue floor();
117:
118: /**
119: * Implement the XPath ceiling() function
120: *
121: * @return a value, of the same type as that supplied, rounded towards
122: * plus infinity
123: */
124:
125: public abstract NumericValue ceiling();
126:
127: /**
128: * Implement the XPath round() function
129: *
130: * @return a value, of the same type as that supplied, rounded towards the
131: * nearest whole number (0.5 rounded up)
132: */
133:
134: public abstract NumericValue round();
135:
136: /**
137: * Implement the XPath 2.0 round-to-half-even() function
138: *
139: * @param scale the decimal position for rounding: e.g. 2 rounds to a
140: * multiple of 0.01, while -2 rounds to a multiple of 100
141: * @return a value, of the same type as the original, rounded towards the
142: * nearest multiple of 10**(-scale), with rounding towards the nearest
143: * even number if two values are equally near
144: */
145:
146: public abstract NumericValue roundToHalfEven(int scale);
147:
148: /**
149: * Determine whether the value is negative, zero, or positive
150: * @return -1 if negative, 0 if zero (including negative zero), +1 if positive, NaN if NaN
151: */
152:
153: public abstract double signum();
154:
155: /**
156: * Perform a binary arithmetic operation
157: *
158: * @param operator the binary arithmetic operation to be performed. Uses
159: * the constants defined in the Tokenizer class
160: * @param other the other operand
161: * @exception XPathException if an arithmetic error occurs
162: * @return the result of the arithmetic operation
163: * @see net.sf.saxon.expr.Tokenizer
164: */
165:
166: public abstract NumericValue arithmetic(int operator,
167: NumericValue other, XPathContext context)
168: throws XPathException;
169:
170: /**
171: * Determine whether the value is a whole number, that is, whether it compares
172: * equal to some integer
173: *
174: * @return true if the value is a whole number
175: */
176:
177: public abstract boolean isWholeNumber();
178:
179: /**
180: * Compare the value to another numeric value
181: *
182: * @exception ClassCastException if the other value is not a NumericValue
183: * (the parameter is declared as Object to satisfy the Comparable
184: * interface)
185: * @param other The other numeric value
186: * @return -1 if this one is the lower, 0 if they are numerically equal,
187: * +1 if this one is the higher.
188: */
189:
190: // This is the default implementation. Subclasses of number avoid the conversion to double
191: // when comparing with another number of the same type.
192: public int compareTo(Object other) {
193: if (other instanceof AtomicValue
194: && !((AtomicValue) other).hasBuiltInType()) {
195: return compareTo(((AtomicValue) other).getPrimitiveValue());
196: }
197: if (!(other instanceof NumericValue)) {
198: throw new ClassCastException(
199: "Numeric values are not comparable to "
200: + other.getClass());
201: }
202: double a = this .getDoubleValue();
203: double b = ((NumericValue) other).getDoubleValue();
204: if (a == b)
205: return 0;
206: if (a < b)
207: return -1;
208: return +1;
209: }
210:
211: /**
212: * The equals() function compares numeric equality among integers, decimals, floats, doubles, and
213: * their subtypes
214: *
215: * @param other the value to be compared with this one
216: * @return true if the two values are numerically equal
217: */
218:
219: public final boolean equals(Object other) {
220: return compareTo(other) == 0;
221: // Note: this is implementing the XPath definition of equality, not the XML Schema definition.
222: // However, the equals() function is probably used largely for XML Schema comparisons...
223: }
224:
225: /**
226: * Identify lowest common supertype of two numeric values for promotion purposes
227: *
228: * @param v1 the item type of the first operand
229: * @param v2 the item type of the second operand
230: * @param typeHierarchy
231: * @return the item type that should be used for arithmetic between
232: * operands of the two specified item types
233: */
234:
235: public static ItemType promote(ItemType v1, ItemType v2,
236: TypeHierarchy typeHierarchy) {
237: ItemType t1 = (typeHierarchy.isSubType(v1, Type.NUMBER_TYPE) ? v1
238: : Type.DOUBLE_TYPE);
239: ItemType t2 = (typeHierarchy.isSubType(v2, Type.NUMBER_TYPE) ? v2
240: : Type.DOUBLE_TYPE);
241:
242: if (t1 == t2)
243: return t1;
244:
245: if (t1 == Type.DOUBLE_TYPE || t2 == Type.DOUBLE_TYPE) {
246: return Type.DOUBLE_TYPE;
247: }
248:
249: if (t1 == Type.FLOAT_TYPE || t2 == Type.FLOAT_TYPE) {
250: return Type.FLOAT_TYPE;
251: }
252:
253: if (t1 == Type.DECIMAL_TYPE || t2 == Type.DECIMAL_TYPE) {
254: return Type.DECIMAL_TYPE;
255: }
256:
257: return Type.INTEGER_TYPE;
258: }
259:
260: /**
261: * hashCode() must be the same for two values that are equal. One
262: * way to ensure this is to convert the value to a double, and take the
263: * hashCode of the double. But this is expensive in the common case where
264: * we are comparing integers. So we adopt the rule: for values that are in
265: * the range of a Java Integer, we use the int value as the hashcode. For
266: * values outside that range, we convert to a double and take the hashCode of
267: * the double. This method needs to have a compatible implementation in
268: * each subclass.
269: *
270: * @return the hash code of the numeric value
271: */
272:
273: public abstract int hashCode();
274:
275: /**
276: * Produce a string representation of the value
277: * @return The result of casting the number to a string
278: */
279: public String toString() {
280: return getStringValue();
281: }
282:
283: }
284:
285: //
286: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
287: // you may not use this file except in compliance with the License. You may obtain a copy of the
288: // License at http://www.mozilla.org/MPL/
289: //
290: // Software distributed under the License is distributed on an "AS IS" basis,
291: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
292: // See the License for the specific language governing rights and limitations under the License.
293: //
294: // The Original Code is: all this file.
295: //
296: // The Initial Developer of the Original Code is Saxonica Limited
297: //
298: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
299: //
300: // Contributor(s): none
301: //
|