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