1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the  "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18/*
19 * $Id: XNumber.java 469368 2006-10-31 04:41:36Z minchau $
20 */
21package org.apache.xpath.objects;
22
23import org.apache.xpath.ExpressionOwner;
24import org.apache.xpath.XPathContext;
25import org.apache.xpath.XPathVisitor;
26
27/**
28 * This class represents an XPath number, and is capable of
29 * converting the number to other types, such as a string.
30 * @xsl.usage general
31 */
32public class XNumber extends XObject
33{
34    static final long serialVersionUID = -2720400709619020193L;
35
36  /** Value of the XNumber object.
37   *  @serial         */
38  double m_val;
39
40  /**
41   * Construct a XNodeSet object.
42   *
43   * @param d Value of the object
44   */
45  public XNumber(double d)
46  {
47    super();
48
49    m_val = d;
50  }
51
52  /**
53   * Construct a XNodeSet object.
54   *
55   * @param num Value of the object
56   */
57  public XNumber(Number num)
58  {
59
60    super();
61
62    m_val = num.doubleValue();
63    setObject(num);
64  }
65
66  /**
67   * Tell that this is a CLASS_NUMBER.
68   *
69   * @return node type CLASS_NUMBER
70   */
71  public int getType()
72  {
73    return CLASS_NUMBER;
74  }
75
76  /**
77   * Given a request type, return the equivalent string.
78   * For diagnostic purposes.
79   *
80   * @return type string "#NUMBER"
81   */
82  public String getTypeString()
83  {
84    return "#NUMBER";
85  }
86
87  /**
88   * Cast result object to a number.
89   *
90   * @return the value of the XNumber object
91   */
92  public double num()
93  {
94    return m_val;
95  }
96
97  /**
98   * Evaluate expression to a number.
99   *
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}
438