1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xalan" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, Lotus
  53. * Development Corporation., http://www.lotus.com. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package org.apache.xpath.objects;
  58. import javax.xml.transform.TransformerException;
  59. import org.apache.xpath.ExpressionOwner;
  60. import org.apache.xpath.XPathContext;
  61. import org.apache.xpath.XPathVisitor;
  62. /**
  63. * <meta name="usage" content="general"/>
  64. * This class represents an XPath number, and is capable of
  65. * converting the number to other types, such as a string.
  66. */
  67. public class XNumber extends XObject
  68. {
  69. /** Value of the XNumber object.
  70. * @serial */
  71. double m_val;
  72. /**
  73. * Construct a XNodeSet object.
  74. *
  75. * @param d Value of the object
  76. */
  77. public XNumber(double d)
  78. {
  79. super();
  80. m_val = d;
  81. }
  82. /**
  83. * Construct a XNodeSet object.
  84. *
  85. * @param d Value of the object
  86. */
  87. public XNumber(Number num)
  88. {
  89. super();
  90. m_val = num.doubleValue();
  91. m_obj = num;
  92. }
  93. /**
  94. * Tell that this is a CLASS_NUMBER.
  95. *
  96. * @return node type CLASS_NUMBER
  97. */
  98. public int getType()
  99. {
  100. return CLASS_NUMBER;
  101. }
  102. /**
  103. * Given a request type, return the equivalent string.
  104. * For diagnostic purposes.
  105. *
  106. * @return type string "#NUMBER"
  107. */
  108. public String getTypeString()
  109. {
  110. return "#NUMBER";
  111. }
  112. /**
  113. * Cast result object to a number.
  114. *
  115. * @return the value of the XNumber object
  116. */
  117. public double num()
  118. {
  119. return m_val;
  120. }
  121. /**
  122. * Evaluate expression to a number.
  123. *
  124. * @return 0.0
  125. *
  126. * @throws javax.xml.transform.TransformerException
  127. */
  128. public double num(XPathContext xctxt)
  129. throws javax.xml.transform.TransformerException
  130. {
  131. return m_val;
  132. }
  133. /**
  134. * Cast result object to a boolean.
  135. *
  136. * @return false if the value is NaN or equal to 0.0
  137. */
  138. public boolean bool()
  139. {
  140. return (Double.isNaN(m_val) || (m_val == 0.0)) ? false : true;
  141. }
  142. // /**
  143. // * Cast result object to a string.
  144. // *
  145. // * @return "NaN" if the number is NaN, Infinity or -Infinity if
  146. // * the number is infinite or the string value of the number.
  147. // */
  148. // private static final int PRECISION = 16;
  149. // public String str()
  150. // {
  151. //
  152. // if (Double.isNaN(m_val))
  153. // {
  154. // return "NaN";
  155. // }
  156. // else if (Double.isInfinite(m_val))
  157. // {
  158. // if (m_val > 0)
  159. // return "Infinity";
  160. // else
  161. // return "-Infinity";
  162. // }
  163. //
  164. // long longVal = (long)m_val;
  165. // if ((double)longVal == m_val)
  166. // return Long.toString(longVal);
  167. //
  168. //
  169. // String s = Double.toString(m_val);
  170. // int len = s.length();
  171. //
  172. // if (s.charAt(len - 2) == '.' && s.charAt(len - 1) == '0')
  173. // {
  174. // return s.substring(0, len - 2);
  175. // }
  176. //
  177. // int exp = 0;
  178. // int e = s.indexOf('E');
  179. // if (e != -1)
  180. // {
  181. // exp = Integer.parseInt(s.substring(e + 1));
  182. // s = s.substring(0,e);
  183. // len = e;
  184. // }
  185. //
  186. // // Calculate Significant Digits:
  187. // // look from start of string for first digit
  188. // // look from end for last digit
  189. // // significant digits = end - start + (0 or 1 depending on decimal location)
  190. //
  191. // int decimalPos = -1;
  192. // int start = (s.charAt(0) == '-') ? 1 : 0;
  193. // findStart: for( ; start < len; start++ )
  194. // {
  195. // switch (s.charAt(start))
  196. // {
  197. // case '0':
  198. // break;
  199. // case '.':
  200. // decimalPos = start;
  201. // break;
  202. // default:
  203. // break findStart;
  204. // }
  205. // }
  206. // int end = s.length() - 1;
  207. // findEnd: for( ; end > start; end-- )
  208. // {
  209. // switch (s.charAt(end))
  210. // {
  211. // case '0':
  212. // break;
  213. // case '.':
  214. // decimalPos = end;
  215. // break;
  216. // default:
  217. // break findEnd;
  218. // }
  219. // }
  220. //
  221. // int sigDig = end - start;
  222. //
  223. // // clarify decimal location if it has not yet been found
  224. // if (decimalPos == -1)
  225. // decimalPos = s.indexOf('.');
  226. //
  227. // // if decimal is not between start and end, add one to sigDig
  228. // if (decimalPos < start || decimalPos > end)
  229. // ++sigDig;
  230. //
  231. // // reduce significant digits to PRECISION if necessary
  232. // if (sigDig > PRECISION)
  233. // {
  234. // // re-scale BigDecimal in order to get significant digits = PRECISION
  235. // BigDecimal num = new BigDecimal(s);
  236. // int newScale = num.scale() - (sigDig - PRECISION);
  237. // if (newScale < 0)
  238. // newScale = 0;
  239. // s = num.setScale(newScale, BigDecimal.ROUND_HALF_UP).toString();
  240. //
  241. // // remove trailing '0's; keep track of decimalPos
  242. // int truncatePoint = s.length();
  243. // while (s.charAt(--truncatePoint) == '0')
  244. // ;
  245. //
  246. // if (s.charAt(truncatePoint) == '.')
  247. // {
  248. // decimalPos = truncatePoint;
  249. // }
  250. // else
  251. // {
  252. // decimalPos = s.indexOf('.');
  253. // truncatePoint += 1;
  254. // }
  255. //
  256. // s = s.substring(0, truncatePoint);
  257. // len = s.length();
  258. // }
  259. //
  260. // // Account for exponent by adding zeros as needed
  261. // // and moving the decimal place
  262. //
  263. // if (exp == 0)
  264. // return s;
  265. //
  266. // start = 0;
  267. // String sign;
  268. // if (s.charAt(0) == '-')
  269. // {
  270. // sign = "-";
  271. // start++;
  272. // }
  273. // else
  274. // sign = "";
  275. //
  276. // String wholePart = s.substring(start, decimalPos);
  277. // String decimalPart = s.substring(decimalPos + 1);
  278. //
  279. // // get the number of digits right of the decimal
  280. // int decimalLen = decimalPart.length();
  281. //
  282. // if (exp >= decimalLen)
  283. // return sign + wholePart + decimalPart + zeros(exp - decimalLen);
  284. //
  285. // if (exp > 0)
  286. // return sign + wholePart + decimalPart.substring(0, exp) + "."
  287. // + decimalPart.substring(exp);
  288. //
  289. // return sign + "0." + zeros(-1 - exp) + wholePart + decimalPart;
  290. // }
  291. /**
  292. * Cast result object to a string.
  293. *
  294. * @return "NaN" if the number is NaN, Infinity or -Infinity if
  295. * the number is infinite or the string value of the number.
  296. */
  297. public String str()
  298. {
  299. if (Double.isNaN(m_val))
  300. {
  301. return "NaN";
  302. }
  303. else if (Double.isInfinite(m_val))
  304. {
  305. if (m_val > 0)
  306. return "Infinity";
  307. else
  308. return "-Infinity";
  309. }
  310. double num = m_val;
  311. String s = Double.toString(num);
  312. int len = s.length();
  313. if (s.charAt(len - 2) == '.' && s.charAt(len - 1) == '0')
  314. {
  315. s = s.substring(0, len - 2);
  316. if (s.equals("-0"))
  317. return "0";
  318. return s;
  319. }
  320. int e = s.indexOf('E');
  321. if (e < 0)
  322. return s;
  323. int exp = Integer.parseInt(s.substring(e + 1));
  324. String sign;
  325. if (s.charAt(0) == '-')
  326. {
  327. sign = "-";
  328. s = s.substring(1);
  329. --e;
  330. }
  331. else
  332. sign = "";
  333. int nDigits = e - 2;
  334. if (exp >= nDigits)
  335. return sign + s.substring(0, 1) + s.substring(2, e)
  336. + zeros(exp - nDigits);
  337. if (exp > 0)
  338. return sign + s.substring(0, 1) + s.substring(2, 2 + exp) + "."
  339. + s.substring(2 + exp, e);
  340. return sign + "0." + zeros(-1 - exp) + s.substring(0, 1)
  341. + s.substring(2, e);
  342. }
  343. /**
  344. * Return a string of '0' of the given length
  345. *
  346. *
  347. * @param n Length of the string to be returned
  348. *
  349. * @return a string of '0' with the given length
  350. */
  351. static private String zeros(int n)
  352. {
  353. if (n < 1)
  354. return "";
  355. char[] buf = new char[n];
  356. for (int i = 0; i < n; i++)
  357. {
  358. buf[i] = '0';
  359. }
  360. return new String(buf);
  361. }
  362. /**
  363. * Return a java object that's closest to the representation
  364. * that should be handed to an extension.
  365. *
  366. * @return The value of this XNumber as a Double object
  367. */
  368. public Object object()
  369. {
  370. if(null == m_obj)
  371. m_obj = new Double(m_val);
  372. return m_obj;
  373. }
  374. /**
  375. * Tell if two objects are functionally equal.
  376. *
  377. * @param obj2 Object to compare this to
  378. *
  379. * @return true if the two objects are equal
  380. *
  381. * @throws javax.xml.transform.TransformerException
  382. */
  383. public boolean equals(XObject obj2)
  384. {
  385. // In order to handle the 'all' semantics of
  386. // nodeset comparisons, we always call the
  387. // nodeset function.
  388. int t = obj2.getType();
  389. try
  390. {
  391. if (t == XObject.CLASS_NODESET)
  392. return obj2.equals(this);
  393. else if(t == XObject.CLASS_BOOLEAN)
  394. return obj2.bool() == bool();
  395. else
  396. return m_val == obj2.num();
  397. }
  398. catch(javax.xml.transform.TransformerException te)
  399. {
  400. throw new org.apache.xml.utils.WrappedRuntimeException(te);
  401. }
  402. }
  403. /**
  404. * Tell if this expression returns a stable number that will not change during
  405. * iterations within the expression. This is used to determine if a proximity
  406. * position predicate can indicate that no more searching has to occur.
  407. *
  408. *
  409. * @return true if the expression represents a stable number.
  410. */
  411. public boolean isStableNumber()
  412. {
  413. return true;
  414. }
  415. /**
  416. * @see XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
  417. */
  418. public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
  419. {
  420. visitor.visitNumberLiteral(owner, this);
  421. }
  422. }