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 */ 21package org.apache.xpath.objects; 22 23import org.apache.xpath.ExpressionOwner; 24import org.apache.xpath.XPathContext; 25import 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 */ 32public 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