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  */
21 package org.apache.xpath.objects;
22 
23 import org.apache.xpath.ExpressionOwner;
24 import org.apache.xpath.XPathContext;
25 import 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  */
32 public 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    */
XNumber(double d)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    */
XNumber(Number num)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    */
getType()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    */
getTypeString()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    */
num()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    */
num(XPathContext xctxt)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    */
bool()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    */
str()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    */
zeros(int n)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    */
object()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    */
equals(XObject obj2)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    */
isStableNumber()423   public boolean isStableNumber()
424   {
425     return true;
426   }
427 
428   /**
429    * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
430    */
callVisitors(ExpressionOwner owner, XPathVisitor visitor)431   public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
432   {
433   	visitor.visitNumberLiteral(owner, this);
434   }
435 
436 
437 }
438