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    }