Home | History | Annotate | Download | only in objects
      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    */
     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