KickJava   Java API By Example, From Geeks To Geeks.

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


1 package net.sf.saxon.value;
2 import net.sf.saxon.Err;
3 import net.sf.saxon.ConversionContext;
4 import net.sf.saxon.expr.Token;
5 import net.sf.saxon.expr.XPathContext;
6 import net.sf.saxon.om.FastStringBuffer;
7 import net.sf.saxon.trans.DynamicError;
8 import net.sf.saxon.trans.XPathException;
9 import net.sf.saxon.type.BuiltInAtomicType;
10 import net.sf.saxon.type.ItemType;
11 import net.sf.saxon.type.Type;
12 import net.sf.saxon.type.ValidationException;
13
14 import java.math.BigDecimal JavaDoc;
15 import java.util.regex.Matcher JavaDoc;
16
17 /**
18 * A numeric (double precision floating point) value
19 */

20
21 public final class DoubleValue extends NumericValue {
22
23     public static final DoubleValue ZERO = new DoubleValue(0.0);
24     public static final DoubleValue NaN = new DoubleValue(Double.NaN);
25
26     private double value;
27
28     /**
29      * Constructor supplying a string
30      * @throws ValidationException if the string does not have the correct lexical form for a double.
31      * Note that the error will contain no error code or context information.
32      */

33
34     public DoubleValue(CharSequence JavaDoc val) throws ValidationException {
35         try {
36             value = Value.stringToNumber(val);
37         } catch (NumberFormatException JavaDoc e) {
38             throw new ValidationException("Cannot convert string " + Err.wrap(val, Err.VALUE) + " to a double");
39         }
40     }
41
42     /**
43     * Constructor supplying a double
44     * @param value the value of the NumericValue
45     */

46
47     public DoubleValue(double value) {
48         this.value = value;
49     }
50
51     /**
52      * Return this numeric value as a double
53      * @return the value as a double
54      */

55
56     public double getDoubleValue() {
57         return value;
58     }
59
60     /**
61      * Get the hashCode. This must conform to the rules for other NumericValue hashcodes
62      * @see NumericValue#hashCode
63      */

64
65     public int hashCode() {
66         if (value > Integer.MIN_VALUE && value < Integer.MAX_VALUE) {
67             return (int)value;
68         } else {
69             return new Double JavaDoc(value).hashCode();
70         }
71     }
72
73     /**
74      * Test whether the value is the double/float value NaN
75      */

76
77     public boolean isNaN() {
78         return Double.isNaN(value);
79     }
80
81     /**
82      * Get the effective boolean value
83      * @param context
84      * @return the effective boolean value (true unless the value is zero or NaN)
85      */

86     public boolean effectiveBooleanValue(XPathContext context) {
87         return (value!=0.0 && !Double.isNaN(value));
88     }
89
90     /**
91     * Convert to target data type
92     * @param requiredType an integer identifying the required atomic type
93     * @param conversion
94      * @return an AtomicValue, a value of the required type
95     */

96
97     public AtomicValue convertPrimitive(BuiltInAtomicType requiredType, boolean validate, ConversionContext conversion) {
98         switch(requiredType.getPrimitiveType()) {
99         case Type.BOOLEAN:
100             return BooleanValue.get(effectiveBooleanValue(null));
101         case Type.DOUBLE:
102         case Type.NUMBER:
103         case Type.ATOMIC:
104         case Type.ITEM:
105             return this;
106         case Type.INTEGER:
107             if (Double.isNaN(value)) {
108                 ValidationException err = new ValidationException("Cannot convert double NaN to an integer");
109                 //err.setXPathContext(context);
110
err.setErrorCode("FORG0001");
111                 return new ValidationErrorValue(err);
112             }
113             if (Double.isInfinite(value)) {
114                 ValidationException err = new ValidationException("Cannot convert double infinity to an integer");
115                 //err.setXPathContext(context);
116
err.setErrorCode("FORG0001");
117                 return new ValidationErrorValue(err);
118             }
119             if (value > Long.MAX_VALUE || value < Long.MIN_VALUE) {
120                 return new BigIntegerValue(new BigDecimal JavaDoc(value).toBigInteger());
121             }
122             return new IntegerValue((long)value);
123         case Type.DECIMAL:
124                 try {
125                     return new DecimalValue(value);
126                 } catch (ValidationException e) {
127                     return new ValidationErrorValue(e);
128                 }
129             case Type.FLOAT:
130             return new FloatValue((float)value);
131         case Type.STRING:
132             return new StringValue(getStringValueCS());
133         case Type.UNTYPED_ATOMIC:
134             return new UntypedAtomicValue(getStringValueCS());
135         default:
136             ValidationException err = new ValidationException("Cannot convert double to " +
137                                      requiredType.getDisplayName());
138             //err.setXPathContext(context);
139
err.setErrorCode("FORG0001");
140             return new ValidationErrorValue(err);
141         }
142     }
143
144     private static java.util.regex.Pattern JavaDoc nonExponentialPattern =
145             java.util.regex.Pattern.compile(
146                     "(-?[0-9])([0-9]+?)(0*)\\.([0-9]*)");
147
148     /**
149      * Convert the double to a string according to the XPath 2.0 rules
150      * @return the string value
151      */

152     public String JavaDoc getStringValue() {
153         return doubleToString(value, Double.toString(value));
154     }
155
156     /**
157      * Internal method used for conversion of a double or a float to a string
158      * @param value the actual value, converted if necessary to a double
159      * @param javaString the result of converting the float or double to a string using the Java conventions.
160      * This value is adjusted as necessary to cater for the differences between the Java and XPath rules. The
161      * number of digits in the string will reflect whether the number started life as a double or as a float.
162      * @return the value converted to a string, according to the XPath casting rules.
163      */

164
165     static String JavaDoc doubleToString(double value, String JavaDoc javaString) {
166         if (value==0.0) {
167             if (javaString.charAt(0) == '-') {
168                 return "-0";
169             } else {
170                 return "0";
171             }
172         }
173         if (Double.isInfinite(value)) {
174             return (value > 0 ? "INF" : "-INF");
175         }
176         if (Double.isNaN(value)) {
177             return "NaN";
178         }
179         double absval = Math.abs(value);
180         String JavaDoc s = javaString;
181         if (absval < 1.0e-6 || absval >= 1.0e+6) {
182             if (s.indexOf('E')<0) {
183                 // need to use scientific notation, but Java isn't using it
184
// (Java's cutoff is 1.0E7, while XPath's is 1.0E6)
185
// So we have for example -2000000.0 rather than -2.0e6
186
FastStringBuffer sb = new FastStringBuffer(32);
187                 Matcher JavaDoc matcher = nonExponentialPattern.matcher(s);
188                 if (matcher.matches()) {
189                     sb.append(matcher.group(1));
190                     sb.append('.');
191                     sb.append(matcher.group(2));
192                     String JavaDoc fraction = matcher.group(4);
193                     if ("0".equals(fraction)) {
194                         sb.append("E" + (matcher.group(2).length() + matcher.group(3).length()));
195                         return sb.toString();
196                     } else {
197                         sb.append(matcher.group(3));
198                         sb.append(matcher.group(4));
199                         sb.append("E" + (matcher.group(2).length() + matcher.group(3).length()));
200                         return sb.toString();
201                     }
202                 } else {
203                     // fallback, this shouldn't happen
204
return s;
205                 }
206             } else {
207                 return s;
208             }
209         }
210         int len = s.length();
211         if (s.endsWith("E0")) {
212             s = s.substring(0, len - 2);
213         }
214         if (s.endsWith(".0")) {
215             return s.substring(0, len - 2);
216         }
217         int e = s.indexOf('E');
218         if (e < 0) {
219             // For some reason, Double.toString() in Java can return strings such as "0.0040"
220
// so we remove any trailing zeros
221
while (s.charAt(len - 1) == '0' && s.charAt(len - 2) != '.') {
222                 s = s.substring(0, --len);
223             }
224             return s;
225         }
226         int exp = Integer.parseInt(s.substring(e + 1));
227         String JavaDoc sign;
228         if (s.charAt(0) == '-') {
229             sign = "-";
230             s = s.substring(1);
231             --e;
232         } else {
233             sign = "";
234         }
235         int nDigits = e - 2;
236         if (exp >= nDigits) {
237             return sign + s.substring(0, 1) + s.substring(2, e) + zeros(exp - nDigits);
238         } else if (exp > 0) {
239             return sign + s.substring(0, 1) + s.substring(2, 2 + exp) + '.' + s.substring(2 + exp, e);
240         } else {
241             while (s.charAt(e-1) == '0') e--;
242             return sign + "0." + zeros(-1 - exp) + s.substring(0, 1) + s.substring(2, e);
243         }
244     }
245
246     private static String JavaDoc zeros(int n) {
247         char[] buf = new char[n];
248         for (int i = 0; i < n; i++)
249             buf[i] = '0';
250         return new String JavaDoc(buf);
251     }
252
253     /**
254     * Determine the data type of the expression
255     * @return Type.DOUBLE
256     */

257
258     public ItemType getItemType() {
259         return Type.DOUBLE_TYPE;
260     }
261
262     /**
263     * Negate the value
264     */

265
266     public NumericValue negate() {
267         return new DoubleValue(-value);
268     }
269
270     /**
271     * Implement the XPath floor() function
272     */

273
274     public NumericValue floor() {
275         return new DoubleValue(Math.floor(value));
276     }
277
278     /**
279     * Implement the XPath ceiling() function
280     */

281
282     public NumericValue ceiling() {
283         return new DoubleValue(Math.ceil(value));
284     }
285
286     /**
287     * Implement the XPath round() function
288     */

289
290     public NumericValue round() {
291         if (Double.isNaN(value)) {
292             return this;
293         }
294         if (Double.isInfinite(value)) {
295             return this;
296         }
297         if (value == 0.0) {
298             return this; // handles the negative zero case
299
}
300         if (value > -0.5 && value < 0.0) {
301             return new DoubleValue(-0.0);
302         }
303         if (value > Long.MIN_VALUE && value < Long.MAX_VALUE) {
304             return new DoubleValue(Math.round(value));
305         }
306
307         // A double holds fewer significant digits than a long. Therefore,
308
// if the double is outside the range of a long, it cannot have
309
// any signficant digits after the decimal point. So in this
310
// case, we return the original value unchanged
311

312         return this;
313     }
314
315     /**
316     * Implement the XPath round-to-half-even() function
317     */

318
319     public NumericValue roundToHalfEven(int scale) {
320         if (Double.isNaN(value)) return this;
321         if (Double.isInfinite(value)) return this;
322         if (value==0.0) return this; // handles the negative zero case
323

324         // Convert to a scaled integer, by multiplying by 10^scale
325

326         double factor = Math.pow(10, scale+1);
327         double d = Math.abs(value * factor);
328
329         // Now apply any rounding needed, using the "round half to even" rule
330

331         double rem = d % 10;
332         if (rem > 5) {
333             d += (10-rem);
334         } else if (rem < 5){
335             d -= rem;
336         } else {
337             // round half to even - check the last bit
338
if ((d % 20) == 15) {
339                 d +=5 ;
340             } else {
341                 d -=5;
342             }
343         }
344
345         // Now convert back to the original magnitude
346

347         d /= factor;
348         if (value < 0) {
349             d = 0.0 -d;
350         }
351         return new DoubleValue(d);
352
353     }
354
355     /**
356      * Determine whether the value is negative, zero, or positive
357      * @return -1 if negative, 0 if zero (including negative zero), +1 if positive, NaN if NaN
358      */

359
360     public double signum() {
361         if (Double.isNaN(value)) {
362             return value;
363         }
364         if (value > 0) return 1;
365         if (value == 0) return 0;
366         return -1;
367     }
368
369     /**
370     * Determine whether the value is a whole number, that is, whether it compares
371     * equal to some integer
372     */

373
374     public boolean isWholeNumber() {
375         return value == Math.floor(value) && !Double.isInfinite(value);
376     }
377
378     /**
379     * Evaluate a binary arithmetic operator.
380     */

381
382     public NumericValue arithmetic(int operator, NumericValue other, XPathContext context) throws XPathException {
383         if (other instanceof DoubleValue) {
384             switch(operator) {
385                 case Token.PLUS:
386                     return new DoubleValue(value + ((DoubleValue)other).value);
387                 case Token.MINUS:
388                     return new DoubleValue(value - ((DoubleValue)other).value);
389                 case Token.MULT:
390                     return new DoubleValue(value * ((DoubleValue)other).value);
391                 case Token.DIV:
392                     return new DoubleValue(value / ((DoubleValue)other).value);
393                 case Token.IDIV:
394                     if (((DoubleValue)other).value == 0.0) {
395                         DynamicError e = new DynamicError("Integer division by zero");
396                         e.setErrorCode("FOAR0001");
397                         e.setXPathContext(context);
398                         throw e;
399                     }
400                     return (NumericValue)(new DoubleValue(value / ((DoubleValue)other).value).convert(Type.INTEGER, context));
401                 case Token.MOD:
402                     return new DoubleValue(value % ((DoubleValue)other).value);
403                 default:
404                     throw new UnsupportedOperationException JavaDoc("Unknown operator");
405             }
406         } else {
407             return arithmetic(operator, (DoubleValue)other.convert(Type.DOUBLE, context), context);
408         }
409     }
410
411     /**
412      * Compare two values for equality. This supports identity constraints in XML Schema,
413      * which allow list-valued elements and attributes to participate in key and uniqueness constraints.
414      * This method returns false if any error occurs during the comparison, or if any of the items
415      * in either sequence is a node rather than an atomic value. The default implementation of
416      * schemaEquals() is the same as equals(), but subclasses can override this.
417      */

418
419     public boolean schemaEquals(Value obj) {
420         if (obj instanceof AtomicValue) {
421             obj = ((AtomicValue)obj).getPrimitiveValue();
422         }
423         if (obj instanceof DoubleValue) {
424             return value == ((DoubleValue)obj).value;
425         } else {
426             return false;
427         }
428     }
429
430
431     /**
432     * Convert to Java object (for passing to external functions)
433     */

434
435     public Object JavaDoc convertToJava(Class JavaDoc target, XPathContext context) throws XPathException {
436         if (target==Object JavaDoc.class) {
437             return new Double JavaDoc(value);
438         } else if (target.isAssignableFrom(DoubleValue.class)) {
439             return this;
440         } else if (target==boolean.class) {
441             return Boolean.valueOf(effectiveBooleanValue(context));
442         } else if (target==Boolean JavaDoc.class) {
443             return Boolean.valueOf(effectiveBooleanValue(context));
444         } else if (target==String JavaDoc.class || target==CharSequence JavaDoc.class) {
445             return getStringValue();
446         } else if (target==double.class || target==Double JavaDoc.class) {
447             return new Double JavaDoc(value);
448         } else if (target==float.class ||target==Float JavaDoc.class ) {
449             return new Float JavaDoc(value);
450         } else if (target==long.class || target==Long JavaDoc.class) {
451             return new Long JavaDoc((long)value);
452         } else if (target==int.class || target==Integer JavaDoc.class) {
453             return new Integer JavaDoc((int)value);
454         } else if (target==short.class || target==Short JavaDoc.class) {
455             return new Short JavaDoc((short)value);
456         } else if (target==byte.class || target==Byte JavaDoc.class) {
457             return new Byte JavaDoc((byte)value);
458         } else if (target==char.class || target==Character JavaDoc.class) {
459             return new Character JavaDoc((char)value);
460         } else {
461             Object JavaDoc o = super.convertToJava(target, context);
462             if (o == null) {
463                 DynamicError err = new DynamicError("Conversion of double to " + target.getName() +
464                         " is not supported");
465                 err.setXPathContext(context);
466                 err.setErrorCode("SAXON:0000");
467             }
468             return o;
469         }
470     }
471
472 // public static void main(String[] args) {
473
// double d = Double.parseDouble("-1.75e-3");
474
// System.err.println(d);
475
// BigDecimal dec = new BigDecimal(d);
476
// System.err.println(dec);
477
// }
478

479  }
480
481 //
482
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
483
// you may not use this file except in compliance with the License. You may obtain a copy of the
484
// License at http://www.mozilla.org/MPL/
485
//
486
// Software distributed under the License is distributed on an "AS IS" basis,
487
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
488
// See the License for the specific language governing rights and limitations under the License.
489
//
490
// The Original Code is: all this file except the asStringXT() and zeros() methods (not currently used).
491
//
492
// The Initial Developer of the Original Code is Michael H. Kay.
493
//
494
// Portions created by (xt) are Copyright (C) (James Clark). All Rights Reserved.
495
//
496
// Contributor(s): none.
497
//
498

499
Popular Tags