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