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 import java.math.MathContext; 024 025 /** 026 * Perform arithmetic. 027 * <p> 028 * All arithmetic operators (+, - , *, /, %) follow the same rules regarding their arguments. 029 * <ol> 030 * <li>If both are null, result is 0</li> 031 * <li>If either is a BigDecimal, coerce both to BigDecimal and and perform operation</li> 032 * <li>If either is a floating point number, coerce both to Double and perform operation</li> 033 * <li>If both are BigInteger, treat as BigInteger and perform operation</li> 034 * <li>Else treat as BigInteger, perform operation and attempt to narrow result: 035 * <ol> 036 * <li>if both arguments can be narrowed to Integer, narrow result to Integer</li> 037 * <li>if both arguments can be narrowed to Long, narrow result to Long</li> 038 * <li>Else return result as BigInteger</li> 039 * </ol> 040 * </li> 041 * </ol> 042 * </p> 043 * Note that the only exception throw by JexlArithmetic is ArithmeticException. 044 * @since 2.0 045 */ 046 public class JexlArithmetic { 047 /** Double.MAX_VALUE as BigDecimal. */ 048 protected static final BigDecimal BIGD_DOUBLE_MAX_VALUE = BigDecimal.valueOf(Double.MAX_VALUE); 049 /** Double.MIN_VALUE as BigDecimal. */ 050 protected static final BigDecimal BIGD_DOUBLE_MIN_VALUE = BigDecimal.valueOf(Double.MIN_VALUE); 051 /** Long.MAX_VALUE as BigInteger. */ 052 protected static final BigInteger BIGI_LONG_MAX_VALUE = BigInteger.valueOf(Long.MAX_VALUE); 053 /** Long.MIN_VALUE as BigInteger. */ 054 protected static final BigInteger BIGI_LONG_MIN_VALUE = BigInteger.valueOf(Long.MIN_VALUE); 055 /** 056 * Default BigDecimal scale. 057 * @since 2.1 058 */ 059 protected static final int BIGD_SCALE = -1; 060 /** Whether this JexlArithmetic instance behaves in strict or lenient mode. 061 * May be made final in a later version. 062 */ 063 private volatile boolean strict; 064 /** 065 * The big decimal math context. 066 * @since 2.1 067 */ 068 protected final MathContext mathContext; 069 /** 070 * The big decimal scale. 071 * @since 2.1 072 */ 073 protected final int mathScale; 074 075 /** 076 * Creates a JexlArithmetic. 077 * @param lenient whether this arithmetic is lenient or strict 078 */ 079 public JexlArithmetic(boolean lenient) { 080 this(lenient, MathContext.DECIMAL128, BIGD_SCALE); 081 } 082 083 /** 084 * Creates a JexlArithmetic. 085 * @param lenient whether this arithmetic is lenient or strict 086 * @param bigdContext the math context instance to use for +,-,/,*,% operations on big decimals. 087 * @param bigdScale the scale used for big decimals. 088 * @since 2.1 089 */ 090 public JexlArithmetic(boolean lenient, MathContext bigdContext, int bigdScale) { 091 this.strict = !lenient; 092 this.mathContext = bigdContext; 093 this.mathScale = bigdScale; 094 } 095 096 097 /** 098 * Sets whether this JexlArithmetic instance triggers errors during evaluation when 099 * null is used as an operand. 100 * <p>This method is <em>not</em> thread safe; it may be called as an optional step by the JexlEngine 101 * in its initialization code before expression creation & evaluation.</p> 102 * @see JexlEngine#setLenient 103 * @see JexlEngine#setSilent 104 * @see JexlEngine#setDebug 105 * @param flag true means no JexlException will occur, false allows them 106 * @deprecated as of 2.1 - may be removed in a later release 107 */ 108 @Deprecated 109 void setLenient(boolean flag) { 110 this.strict = !flag; 111 } 112 113 /** 114 * Checks whether this JexlArithmetic instance triggers errors during evaluation 115 * when null is used as an operand. 116 * @return true if lenient, false if strict 117 */ 118 public boolean isLenient() { 119 return !this.strict; 120 } 121 122 /** 123 * The MathContext instance used for +,-,/,*,% operations on big decimals. 124 * @return the math context 125 * @since 2.1 126 */ 127 public MathContext getMathContext() { 128 return mathContext; 129 } 130 131 /** 132 * The BigDecimal scale used for comparison and coercion operations. 133 * @return the scale 134 * @since 2.1 135 */ 136 public int getMathScale() { 137 return mathScale; 138 } 139 140 /** 141 * Ensure a big decimal is rounded by this arithmetic scale and rounding mode. 142 * @param number the big decimal to round 143 * @return the rounded big decimal 144 * @since 2.1 145 */ 146 public BigDecimal roundBigDecimal(final BigDecimal number) { 147 int mscale = getMathScale(); 148 if (mscale >= 0) { 149 return number.setScale(mscale, getMathContext().getRoundingMode()); 150 } else { 151 return number; 152 } 153 } 154 155 /** 156 * The result of +,/,-,*,% when both operands are null. 157 * @return Integer(0) if lenient 158 * @throws ArithmeticException if strict 159 */ 160 protected Object controlNullNullOperands() { 161 if (!isLenient()) { 162 throw new ArithmeticException(JexlException.NULL_OPERAND); 163 } 164 return Integer.valueOf(0); 165 } 166 167 /** 168 * Throw a NPE if arithmetic is strict. 169 * @throws ArithmeticException if strict 170 */ 171 protected void controlNullOperand() { 172 if (!isLenient()) { 173 throw new ArithmeticException(JexlException.NULL_OPERAND); 174 } 175 } 176 177 /** 178 * Test if either left or right are either a Float or Double. 179 * @param left one object to test 180 * @param right the other 181 * @return the result of the test. 182 */ 183 protected boolean isFloatingPointType(Object left, Object right) { 184 return left instanceof Float || left instanceof Double || right instanceof Float || right instanceof Double; 185 } 186 187 /** 188 * Test if the passed value is a floating point number, i.e. a float, double 189 * or string with ( "." | "E" | "e"). 190 * 191 * @param val the object to be tested 192 * @return true if it is, false otherwise. 193 */ 194 protected boolean isFloatingPointNumber(Object val) { 195 if (val instanceof Float || val instanceof Double) { 196 return true; 197 } 198 if (val instanceof String) { 199 String string = (String) val; 200 return string.indexOf('.') != -1 || string.indexOf('e') != -1 || string.indexOf('E') != -1; 201 } 202 return false; 203 } 204 205 /** 206 * Is Object a floating point number. 207 * 208 * @param o Object to be analyzed. 209 * @return true if it is a Float or a Double. 210 */ 211 protected boolean isFloatingPoint(final Object o) { 212 return o instanceof Float || o instanceof Double; 213 } 214 215 /** 216 * Is Object a whole number. 217 * 218 * @param o Object to be analyzed. 219 * @return true if Integer, Long, Byte, Short or Character. 220 */ 221 protected boolean isNumberable(final Object o) { 222 return o instanceof Integer 223 || o instanceof Long 224 || o instanceof Byte 225 || o instanceof Short 226 || o instanceof Character; 227 } 228 229 /** 230 * Given a BigInteger, narrow it to an Integer or Long if it fits and the arguments 231 * class allow it. 232 * <p> 233 * The rules are: 234 * if either arguments is a BigInteger, no narrowing will occur 235 * if either arguments is a Long, no narrowing to Integer will occur 236 * </p> 237 * @param lhs the left hand side operand that lead to the bigi result 238 * @param rhs the right hand side operand that lead to the bigi result 239 * @param bigi the BigInteger to narrow 240 * @return an Integer or Long if narrowing is possible, the original BigInteger otherwise 241 */ 242 protected Number narrowBigInteger(Object lhs, Object rhs, BigInteger bigi) { 243 //coerce to long if possible 244 if (!(lhs instanceof BigInteger || rhs instanceof BigInteger) 245 && bigi.compareTo(BIGI_LONG_MAX_VALUE) <= 0 246 && bigi.compareTo(BIGI_LONG_MIN_VALUE) >= 0) { 247 // coerce to int if possible 248 long l = bigi.longValue(); 249 // coerce to int when possible (int being so often used in method parms) 250 if (!(lhs instanceof Long || rhs instanceof Long) 251 && l <= Integer.MAX_VALUE 252 && l >= Integer.MIN_VALUE) { 253 return Integer.valueOf((int) l); 254 } 255 return Long.valueOf(l); 256 } 257 return bigi; 258 } 259 260 /** 261 * Given a BigDecimal, attempt to narrow it to an Integer or Long if it fits if 262 * one of the arguments is a numberable. 263 * 264 * @param lhs the left hand side operand that lead to the bigd result 265 * @param rhs the right hand side operand that lead to the bigd result 266 * @param bigd the BigDecimal to narrow 267 * @return an Integer or Long if narrowing is possible, the original BigInteger otherwise 268 * @since 2.1 269 */ 270 protected Number narrowBigDecimal(Object lhs, Object rhs, BigDecimal bigd) { 271 if (isNumberable(lhs) || isNumberable(rhs)) { 272 try { 273 long l = bigd.longValueExact(); 274 // coerce to int when possible (int being so often used in method parms) 275 if (l <= Integer.MAX_VALUE && l >= Integer.MIN_VALUE) { 276 return Integer.valueOf((int) l); 277 } else { 278 return Long.valueOf(l); 279 } 280 } catch (ArithmeticException xa) { 281 // ignore, no exact value possible 282 } 283 } 284 return bigd; 285 } 286 287 /** 288 * Given an array of objects, attempt to type it more strictly. 289 * <ul> 290 * <li>If all objects are of the same type, the array returned will be an array of that same type</li> 291 * <li>If all objects are Numbers, the array returned will be an array of Numbers</li> 292 * <li>If all objects are convertible to a primitive type, the array returned will be an array 293 * of the primitive type</li> 294 * </ul> 295 * @param untyped an untyped array 296 * @return the original array if the attempt to strictly type the array fails, a typed array otherwise 297 */ 298 protected Object narrowArrayType(Object[] untyped) { 299 final int size = untyped.length; 300 Class<?> commonClass = null; 301 if (size > 0) { 302 boolean isNumber = true; 303 // for all children after first... 304 for (int u = 0; u < size && !Object.class.equals(commonClass); ++u) { 305 if (untyped[u] != null) { 306 Class<?> eclass = untyped[u].getClass(); 307 // base common class on first non-null entry 308 if (commonClass == null) { 309 commonClass = eclass; 310 isNumber &= Number.class.isAssignableFrom(commonClass); 311 } else if (!commonClass.equals(eclass)) { 312 // if both are numbers... 313 if (isNumber && Number.class.isAssignableFrom(eclass)) { 314 commonClass = Number.class; 315 } else { 316 // attempt to find valid superclass 317 do { 318 eclass = eclass.getSuperclass(); 319 if (eclass == null) { 320 commonClass = Object.class; 321 break; 322 } 323 } while (!commonClass.isAssignableFrom(eclass)); 324 } 325 } 326 } else { 327 isNumber = false; 328 } 329 } 330 // convert array to the common class if not Object.class 331 if (commonClass != null && !Object.class.equals(commonClass)) { 332 // if the commonClass has an equivalent primitive type, get it 333 if (isNumber) { 334 try { 335 final Field type = commonClass.getField("TYPE"); 336 commonClass = (Class<?>) type.get(null); 337 } catch (Exception xany) { 338 // ignore 339 } 340 } 341 // allocate and fill up the typed array 342 Object typed = Array.newInstance(commonClass, size); 343 for (int i = 0; i < size; ++i) { 344 Array.set(typed, i, untyped[i]); 345 } 346 return typed; 347 } 348 } 349 return untyped; 350 } 351 352 /** 353 * Replace all numbers in an arguments array with the smallest type that will fit. 354 * @param args the argument array 355 * @return true if some arguments were narrowed and args array is modified, 356 * false if no narrowing occured and args array has not been modified 357 */ 358 protected boolean narrowArguments(Object[] args) { 359 boolean narrowed = false; 360 for (int a = 0; a < args.length; ++a) { 361 Object arg = args[a]; 362 if (arg instanceof Number) { 363 Object narg = narrow((Number) arg); 364 if (narg != arg) { 365 narrowed = true; 366 } 367 args[a] = narg; 368 } 369 } 370 return narrowed; 371 } 372 373 /** 374 * Add two values together. 375 * <p> 376 * If any numeric add fails on coercion to the appropriate type, 377 * treat as Strings and do concatenation. 378 * </p> 379 * @param left first value 380 * @param right second value 381 * @return left + right. 382 */ 383 public Object add(Object left, Object right) { 384 if (left == null && right == null) { 385 return controlNullNullOperands(); 386 } 387 388 try { 389 // if either are floating point (double or float) use double 390 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 391 double l = toDouble(left); 392 double r = toDouble(right); 393 return new Double(l + r); 394 } 395 396 // if either are bigdecimal use that type 397 if (left instanceof BigDecimal || right instanceof BigDecimal) { 398 BigDecimal l = toBigDecimal(left); 399 BigDecimal r = toBigDecimal(right); 400 BigDecimal result = l.add(r, getMathContext()); 401 return narrowBigDecimal(left, right, result); 402 } 403 404 // otherwise treat as integers 405 BigInteger l = toBigInteger(left); 406 BigInteger r = toBigInteger(right); 407 BigInteger result = l.add(r); 408 return narrowBigInteger(left, right, result); 409 } catch (java.lang.NumberFormatException nfe) { 410 // Well, use strings! 411 return toString(left).concat(toString(right)); 412 } 413 } 414 415 /** 416 * Divide the left value by the right. 417 * @param left first value 418 * @param right second value 419 * @return left / right 420 * @throws ArithmeticException if right == 0 421 */ 422 public Object divide(Object left, Object right) { 423 if (left == null && right == null) { 424 return controlNullNullOperands(); 425 } 426 427 // if either are floating point (double or float) use double 428 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 429 double l = toDouble(left); 430 double r = toDouble(right); 431 if (r == 0.0) { 432 throw new ArithmeticException("/"); 433 } 434 return new Double(l / r); 435 } 436 437 // if either are bigdecimal use that type 438 if (left instanceof BigDecimal || right instanceof BigDecimal) { 439 BigDecimal l = toBigDecimal(left); 440 BigDecimal r = toBigDecimal(right); 441 if (BigDecimal.ZERO.equals(r)) { 442 throw new ArithmeticException("/"); 443 } 444 BigDecimal result = l.divide(r, getMathContext()); 445 return narrowBigDecimal(left, right, result); 446 } 447 448 // otherwise treat as integers 449 BigInteger l = toBigInteger(left); 450 BigInteger r = toBigInteger(right); 451 if (BigInteger.ZERO.equals(r)) { 452 throw new ArithmeticException("/"); 453 } 454 BigInteger result = l.divide(r); 455 return narrowBigInteger(left, right, result); 456 } 457 458 /** 459 * left value mod right. 460 * @param left first value 461 * @param right second value 462 * @return left mod right 463 * @throws ArithmeticException if right == 0.0 464 */ 465 public Object mod(Object left, Object right) { 466 if (left == null && right == null) { 467 return controlNullNullOperands(); 468 } 469 470 // if either are floating point (double or float) use double 471 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 472 double l = toDouble(left); 473 double r = toDouble(right); 474 if (r == 0.0) { 475 throw new ArithmeticException("%"); 476 } 477 return new Double(l % r); 478 } 479 480 // if either are bigdecimal use that type 481 if (left instanceof BigDecimal || right instanceof BigDecimal) { 482 BigDecimal l = toBigDecimal(left); 483 BigDecimal r = toBigDecimal(right); 484 if (BigDecimal.ZERO.equals(r)) { 485 throw new ArithmeticException("%"); 486 } 487 BigDecimal remainder = l.remainder(r, getMathContext()); 488 return narrowBigDecimal(left, right, remainder); 489 } 490 491 // otherwise treat as integers 492 BigInteger l = toBigInteger(left); 493 BigInteger r = toBigInteger(right); 494 BigInteger result = l.mod(r); 495 if (BigInteger.ZERO.equals(r)) { 496 throw new ArithmeticException("%"); 497 } 498 return narrowBigInteger(left, right, result); 499 } 500 501 /** 502 * Multiply the left value by the right. 503 * @param left first value 504 * @param right second value 505 * @return left * right. 506 */ 507 public Object multiply(Object left, Object right) { 508 if (left == null && right == null) { 509 return controlNullNullOperands(); 510 } 511 512 // if either are floating point (double or float) use double 513 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 514 double l = toDouble(left); 515 double r = toDouble(right); 516 return new Double(l * r); 517 } 518 519 // if either are bigdecimal use that type 520 if (left instanceof BigDecimal || right instanceof BigDecimal) { 521 BigDecimal l = toBigDecimal(left); 522 BigDecimal r = toBigDecimal(right); 523 BigDecimal result = l.multiply(r, getMathContext()); 524 return narrowBigDecimal(left, right, result); 525 } 526 527 // otherwise treat as integers 528 BigInteger l = toBigInteger(left); 529 BigInteger r = toBigInteger(right); 530 BigInteger result = l.multiply(r); 531 return narrowBigInteger(left, right, result); 532 } 533 534 /** 535 * Subtract the right value from the left. 536 * @param left first value 537 * @param right second value 538 * @return left - right. 539 */ 540 public Object subtract(Object left, Object right) { 541 if (left == null && right == null) { 542 return controlNullNullOperands(); 543 } 544 545 // if either are floating point (double or float) use double 546 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 547 double l = toDouble(left); 548 double r = toDouble(right); 549 return new Double(l - r); 550 } 551 552 // if either are bigdecimal use that type 553 if (left instanceof BigDecimal || right instanceof BigDecimal) { 554 BigDecimal l = toBigDecimal(left); 555 BigDecimal r = toBigDecimal(right); 556 BigDecimal result = l.subtract(r, getMathContext()); 557 return narrowBigDecimal(left, right, result); 558 } 559 560 // otherwise treat as integers 561 BigInteger l = toBigInteger(left); 562 BigInteger r = toBigInteger(right); 563 BigInteger result = l.subtract(r); 564 return narrowBigInteger(left, right, result); 565 } 566 567 /** 568 * Negates a value (unary minus for numbers). 569 * @param val the value to negate 570 * @return the negated value 571 * @since 2.1 572 */ 573 public Object negate(Object val) { 574 if (val instanceof Integer) { 575 int valueAsInt = ((Integer) val).intValue(); 576 return Integer.valueOf(-valueAsInt); 577 } else if (val instanceof Double) { 578 double valueAsDouble = ((Double) val).doubleValue(); 579 return new Double(-valueAsDouble); 580 } else if (val instanceof Long) { 581 long valueAsLong = -((Long) val).longValue(); 582 return Long.valueOf(valueAsLong); 583 } else if (val instanceof BigDecimal) { 584 BigDecimal valueAsBigD = (BigDecimal) val; 585 return valueAsBigD.negate(); 586 } else if (val instanceof BigInteger) { 587 BigInteger valueAsBigI = (BigInteger) val; 588 return valueAsBigI.negate(); 589 } else if (val instanceof Float) { 590 float valueAsFloat = ((Float) val).floatValue(); 591 return new Float(-valueAsFloat); 592 } else if (val instanceof Short) { 593 short valueAsShort = ((Short) val).shortValue(); 594 return Short.valueOf((short) -valueAsShort); 595 } else if (val instanceof Byte) { 596 byte valueAsByte = ((Byte) val).byteValue(); 597 return Byte.valueOf((byte) -valueAsByte); 598 } else if (val instanceof Boolean) { 599 return ((Boolean) val).booleanValue() ? Boolean.FALSE : Boolean.TRUE; 600 } 601 throw new ArithmeticException("Object negation:(" + val + ")"); 602 } 603 604 /** 605 * Test if left regexp matches right. 606 * 607 * @param left first value 608 * @param right second value 609 * @return test result. 610 * @since 2.1 611 */ 612 public boolean matches(Object left, Object right) { 613 if (left == null && right == null) { 614 //if both are null L == R 615 return true; 616 } 617 if (left == null || right == null) { 618 // we know both aren't null, therefore L != R 619 return false; 620 } 621 final String arg = left.toString(); 622 if (right instanceof java.util.regex.Pattern) { 623 return ((java.util.regex.Pattern) right).matcher(arg).matches(); 624 } else { 625 return arg.matches(right.toString()); 626 } 627 } 628 629 /** 630 * Performs a bitwise and. 631 * @param left the left operand 632 * @param right the right operator 633 * @return left & right 634 * @since 2.1 635 */ 636 public Object bitwiseAnd(Object left, Object right) { 637 long l = toLong(left); 638 long r = toLong(right); 639 return Long.valueOf(l & r); 640 } 641 642 /** 643 * Performs a bitwise or. 644 * @param left the left operand 645 * @param right the right operator 646 * @return left | right 647 * @since 2.1 648 */ 649 public Object bitwiseOr(Object left, Object right) { 650 long l = toLong(left); 651 long r = toLong(right); 652 return Long.valueOf(l | r); 653 } 654 655 /** 656 * Performs a bitwise xor. 657 * @param left the left operand 658 * @param right the right operator 659 * @return left right 660 * @since 2.1 661 */ 662 public Object bitwiseXor(Object left, Object right) { 663 long l = toLong(left); 664 long r = toLong(right); 665 return Long.valueOf(l ^ r); 666 } 667 668 /** 669 * Performs a bitwise complement. 670 * @param val the operand 671 * @return ~val 672 * @since 2.1 673 */ 674 public Object bitwiseComplement(Object val) { 675 long l = toLong(val); 676 return Long.valueOf(~l); 677 } 678 679 /** 680 * Performs a comparison. 681 * @param left the left operand 682 * @param right the right operator 683 * @param operator the operator 684 * @return -1 if left < right; +1 if left > > right; 0 if left == right 685 * @throws ArithmeticException if either left or right is null 686 * @since 2.1 687 */ 688 protected int compare(Object left, Object right, String operator) { 689 if (left != null && right != null) { 690 if (left instanceof BigDecimal || right instanceof BigDecimal) { 691 BigDecimal l = toBigDecimal(left); 692 BigDecimal r = toBigDecimal(right); 693 return l.compareTo(r); 694 } else if (left instanceof BigInteger || right instanceof BigInteger) { 695 BigInteger l = toBigInteger(left); 696 BigInteger r = toBigInteger(right); 697 return l.compareTo(r); 698 } else if (isFloatingPoint(left) || isFloatingPoint(right)) { 699 double lhs = toDouble(left); 700 double rhs = toDouble(right); 701 if (Double.isNaN(lhs)) { 702 if (Double.isNaN(rhs)) { 703 return 0; 704 } else { 705 return -1; 706 } 707 } else if (Double.isNaN(rhs)) { 708 // lhs is not NaN 709 return +1; 710 } else if (lhs < rhs) { 711 return -1; 712 } else if (lhs > rhs) { 713 return +1; 714 } else { 715 return 0; 716 } 717 } else if (isNumberable(left) || isNumberable(right)) { 718 long lhs = toLong(left); 719 long rhs = toLong(right); 720 if (lhs < rhs) { 721 return -1; 722 } else if (lhs > rhs) { 723 return +1; 724 } else { 725 return 0; 726 } 727 } else if (left instanceof String || right instanceof String) { 728 return toString(left).compareTo(toString(right)); 729 } else if ("==".equals(operator)) { 730 return left.equals(right) ? 0 : -1; 731 } else if (left instanceof Comparable<?>) { 732 @SuppressWarnings("unchecked") // OK because of instanceof check above 733 final Comparable<Object> comparable = (Comparable<Object>) left; 734 return comparable.compareTo(right); 735 } else if (right instanceof Comparable<?>) { 736 @SuppressWarnings("unchecked") // OK because of instanceof check above 737 final Comparable<Object> comparable = (Comparable<Object>) right; 738 return comparable.compareTo(left); 739 } 740 } 741 throw new ArithmeticException("Object comparison:(" + left + " " + operator + " " + right + ")"); 742 } 743 744 /** 745 * Test if left and right are equal. 746 * 747 * @param left first value 748 * @param right second value 749 * @return test result. 750 */ 751 public boolean equals(Object left, Object right) { 752 if (left == right) { 753 return true; 754 } else if (left == null || right == null) { 755 return false; 756 } else if (left instanceof Boolean || right instanceof Boolean) { 757 return toBoolean(left) == toBoolean(right); 758 } else { 759 return compare(left, right, "==") == 0; 760 } 761 } 762 763 /** 764 * Test if left < right. 765 * 766 * @param left first value 767 * @param right second value 768 * @return test result. 769 */ 770 public boolean lessThan(Object left, Object right) { 771 if ((left == right) || (left == null) || (right == null)) { 772 return false; 773 } else { 774 return compare(left, right, "<") < 0; 775 } 776 777 } 778 779 /** 780 * Test if left > right. 781 * 782 * @param left first value 783 * @param right second value 784 * @return test result. 785 */ 786 public boolean greaterThan(Object left, Object right) { 787 if ((left == right) || left == null || right == null) { 788 return false; 789 } else { 790 return compare(left, right, ">") > 0; 791 } 792 } 793 794 /** 795 * Test if left <= right. 796 * 797 * @param left first value 798 * @param right second value 799 * @return test result. 800 */ 801 public boolean lessThanOrEqual(Object left, Object right) { 802 if (left == right) { 803 return true; 804 } else if (left == null || right == null) { 805 return false; 806 } else { 807 return compare(left, right, "<=") <= 0; 808 } 809 } 810 811 /** 812 * Test if left >= right. 813 * 814 * @param left first value 815 * @param right second value 816 * @return test result. 817 */ 818 public boolean greaterThanOrEqual(Object left, Object right) { 819 if (left == right) { 820 return true; 821 } else if (left == null || right == null) { 822 return false; 823 } else { 824 return compare(left, right, ">=") >= 0; 825 } 826 } 827 828 /** 829 * Coerce to a boolean (not a java.lang.Boolean). 830 * 831 * @param val Object to be coerced. 832 * @return The boolean coerced value, or false if none possible. 833 */ 834 public boolean toBoolean(Object val) { 835 if (val == null) { 836 controlNullOperand(); 837 return false; 838 } else if (val instanceof Boolean) { 839 return ((Boolean) val).booleanValue(); 840 } else if (val instanceof Number) { 841 double number = toDouble(val); 842 return !Double.isNaN(number) && number != 0.d; 843 } else if (val instanceof String) { 844 String strval = val.toString(); 845 return strval.length() > 0 && !"false".equals(strval); 846 } 847 // TODO: is this a reasonable default? 848 return false; 849 } 850 851 /** 852 * Coerce to a int. 853 * 854 * @param val Object to be coerced. 855 * @return The int coerced value. 856 */ 857 public int toInteger(Object val) { 858 if (val == null) { 859 controlNullOperand(); 860 return 0; 861 } else if (val instanceof Double) { 862 if (!Double.isNaN(((Double) val).doubleValue())) { 863 return 0; 864 } else { 865 return ((Double) val).intValue(); 866 } 867 } else if (val instanceof Number) { 868 return ((Number) val).intValue(); 869 } else if (val instanceof String) { 870 if ("".equals(val)) { 871 return 0; 872 } 873 return Integer.parseInt((String) val); 874 } else if (val instanceof Boolean) { 875 return ((Boolean) val).booleanValue() ? 1 : 0; 876 } else if (val instanceof Character) { 877 return ((Character) val).charValue(); 878 } 879 880 throw new ArithmeticException("Integer coercion: " 881 + val.getClass().getName() + ":(" + val + ")"); 882 } 883 884 /** 885 * Coerce to a long (not a java.lang.Long). 886 * 887 * @param val Object to be coerced. 888 * @return The long coerced value. 889 */ 890 public long toLong(Object val) { 891 if (val == null) { 892 controlNullOperand(); 893 return 0L; 894 } else if (val instanceof Double) { 895 if (!Double.isNaN(((Double) val).doubleValue())) { 896 return 0; 897 } else { 898 return ((Double) val).longValue(); 899 } 900 } else if (val instanceof Number) { 901 return ((Number) val).longValue(); 902 } else if (val instanceof String) { 903 if ("".equals(val)) { 904 return 0; 905 } else { 906 return Long.parseLong((String) val); 907 } 908 } else if (val instanceof Boolean) { 909 return ((Boolean) val).booleanValue() ? 1L : 0L; 910 } else if (val instanceof Character) { 911 return ((Character) val).charValue(); 912 } 913 914 throw new ArithmeticException("Long coercion: " 915 + val.getClass().getName() + ":(" + val + ")"); 916 } 917 918 /** 919 * Get a BigInteger from the object passed. 920 * Null and empty string maps to zero. 921 * @param val the object to be coerced. 922 * @return a BigDecimal. 923 * @throws NullPointerException if val is null and mode is strict. 924 */ 925 public BigInteger toBigInteger(Object val) { 926 if (val == null) { 927 controlNullOperand(); 928 return BigInteger.ZERO; 929 } else if (val instanceof BigInteger) { 930 return (BigInteger) val; 931 } else if (val instanceof Double) { 932 if (!Double.isNaN(((Double) val).doubleValue())) { 933 return new BigInteger(val.toString()); 934 } else { 935 return BigInteger.ZERO; 936 } 937 } else if (val instanceof Number) { 938 return new BigInteger(val.toString()); 939 } else if (val instanceof String) { 940 String string = (String) val; 941 if ("".equals(string.trim())) { 942 return BigInteger.ZERO; 943 } else { 944 return new BigInteger(string); 945 } 946 } else if (val instanceof Character) { 947 int i = ((Character) val).charValue(); 948 return BigInteger.valueOf(i); 949 } 950 951 throw new ArithmeticException("BigInteger coercion: " 952 + val.getClass().getName() + ":(" + val + ")"); 953 } 954 955 /** 956 * Get a BigDecimal from the object passed. 957 * Null and empty string maps to zero. 958 * @param val the object to be coerced. 959 * @return a BigDecimal. 960 * @throws NullPointerException if val is null and mode is strict. 961 */ 962 public BigDecimal toBigDecimal(Object val) { 963 if (val instanceof BigDecimal) { 964 return roundBigDecimal((BigDecimal) val); 965 } else if (val == null) { 966 controlNullOperand(); 967 return BigDecimal.ZERO; 968 } else if (val instanceof String) { 969 String string = ((String) val).trim(); 970 if ("".equals(string)) { 971 return BigDecimal.ZERO; 972 } 973 return roundBigDecimal(new BigDecimal(string, getMathContext())); 974 } else if (val instanceof Double) { 975 if (!Double.isNaN(((Double) val).doubleValue())) { 976 return roundBigDecimal(new BigDecimal(val.toString(), getMathContext())); 977 } else { 978 return BigDecimal.ZERO; 979 } 980 } else if (val instanceof Number) { 981 return roundBigDecimal(new BigDecimal(val.toString(), getMathContext())); 982 } else if (val instanceof Character) { 983 int i = ((Character) val).charValue(); 984 return new BigDecimal(i); 985 } 986 987 throw new ArithmeticException("BigDecimal coercion: " 988 + val.getClass().getName() + ":(" + val + ")"); 989 } 990 991 /** 992 * Coerce to a double. 993 * 994 * @param val Object to be coerced. 995 * @return The double coerced value. 996 * @throws NullPointerException if val is null and mode is strict. 997 */ 998 public double toDouble(Object val) { 999 if (val == null) { 1000 controlNullOperand(); 1001 return 0; 1002 } else if (val instanceof Double) { 1003 return ((Double) val).doubleValue(); 1004 } else if (val instanceof Number) { 1005 //The below construct is used rather than ((Number)val).doubleValue() to ensure 1006 //equality between comparing new Double( 6.4 / 3 ) and the jexl expression of 6.4 / 3 1007 return Double.parseDouble(String.valueOf(val)); 1008 } else if (val instanceof Boolean) { 1009 return ((Boolean) val).booleanValue() ? 1. : 0.; 1010 } else if (val instanceof String) { 1011 String string = ((String) val).trim(); 1012 if ("".equals(string)) { 1013 return Double.NaN; 1014 } else { 1015 // the spec seems to be iffy about this. Going to give it a wack anyway 1016 return Double.parseDouble(string); 1017 } 1018 } else if (val instanceof Character) { 1019 int i = ((Character) val).charValue(); 1020 return i; 1021 } 1022 1023 throw new ArithmeticException("Double coercion: " 1024 + val.getClass().getName() + ":(" + val + ")"); 1025 } 1026 1027 /** 1028 * Coerce to a string. 1029 * 1030 * @param val Object to be coerced. 1031 * @return The String coerced value. 1032 * @throws NullPointerException if val is null and mode is strict. 1033 */ 1034 public String toString(Object val) { 1035 if (val == null) { 1036 controlNullOperand(); 1037 return ""; 1038 } else if (val instanceof Double) { 1039 Double dval = (Double) val; 1040 if (Double.isNaN(dval.doubleValue())) { 1041 return ""; 1042 } else { 1043 return dval.toString(); 1044 } 1045 } else { 1046 return val.toString(); 1047 } 1048 } 1049 1050 /** 1051 * Given a Number, return back the value using the smallest type the result 1052 * will fit into. This works hand in hand with parameter 'widening' in java 1053 * method calls, e.g. a call to substring(int,int) with an int and a long 1054 * will fail, but a call to substring(int,int) with an int and a short will 1055 * succeed. 1056 * 1057 * @param original the original number. 1058 * @return a value of the smallest type the original number will fit into. 1059 */ 1060 public Number narrow(Number original) { 1061 return narrowNumber(original, null); 1062 } 1063 1064 /** 1065 * Whether we consider the narrow class as a potential candidate for narrowing the source. 1066 * @param narrow the target narrow class 1067 * @param source the orginal source class 1068 * @return true if attempt to narrow source to target is accepted 1069 * @since 2.1 1070 */ 1071 protected boolean narrowAccept(Class<?> narrow, Class<?> source) { 1072 return narrow == null || narrow.equals(source); 1073 } 1074 1075 /** 1076 * Given a Number, return back the value attempting to narrow it to a target class. 1077 * @param original the original number 1078 * @param narrow the attempted target class 1079 * @return the narrowed number or the source if no narrowing was possible 1080 * @since 2.1 1081 */ 1082 protected Number narrowNumber(Number original, Class<?> narrow) { 1083 if (original == null) { 1084 return original; 1085 } 1086 Number result = original; 1087 if (original instanceof BigDecimal) { 1088 BigDecimal bigd = (BigDecimal) original; 1089 // if it's bigger than a double it can't be narrowed 1090 if (bigd.compareTo(BIGD_DOUBLE_MAX_VALUE) > 0) { 1091 return original; 1092 } else { 1093 try { 1094 long l = bigd.longValueExact(); 1095 // coerce to int when possible (int being so often used in method parms) 1096 if (narrowAccept(narrow, Integer.class) 1097 && l <= Integer.MAX_VALUE 1098 && l >= Integer.MIN_VALUE) { 1099 return Integer.valueOf((int) l); 1100 } else if (narrowAccept(narrow, Long.class)) { 1101 return Long.valueOf(l); 1102 } 1103 } catch (ArithmeticException xa) { 1104 // ignore, no exact value possible 1105 } 1106 } 1107 } 1108 if (original instanceof Double || original instanceof Float || original instanceof BigDecimal) { 1109 double value = original.doubleValue(); 1110 if (narrowAccept(narrow, Float.class) 1111 && value <= Float.MAX_VALUE 1112 && value >= Float.MIN_VALUE) { 1113 result = Float.valueOf(result.floatValue()); 1114 } 1115 // else it fits in a double only 1116 } else { 1117 if (original instanceof BigInteger) { 1118 BigInteger bigi = (BigInteger) original; 1119 // if it's bigger than a Long it can't be narrowed 1120 if (bigi.compareTo(BIGI_LONG_MAX_VALUE) > 0 1121 || bigi.compareTo(BIGI_LONG_MIN_VALUE) < 0) { 1122 return original; 1123 } 1124 } 1125 long value = original.longValue(); 1126 if (narrowAccept(narrow, Byte.class) 1127 && value <= Byte.MAX_VALUE 1128 && value >= Byte.MIN_VALUE) { 1129 // it will fit in a byte 1130 result = Byte.valueOf((byte) value); 1131 } else if (narrowAccept(narrow, Short.class) 1132 && value <= Short.MAX_VALUE 1133 && value >= Short.MIN_VALUE) { 1134 result = Short.valueOf((short) value); 1135 } else if (narrowAccept(narrow, Integer.class) 1136 && value <= Integer.MAX_VALUE 1137 && value >= Integer.MIN_VALUE) { 1138 result = Integer.valueOf((int) value); 1139 } 1140 // else it fits in a long 1141 } 1142 return result; 1143 } 1144 }