001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: XNumber.java 469368 2006-10-31 04:41:36Z minchau $
020 */
021 package org.apache.xpath.objects;
022
023 import org.apache.xpath.ExpressionOwner;
024 import org.apache.xpath.XPathContext;
025 import org.apache.xpath.XPathVisitor;
026
027 /**
028 * This class represents an XPath number, and is capable of
029 * converting the number to other types, such as a string.
030 * @xsl.usage general
031 */
032 public class XNumber extends XObject
033 {
034 static final long serialVersionUID = -2720400709619020193L;
035
036 /** Value of the XNumber object.
037 * @serial */
038 double m_val;
039
040 /**
041 * Construct a XNodeSet object.
042 *
043 * @param d Value of the object
044 */
045 public XNumber(double d)
046 {
047 super();
048
049 m_val = d;
050 }
051
052 /**
053 * Construct a XNodeSet object.
054 *
055 * @param num Value of the object
056 */
057 public XNumber(Number num)
058 {
059
060 super();
061
062 m_val = num.doubleValue();
063 setObject(num);
064 }
065
066 /**
067 * Tell that this is a CLASS_NUMBER.
068 *
069 * @return node type CLASS_NUMBER
070 */
071 public int getType()
072 {
073 return CLASS_NUMBER;
074 }
075
076 /**
077 * Given a request type, return the equivalent string.
078 * For diagnostic purposes.
079 *
080 * @return type string "#NUMBER"
081 */
082 public String getTypeString()
083 {
084 return "#NUMBER";
085 }
086
087 /**
088 * Cast result object to a number.
089 *
090 * @return the value of the XNumber object
091 */
092 public double num()
093 {
094 return m_val;
095 }
096
097 /**
098 * Evaluate expression to a number.
099 *
100 * @return 0.0
101 *
102 * @throws javax.xml.transform.TransformerException
103 */
104 public double num(XPathContext xctxt)
105 throws javax.xml.transform.TransformerException
106 {
107
108 return m_val;
109 }
110
111 /**
112 * Cast result object to a boolean.
113 *
114 * @return false if the value is NaN or equal to 0.0
115 */
116 public boolean bool()
117 {
118 return (Double.isNaN(m_val) || (m_val == 0.0)) ? false : true;
119 }
120
121 // /**
122 // * Cast result object to a string.
123 // *
124 // * @return "NaN" if the number is NaN, Infinity or -Infinity if
125 // * the number is infinite or the string value of the number.
126 // */
127 // private static final int PRECISION = 16;
128 // public String str()
129 // {
130 //
131 // if (Double.isNaN(m_val))
132 // {
133 // return "NaN";
134 // }
135 // else if (Double.isInfinite(m_val))
136 // {
137 // if (m_val > 0)
138 // return "Infinity";
139 // else
140 // return "-Infinity";
141 // }
142 //
143 // long longVal = (long)m_val;
144 // if ((double)longVal == m_val)
145 // return Long.toString(longVal);
146 //
147 //
148 // String s = Double.toString(m_val);
149 // int len = s.length();
150 //
151 // if (s.charAt(len - 2) == '.' && s.charAt(len - 1) == '0')
152 // {
153 // return s.substring(0, len - 2);
154 // }
155 //
156 // int exp = 0;
157 // int e = s.indexOf('E');
158 // if (e != -1)
159 // {
160 // exp = Integer.parseInt(s.substring(e + 1));
161 // s = s.substring(0,e);
162 // len = e;
163 // }
164 //
165 // // Calculate Significant Digits:
166 // // look from start of string for first digit
167 // // look from end for last digit
168 // // significant digits = end - start + (0 or 1 depending on decimal location)
169 //
170 // int decimalPos = -1;
171 // int start = (s.charAt(0) == '-') ? 1 : 0;
172 // findStart: for( ; start < len; start++ )
173 // {
174 // switch (s.charAt(start))
175 // {
176 // case '0':
177 // break;
178 // case '.':
179 // decimalPos = start;
180 // break;
181 // default:
182 // break findStart;
183 // }
184 // }
185 // int end = s.length() - 1;
186 // findEnd: for( ; end > start; end-- )
187 // {
188 // switch (s.charAt(end))
189 // {
190 // case '0':
191 // break;
192 // case '.':
193 // decimalPos = end;
194 // break;
195 // default:
196 // break findEnd;
197 // }
198 // }
199 //
200 // int sigDig = end - start;
201 //
202 // // clarify decimal location if it has not yet been found
203 // if (decimalPos == -1)
204 // decimalPos = s.indexOf('.');
205 //
206 // // if decimal is not between start and end, add one to sigDig
207 // if (decimalPos < start || decimalPos > end)
208 // ++sigDig;
209 //
210 // // reduce significant digits to PRECISION if necessary
211 // if (sigDig > PRECISION)
212 // {
213 // // re-scale BigDecimal in order to get significant digits = PRECISION
214 // BigDecimal num = new BigDecimal(s);
215 // int newScale = num.scale() - (sigDig - PRECISION);
216 // if (newScale < 0)
217 // newScale = 0;
218 // s = num.setScale(newScale, BigDecimal.ROUND_HALF_UP).toString();
219 //
220 // // remove trailing '0's; keep track of decimalPos
221 // int truncatePoint = s.length();
222 // while (s.charAt(--truncatePoint) == '0')
223 // ;
224 //
225 // if (s.charAt(truncatePoint) == '.')
226 // {
227 // decimalPos = truncatePoint;
228 // }
229 // else
230 // {
231 // decimalPos = s.indexOf('.');
232 // truncatePoint += 1;
233 // }
234 //
235 // s = s.substring(0, truncatePoint);
236 // len = s.length();
237 // }
238 //
239 // // Account for exponent by adding zeros as needed
240 // // and moving the decimal place
241 //
242 // if (exp == 0)
243 // return s;
244 //
245 // start = 0;
246 // String sign;
247 // if (s.charAt(0) == '-')
248 // {
249 // sign = "-";
250 // start++;
251 // }
252 // else
253 // sign = "";
254 //
255 // String wholePart = s.substring(start, decimalPos);
256 // String decimalPart = s.substring(decimalPos + 1);
257 //
258 // // get the number of digits right of the decimal
259 // int decimalLen = decimalPart.length();
260 //
261 // if (exp >= decimalLen)
262 // return sign + wholePart + decimalPart + zeros(exp - decimalLen);
263 //
264 // if (exp > 0)
265 // return sign + wholePart + decimalPart.substring(0, exp) + "."
266 // + decimalPart.substring(exp);
267 //
268 // return sign + "0." + zeros(-1 - exp) + wholePart + decimalPart;
269 // }
270
271 /**
272 * Cast result object to a string.
273 *
274 * @return "NaN" if the number is NaN, Infinity or -Infinity if
275 * the number is infinite or the string value of the number.
276 */
277 public String str()
278 {
279
280 if (Double.isNaN(m_val))
281 {
282 return "NaN";
283 }
284 else if (Double.isInfinite(m_val))
285 {
286 if (m_val > 0)
287 return "Infinity";
288 else
289 return "-Infinity";
290 }
291
292 double num = m_val;
293 String s = Double.toString(num);
294 int len = s.length();
295
296 if (s.charAt(len - 2) == '.' && s.charAt(len - 1) == '0')
297 {
298 s = s.substring(0, len - 2);
299
300 if (s.equals("-0"))
301 return "0";
302
303 return s;
304 }
305
306 int e = s.indexOf('E');
307
308 if (e < 0)
309 {
310 if (s.charAt(len - 1) == '0')
311 return s.substring(0, len - 1);
312 else
313 return s;
314 }
315
316 int exp = Integer.parseInt(s.substring(e + 1));
317 String sign;
318
319 if (s.charAt(0) == '-')
320 {
321 sign = "-";
322 s = s.substring(1);
323
324 --e;
325 }
326 else
327 sign = "";
328
329 int nDigits = e - 2;
330
331 if (exp >= nDigits)
332 return sign + s.substring(0, 1) + s.substring(2, e)
333 + zeros(exp - nDigits);
334
335 // Eliminate trailing 0's - bugzilla 14241
336 while (s.charAt(e-1) == '0')
337 e--;
338
339 if (exp > 0)
340 return sign + s.substring(0, 1) + s.substring(2, 2 + exp) + "."
341 + s.substring(2 + exp, e);
342
343 return sign + "0." + zeros(-1 - exp) + s.substring(0, 1)
344 + s.substring(2, e);
345 }
346
347
348 /**
349 * Return a string of '0' of the given length
350 *
351 *
352 * @param n Length of the string to be returned
353 *
354 * @return a string of '0' with the given length
355 */
356 static private String zeros(int n)
357 {
358 if (n < 1)
359 return "";
360
361 char[] buf = new char[n];
362
363 for (int i = 0; i < n; i++)
364 {
365 buf[i] = '0';
366 }
367
368 return new String(buf);
369 }
370
371 /**
372 * Return a java object that's closest to the representation
373 * that should be handed to an extension.
374 *
375 * @return The value of this XNumber as a Double object
376 */
377 public Object object()
378 {
379 if(null == m_obj)
380 setObject(new Double(m_val));
381 return m_obj;
382 }
383
384 /**
385 * Tell if two objects are functionally equal.
386 *
387 * @param obj2 Object to compare this to
388 *
389 * @return true if the two objects are equal
390 *
391 * @throws javax.xml.transform.TransformerException
392 */
393 public boolean equals(XObject obj2)
394 {
395
396 // In order to handle the 'all' semantics of
397 // nodeset comparisons, we always call the
398 // nodeset function.
399 int t = obj2.getType();
400 try
401 {
402 if (t == XObject.CLASS_NODESET)
403 return obj2.equals(this);
404 else if(t == XObject.CLASS_BOOLEAN)
405 return obj2.bool() == bool();
406 else
407 return m_val == obj2.num();
408 }
409 catch(javax.xml.transform.TransformerException te)
410 {
411 throw new org.apache.xml.utils.WrappedRuntimeException(te);
412 }
413 }
414
415 /**
416 * Tell if this expression returns a stable number that will not change during
417 * iterations within the expression. This is used to determine if a proximity
418 * position predicate can indicate that no more searching has to occur.
419 *
420 *
421 * @return true if the expression represents a stable number.
422 */
423 public boolean isStableNumber()
424 {
425 return true;
426 }
427
428 /**
429 * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
430 */
431 public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
432 {
433 visitor.visitNumberLiteral(owner, this);
434 }
435
436
437 }