001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.jexl2; 018 019 import java.lang.reflect.Array; 020 import java.lang.reflect.Field; 021 import java.math.BigDecimal; 022 import java.math.BigInteger; 023 024 /** 025 * Perform arithmetic. 026 * <p> 027 * All arithmetic operators (+, - , *, /, %) follow the same rules regarding their arguments. 028 * <ol> 029 * <li>If both are null, result is 0</li> 030 * <li>If either is a floating point number, coerce both to Double and perform operation</li> 031 * <li>If both are BigInteger, treat as BigInteger and perform operation</li> 032 * <li>If either is a BigDecimal, coerce both to BigDecimal and and perform operation</li> 033 * <li>Else treat as BigInteger, perform operation and attempt to narrow result: 034 * <ol> 035 * <li>if both arguments can be narrowed to Integer, narrow result to Integer</li> 036 * <li>if both arguments can be narrowed to Long, narrow result to Long</li> 037 * <li>Else return result as BigInteger</li> 038 * </ol> 039 * </li> 040 * </ol> 041 * </p> 042 * @since 2.0 043 */ 044 public class JexlArithmetic { 045 /** Double.MAX_VALUE as BigDecimal. */ 046 protected static final BigDecimal BIGD_DOUBLE_MAX_VALUE = BigDecimal.valueOf(Double.MAX_VALUE); 047 /** Double.MIN_VALUE as BigDecimal. */ 048 protected static final BigDecimal BIGD_DOUBLE_MIN_VALUE = BigDecimal.valueOf(Double.MIN_VALUE); 049 /** Long.MAX_VALUE as BigInteger. */ 050 protected static final BigInteger BIGI_LONG_MAX_VALUE = BigInteger.valueOf(Long.MAX_VALUE); 051 /** Long.MIN_VALUE as BigInteger. */ 052 protected static final BigInteger BIGI_LONG_MIN_VALUE = BigInteger.valueOf(Long.MIN_VALUE); 053 /** Whether this JexlArithmetic instance behaves in strict or lenient mode. */ 054 private boolean strict; 055 056 /** 057 * Creates a JexlArithmetic. 058 * @param lenient whether this arithmetic is lenient or strict 059 */ 060 public JexlArithmetic(boolean lenient) { 061 this.strict = !lenient; 062 } 063 064 /** 065 * Sets whether this JexlArithmetic instance triggers errors during evaluation when 066 * null is used as an operand. 067 * <p>This method is <em>not</em> thread safe; it may be called as an optional step by the JexlEngine 068 * in its initialization code before expression creation & evaluation.</p> 069 * @see JexlEngine#setSilent 070 * @see JexlEngine#setDebug 071 * @param lenient true means no JexlException will occur, false allows them 072 */ 073 void setLenient(boolean lenient) { 074 this.strict = !lenient; 075 } 076 077 /** 078 * Checks whether this JexlArithmetic instance triggers errors during evaluation 079 * when null is used as an operand. 080 * @return true if lenient, false if strict 081 */ 082 public boolean isLenient() { 083 return !this.strict; 084 } 085 086 /** 087 * The result of +,/,-,*,% when both operands are null. 088 * @return Integer(0) if lenient 089 * @throws NullPointerException if strict 090 */ 091 protected Object controlNullNullOperands() { 092 if (strict) { 093 throw new NullPointerException(JexlException.NULL_OPERAND); 094 } 095 return Integer.valueOf(0); 096 } 097 098 /** 099 * Throw a NPE if arithmetic is strict. 100 * @throws NullPointerException if strict 101 */ 102 protected void controlNullOperand() { 103 if (strict) { 104 throw new NullPointerException(JexlException.NULL_OPERAND); 105 } 106 } 107 108 /** 109 * Test if either left or right are either a Float or Double. 110 * @param left one object to test 111 * @param right the other 112 * @return the result of the test. 113 */ 114 protected boolean isFloatingPointType(Object left, Object right) { 115 return left instanceof Float || left instanceof Double || right instanceof Float || right instanceof Double; 116 } 117 118 /** 119 * Test if the passed value is a floating point number, i.e. a float, double 120 * or string with ( "." | "E" | "e"). 121 * 122 * @param val the object to be tested 123 * @return true if it is, false otherwise. 124 */ 125 protected boolean isFloatingPointNumber(Object val) { 126 if (val instanceof Float || val instanceof Double) { 127 return true; 128 } 129 if (val instanceof String) { 130 String string = (String) val; 131 return string.indexOf('.') != -1 || string.indexOf('e') != -1 || string.indexOf('E') != -1; 132 } 133 return false; 134 } 135 136 /** 137 * Is Object a floating point number. 138 * 139 * @param o Object to be analyzed. 140 * @return true if it is a Float or a Double. 141 */ 142 protected boolean isFloatingPoint(final Object o) { 143 return o instanceof Float || o instanceof Double; 144 } 145 146 /** 147 * Is Object a whole number. 148 * 149 * @param o Object to be analyzed. 150 * @return true if Integer, Long, Byte, Short or Character. 151 */ 152 protected boolean isNumberable(final Object o) { 153 return o instanceof Integer 154 || o instanceof Long 155 || o instanceof Byte 156 || o instanceof Short 157 || o instanceof Character; 158 } 159 160 /** 161 * Given a BigInteger, narrow it to an Integer or Long if it fits and the arguments 162 * class allow it. 163 * <p> 164 * The rules are: 165 * if either arguments is a BigInteger, no narrowing will occur 166 * if either arguments is a Long, no narrowing to Integer will occur 167 * </p> 168 * @param lhs the left hand side operand that lead to the bigi result 169 * @param rhs the right hand side operand that lead to the bigi result 170 * @param bigi the BigInteger to narrow 171 * @return an Integer or Long if narrowing is possible, the original BigInteger otherwise 172 */ 173 protected Number narrowBigInteger(Object lhs, Object rhs, BigInteger bigi) { 174 //coerce to long if possible 175 if (!(lhs instanceof BigInteger || rhs instanceof BigInteger) 176 && bigi.compareTo(BIGI_LONG_MAX_VALUE) <= 0 177 && bigi.compareTo(BIGI_LONG_MIN_VALUE) >= 0) { 178 // coerce to int if possible 179 long l = bigi.longValue(); 180 // coerce to int when possible (int being so often used in method parms) 181 if (!(lhs instanceof Long || rhs instanceof Long) 182 && l <= Integer.MAX_VALUE 183 && l >= Integer.MIN_VALUE) { 184 return Integer.valueOf((int) l); 185 } 186 return Long.valueOf(l); 187 } 188 return bigi; 189 } 190 191 /** 192 * Given an array of objects, attempt to type it more strictly. 193 * <ul> 194 * <li>If all objects are of the same type, the array returned will be an array of that same type</li> 195 * <li>If all objects are Numbers, the array returned will be an array of Numbers</li> 196 * <li>If all objects are convertible to a primitive type, the array returned will be an array 197 * of the primitive type</li> 198 * </ul> 199 * @param untyped an untyped array 200 * @return the original array if the attempt to strictly type the array fails, a typed array otherwise 201 */ 202 protected Object narrowArrayType(Object[] untyped) { 203 final int size = untyped.length; 204 Class<?> commonClass = null; 205 if (size > 0) { 206 // base common class on first entry 207 commonClass = untyped[0].getClass(); 208 final boolean isNumber = Number.class.isAssignableFrom(commonClass); 209 // for all children after first... 210 for (int i = 1; i < size; i++) { 211 Class<?> eclass = untyped[i].getClass(); 212 // detect same type for all elements in array 213 if (!Object.class.equals(commonClass) && !commonClass.equals(eclass)) { 214 // if both are numbers... 215 if (isNumber && Number.class.isAssignableFrom(eclass)) { 216 commonClass = Number.class; 217 } else { 218 commonClass = Object.class; 219 } 220 } 221 } 222 // convert array to the common class if not Object.class 223 if (!Object.class.equals(commonClass)) { 224 // if the commonClass has an equivalent primitive type, get it 225 if (isNumber) { 226 try { 227 Field TYPE = commonClass.getField("TYPE"); 228 commonClass = (Class<?>) TYPE.get(null); 229 } catch (Exception xany) { 230 // ignore 231 } 232 } 233 // allocate and fill up the typed array 234 Object typed = Array.newInstance(commonClass, size); 235 for(int i = 0; i < size; ++i) { 236 Array.set(typed, i, untyped[i]); 237 } 238 return typed; 239 } 240 } 241 return untyped; 242 } 243 244 /** 245 * Replace all numbers in an arguments array with the smallest type that will fit. 246 * @param args the argument array 247 * @return true if some arguments were narrowed and args array is modified, 248 * false if no narrowing occured and args array has not been modified 249 */ 250 protected boolean narrowArguments(Object[] args) { 251 boolean narrowed = false; 252 for (int a = 0; a < args.length; ++a) { 253 Object arg = args[a]; 254 if (arg instanceof Number) { 255 Object narg = narrow((Number) arg); 256 if (narg != arg) { 257 narrowed = true; 258 } 259 args[a] = narg; 260 } 261 } 262 return narrowed; 263 } 264 265 /** 266 * Add two values together. 267 * <p> 268 * If any numeric add fails on coercion to the appropriate type, 269 * treat as Strings and do concatenation. 270 * </p> 271 * @param left first value 272 * @param right second value 273 * @return left + right. 274 */ 275 public Object add(Object left, Object right) { 276 if (left == null && right == null) { 277 return controlNullNullOperands(); 278 } 279 280 try { 281 // if either are floating point (double or float) use double 282 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 283 double l = toDouble(left); 284 double r = toDouble(right); 285 return new Double(l + r); 286 } 287 288 // if both are bigintegers use that type 289 if (left instanceof BigInteger && right instanceof BigInteger) { 290 BigInteger l = toBigInteger(left); 291 BigInteger r = toBigInteger(right); 292 return l.add(r); 293 } 294 295 // if either are bigdecimal use that type 296 if (left instanceof BigDecimal || right instanceof BigDecimal) { 297 BigDecimal l = toBigDecimal(left); 298 BigDecimal r = toBigDecimal(right); 299 return l.add(r); 300 } 301 302 // otherwise treat as integers 303 BigInteger l = toBigInteger(left); 304 BigInteger r = toBigInteger(right); 305 BigInteger result = l.add(r); 306 return narrowBigInteger(left, right, result); 307 } catch (java.lang.NumberFormatException nfe) { 308 // Well, use strings! 309 return toString(left).concat(toString(right)); 310 } 311 } 312 313 /** 314 * Divide the left value by the right. 315 * @param left first value 316 * @param right second value 317 * @return left / right 318 * @throws ArithmeticException if right == 0 319 */ 320 public Object divide(Object left, Object right) { 321 if (left == null && right == null) { 322 return controlNullNullOperands(); 323 } 324 325 // if either are floating point (double or float) use double 326 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 327 double l = toDouble(left); 328 double r = toDouble(right); 329 if (r == 0.0) { 330 throw new ArithmeticException("/"); 331 } 332 return new Double(l / r); 333 } 334 335 // if both are bigintegers use that type 336 if (left instanceof BigInteger && right instanceof BigInteger) { 337 BigInteger l = toBigInteger(left); 338 BigInteger r = toBigInteger(right); 339 return l.divide(r); 340 } 341 342 // if either are bigdecimal use that type 343 if (left instanceof BigDecimal || right instanceof BigDecimal) { 344 BigDecimal l = toBigDecimal(left); 345 BigDecimal r = toBigDecimal(right); 346 BigDecimal d = l.divide(r); 347 return d; 348 } 349 350 // otherwise treat as integers 351 BigInteger l = toBigInteger(left); 352 BigInteger r = toBigInteger(right); 353 BigInteger result = l.divide(r); 354 return narrowBigInteger(left, right, result); 355 } 356 357 /** 358 * left value mod right. 359 * @param left first value 360 * @param right second value 361 * @return left mod right 362 * @throws ArithmeticException if right == 0.0 363 */ 364 public Object mod(Object left, Object right) { 365 if (left == null && right == null) { 366 return controlNullNullOperands(); 367 } 368 369 // if either are floating point (double or float) use double 370 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 371 double l = toDouble(left); 372 double r = toDouble(right); 373 if (r == 0.0) { 374 throw new ArithmeticException("%"); 375 } 376 return new Double(l % r); 377 } 378 379 // if both are bigintegers use that type 380 if (left instanceof BigInteger && right instanceof BigInteger) { 381 BigInteger l = toBigInteger(left); 382 BigInteger r = toBigInteger(right); 383 return l.mod(r); 384 } 385 386 // if either are bigdecimal use that type 387 if (left instanceof BigDecimal || right instanceof BigDecimal) { 388 BigDecimal l = toBigDecimal(left); 389 BigDecimal r = toBigDecimal(right); 390 BigDecimal remainder = l.remainder(r); 391 return remainder; 392 } 393 394 // otherwise treat as integers 395 BigInteger l = toBigInteger(left); 396 BigInteger r = toBigInteger(right); 397 BigInteger result = l.mod(r); 398 return narrowBigInteger(left, right, result); 399 } 400 401 /** 402 * Multiply the left value by the right. 403 * @param left first value 404 * @param right second value 405 * @return left * right. 406 */ 407 public Object multiply(Object left, Object right) { 408 if (left == null && right == null) { 409 return controlNullNullOperands(); 410 } 411 412 // if either are floating point (double or float) use double 413 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 414 double l = toDouble(left); 415 double r = toDouble(right); 416 return new Double(l * r); 417 } 418 419 // if both are bigintegers use that type 420 if (left instanceof BigInteger && right instanceof BigInteger) { 421 BigInteger l = toBigInteger(left); 422 BigInteger r = toBigInteger(right); 423 return l.multiply(r); 424 } 425 426 // if either are bigdecimal use that type 427 if (left instanceof BigDecimal || right instanceof BigDecimal) { 428 BigDecimal l = toBigDecimal(left); 429 BigDecimal r = toBigDecimal(right); 430 return l.multiply(r); 431 } 432 433 // otherwise treat as integers 434 BigInteger l = toBigInteger(left); 435 BigInteger r = toBigInteger(right); 436 BigInteger result = l.multiply(r); 437 return narrowBigInteger(left, right, result); 438 } 439 440 /** 441 * Subtract the right value from the left. 442 * @param left first value 443 * @param right second value 444 * @return left - right. 445 */ 446 public Object subtract(Object left, Object right) { 447 if (left == null && right == null) { 448 return controlNullNullOperands(); 449 } 450 451 // if either are floating point (double or float) use double 452 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 453 double l = toDouble(left); 454 double r = toDouble(right); 455 return new Double(l - r); 456 } 457 458 // if both are bigintegers use that type 459 if (left instanceof BigInteger && right instanceof BigInteger) { 460 BigInteger l = toBigInteger(left); 461 BigInteger r = toBigInteger(right); 462 return l.subtract(r); 463 } 464 465 // if either are bigdecimal use that type 466 if (left instanceof BigDecimal || right instanceof BigDecimal) { 467 BigDecimal l = toBigDecimal(left); 468 BigDecimal r = toBigDecimal(right); 469 return l.subtract(r); 470 } 471 472 // otherwise treat as integers 473 BigInteger l = toBigInteger(left); 474 BigInteger r = toBigInteger(right); 475 BigInteger result = l.subtract(r); 476 return narrowBigInteger(left, right, result); 477 } 478 479 /** 480 * Test if left regexp matches right. 481 * 482 * @param left first value 483 * @param right second value 484 * @return test result. 485 */ 486 public boolean matches(Object left, Object right) { 487 if (left == null && right == null) { 488 //if both are null L == R 489 return true; 490 } 491 if (left == null || right == null) { 492 // we know both aren't null, therefore L != R 493 return false; 494 } 495 final String arg = left.toString(); 496 if (right instanceof java.util.regex.Pattern) { 497 return ((java.util.regex.Pattern) right).matcher(arg).matches(); 498 } else { 499 return arg.matches(right.toString()); 500 } 501 } 502 503 /** 504 * Test if left and right are equal. 505 * 506 * @param left first value 507 * @param right second value 508 * @return test result. 509 */ 510 public boolean equals(Object left, Object right) { 511 if (left == null && right == null) { 512 /* 513 * if both are null L == R 514 */ 515 return true; 516 } else if (left == null || right == null) { 517 /* 518 * we know both aren't null, therefore L != R 519 */ 520 return false; 521 } else if (left.getClass().equals(right.getClass())) { 522 return left.equals(right); 523 } else if (left instanceof BigDecimal || right instanceof BigDecimal) { 524 return toBigDecimal(left).compareTo(toBigDecimal(right)) == 0; 525 } else if (isFloatingPointType(left, right)) { 526 return toDouble(left) == toDouble(right); 527 } else if (left instanceof Number || right instanceof Number || left instanceof Character 528 || right instanceof Character) { 529 return toLong(left) == toLong(right); 530 } else if (left instanceof Boolean || right instanceof Boolean) { 531 return toBoolean(left) == toBoolean(right); 532 } else if (left instanceof java.lang.String || right instanceof String) { 533 return left.toString().equals(right.toString()); 534 } 535 536 return left.equals(right); 537 } 538 539 540 /** 541 * Test if left < right. 542 * 543 * @param left first value 544 * @param right second value 545 * @return test result. 546 */ 547 public boolean lessThan(Object left, Object right) { 548 if ((left == right) || (left == null) || (right == null)) { 549 return false; 550 } else if (isFloatingPoint(left) || isFloatingPoint(right)) { 551 double leftDouble = toDouble(left); 552 double rightDouble = toDouble(right); 553 return leftDouble < rightDouble; 554 } else if (left instanceof BigDecimal || right instanceof BigDecimal) { 555 BigDecimal l = toBigDecimal(left); 556 BigDecimal r = toBigDecimal(right); 557 return l.compareTo(r) < 0; 558 } else if (isNumberable(left) || isNumberable(right)) { 559 long leftLong = toLong(left); 560 long rightLong = toLong(right); 561 return leftLong < rightLong; 562 } else if (left instanceof String || right instanceof String) { 563 String leftString = left.toString(); 564 String rightString = right.toString(); 565 return leftString.compareTo(rightString) < 0; 566 } else if (left instanceof Comparable<?>) { 567 @SuppressWarnings("unchecked") // OK because of instanceof check above 568 final Comparable<Object> comparable = (Comparable<Object>) left; 569 return comparable.compareTo(right) < 0; 570 } else if (right instanceof Comparable<?>) { 571 @SuppressWarnings("unchecked") // OK because of instanceof check above 572 final Comparable<Object> comparable = (Comparable<Object>) right; 573 return comparable.compareTo(left) > 0; 574 } 575 576 throw new IllegalArgumentException("Invalid comparison : comparing cardinality for left: " + left 577 + " and right: " + right); 578 579 } 580 581 /** 582 * Test if left > right. 583 * 584 * @param left first value 585 * @param right second value 586 * @return test result. 587 */ 588 public boolean greaterThan(Object left, Object right) { 589 if (left == null || right == null) { 590 return false; 591 } 592 return !equals(left, right) && !lessThan(left, right); 593 } 594 595 /** 596 * Test if left <= right. 597 * 598 * @param left first value 599 * @param right second value 600 * @return test result. 601 */ 602 public boolean lessThanOrEqual(Object left, Object right) { 603 return equals(left, right) || lessThan(left, right); 604 } 605 606 /** 607 * Test if left >= right. 608 * 609 * @param left first value 610 * @param right second value 611 * @return test result. 612 */ 613 public boolean greaterThanOrEqual(Object left, Object right) { 614 return equals(left, right) || greaterThan(left, right); 615 } 616 617 /** 618 * Coerce to a boolean (not a java.lang.Boolean). 619 * 620 * @param val Object to be coerced. 621 * @return The boolean coerced value, or false if none possible. 622 */ 623 public boolean toBoolean(Object val) { 624 if (val == null) { 625 controlNullOperand(); 626 return false; 627 } else if (val instanceof Boolean) { 628 return ((Boolean) val).booleanValue(); 629 } else if (val instanceof String) { 630 return Boolean.valueOf((String) val).booleanValue(); 631 } 632 // TODO: is this a reasonable default? 633 return false; 634 } 635 636 /** 637 * Coerce to a int. 638 * 639 * @param val Object to be coerced. 640 * @return The int coerced value. 641 */ 642 public int toInteger(Object val) { 643 if (val == null) { 644 controlNullOperand(); 645 return 0; 646 } else if (val instanceof String) { 647 if ("".equals(val)) { 648 return 0; 649 } 650 return Integer.parseInt((String) val); 651 } else if (val instanceof Character) { 652 return ((Character) val).charValue(); 653 } else if (val instanceof Boolean) { 654 throw new IllegalArgumentException("Boolean->Integer coercion exception"); 655 } else if (val instanceof Number) { 656 return ((Number) val).intValue(); 657 } 658 659 throw new IllegalArgumentException("Integer coercion exception. Can't coerce type: " 660 + val.getClass().getName()); 661 } 662 663 664 /** 665 * Coerce to a long (not a java.lang.Long). 666 * 667 * @param val Object to be coerced. 668 * @return The long coerced value. 669 */ 670 public long toLong(Object val) { 671 if (val == null) { 672 controlNullOperand(); 673 return 0L; 674 } else if (val instanceof String) { 675 if ("".equals(val)) { 676 return 0; 677 } 678 return Long.parseLong((String) val); 679 } else if (val instanceof Character) { 680 return ((Character) val).charValue(); 681 } else if (val instanceof Boolean) { 682 throw new NumberFormatException("Boolean->Long coercion exception"); 683 } else if (val instanceof Number) { 684 return ((Number) val).longValue(); 685 } 686 687 throw new NumberFormatException("Long coercion exception. Can't coerce type: " + val.getClass().getName()); 688 } 689 690 /** 691 * Get a BigInteger from the object passed. 692 * Null and empty string maps to zero. 693 * @param val the object to be coerced. 694 * @return a BigDecimal. 695 * @throws NullPointerException if val is null and mode is strict. 696 */ 697 public BigInteger toBigInteger(Object val) { 698 if (val instanceof BigInteger) { 699 return (BigInteger) val; 700 } else if (val == null) { 701 controlNullOperand(); 702 return BigInteger.valueOf(0); 703 } else if (val instanceof String) { 704 String string = (String) val; 705 if ("".equals(string.trim())) { 706 return BigInteger.ZERO; 707 } 708 return new BigInteger(string); 709 } else if (val instanceof Number) { 710 return new BigInteger(val.toString()); 711 } else if (val instanceof Character) { 712 int i = ((Character) val).charValue(); 713 return BigInteger.valueOf(i); 714 } 715 716 throw new IllegalArgumentException("BigInteger coercion exception. Can't coerce type: " 717 + val.getClass().getName()); 718 } 719 720 /** 721 * Get a BigDecimal from the object passed. 722 * Null and empty string maps to zero. 723 * @param val the object to be coerced. 724 * @return a BigDecimal. 725 * @throws NullPointerException if val is null and mode is strict. 726 */ 727 public BigDecimal toBigDecimal(Object val) { 728 if (val instanceof BigDecimal) { 729 return (BigDecimal) val; 730 } else if (val == null) { 731 controlNullOperand(); 732 return BigDecimal.ZERO; 733 } else if (val instanceof String) { 734 String string = (String) val; 735 if ("".equals(string.trim())) { 736 return BigDecimal.valueOf(0); 737 } 738 return new BigDecimal(string); 739 } else if (val instanceof Number) { 740 return new BigDecimal(val.toString()); 741 } else if (val instanceof Character) { 742 int i = ((Character) val).charValue(); 743 return new BigDecimal(i); 744 } 745 746 throw new IllegalArgumentException("BigDecimal coercion exception. Can't coerce type: " 747 + val.getClass().getName()); 748 } 749 750 /** 751 * Coerce to a double. 752 * 753 * @param val Object to be coerced. 754 * @return The double coerced value. 755 * @throws NullPointerException if val is null and mode is strict. 756 */ 757 public double toDouble(Object val) { 758 if (val == null) { 759 controlNullOperand(); 760 return 0; 761 } else if (val instanceof String) { 762 String string = (String) val; 763 if ("".equals(string.trim())) { 764 return 0; 765 } 766 // the spec seems to be iffy about this. Going to give it a wack anyway 767 return Double.parseDouble(string); 768 } else if (val instanceof Character) { 769 int i = ((Character) val).charValue(); 770 771 return i; 772 } else if (val instanceof Double) { 773 return ((Double) val).doubleValue(); 774 } else if (val instanceof Number) { 775 //The below construct is used rather than ((Number)val).doubleValue() to ensure 776 //equality between comparing new Double( 6.4 / 3 ) and the jexl expression of 6.4 / 3 777 return Double.parseDouble(String.valueOf(val)); 778 } else if (val instanceof Boolean) { 779 throw new IllegalArgumentException("Boolean->Double coercion exception"); 780 } 781 782 throw new IllegalArgumentException("Double coercion exception. Can't coerce type: " 783 + val.getClass().getName()); 784 } 785 786 787 /** 788 * Coerce to a string. 789 * 790 * @param val Object to be coerced. 791 * @return The String coerced value. 792 * @throws NullPointerException if val is null and mode is strict. 793 */ 794 public String toString(Object val) { 795 if (val == null) { 796 controlNullOperand(); 797 val = ""; 798 } 799 return val.toString(); 800 } 801 802 /** 803 * Given a Number, return back the value using the smallest type the result 804 * will fit into. This works hand in hand with parameter 'widening' in java 805 * method calls, e.g. a call to substring(int,int) with an int and a long 806 * will fail, but a call to substring(int,int) with an int and a short will 807 * succeed. 808 * 809 * @param original the original number. 810 * @return a value of the smallest type the original number will fit into. 811 */ 812 public Number narrow(Number original) { 813 if (original == null) { 814 return original; 815 } 816 Number result = original; 817 if (original instanceof BigDecimal) { 818 BigDecimal bigd = (BigDecimal) original; 819 // if it's bigger than a double it can't be narrowed 820 if (bigd.compareTo(BIGD_DOUBLE_MAX_VALUE) > 0) { 821 return original; 822 } 823 } 824 if (original instanceof Double || original instanceof Float || original instanceof BigDecimal) { 825 double value = original.doubleValue(); 826 if (value <= Float.MAX_VALUE && value >= Float.MIN_VALUE) { 827 result = Float.valueOf(result.floatValue()); 828 } 829 // else it fits in a double only 830 } else { 831 if (original instanceof BigInteger) { 832 BigInteger bigi = (BigInteger) original; 833 // if it's bigger than a Long it can't be narrowed 834 if (bigi.compareTo(BIGI_LONG_MAX_VALUE) > 0 835 || bigi.compareTo(BIGI_LONG_MIN_VALUE) < 0) { 836 return original; 837 } 838 } 839 long value = original.longValue(); 840 if (value <= Byte.MAX_VALUE && value >= Byte.MIN_VALUE) { 841 // it will fit in a byte 842 result = Byte.valueOf((byte) value); 843 } else if (value <= Short.MAX_VALUE && value >= Short.MIN_VALUE) { 844 result = Short.valueOf((short) value); 845 } else if (value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE) { 846 result = Integer.valueOf((int) value); 847 } 848 // else it fits in a long 849 } 850 return result; 851 } 852 853 }