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 018package org.apache.commons.jexl3; 019 020import org.apache.commons.jexl3.introspection.JexlMethod; 021 022import java.lang.reflect.Array; 023import java.lang.reflect.Constructor; 024import java.lang.reflect.InvocationTargetException; 025import java.math.BigDecimal; 026import java.math.BigInteger; 027import java.math.MathContext; 028import java.util.Collection; 029import java.util.Map; 030import java.util.concurrent.atomic.AtomicBoolean; 031import java.util.regex.Matcher; 032import java.util.regex.Pattern; 033 034/** 035 * Perform arithmetic, implements JexlOperator methods. 036 * 037 * <p>This is the class to derive to implement new operator behaviors.</p> 038 * 039 * <p>The 5 base arithmetic operators (+, - , *, /, %) follow the same evaluation rules regarding their arguments.</p> 040 * <ol> 041 * <li>If both are null, result is 0</li> 042 * <li>If either is a BigDecimal, coerce both to BigDecimal and perform operation</li> 043 * <li>If either is a floating point number, coerce both to Double and perform operation</li> 044 * <li>Else treat as BigInteger, perform operation and attempt to narrow result: 045 * <ol> 046 * <li>if both arguments can be narrowed to Integer, narrow result to Integer</li> 047 * <li>if both arguments can be narrowed to Long, narrow result to Long</li> 048 * <li>Else return result as BigInteger</li> 049 * </ol> 050 * </li> 051 * </ol> 052 * 053 * Note that the only exception thrown by JexlArithmetic is and must be ArithmeticException. 054 * 055 * @see JexlOperator 056 * @since 2.0 057 */ 058public class JexlArithmetic { 059 060 /** Marker class for null operand exceptions. */ 061 public static class NullOperand extends ArithmeticException {} 062 063 /** Double.MAX_VALUE as BigDecimal. */ 064 protected static final BigDecimal BIGD_DOUBLE_MAX_VALUE = BigDecimal.valueOf(Double.MAX_VALUE); 065 066 /** Double.MIN_VALUE as BigDecimal. */ 067 protected static final BigDecimal BIGD_DOUBLE_MIN_VALUE = BigDecimal.valueOf(Double.MIN_VALUE); 068 069 /** Long.MAX_VALUE as BigInteger. */ 070 protected static final BigInteger BIGI_LONG_MAX_VALUE = BigInteger.valueOf(Long.MAX_VALUE); 071 072 /** Long.MIN_VALUE as BigInteger. */ 073 protected static final BigInteger BIGI_LONG_MIN_VALUE = BigInteger.valueOf(Long.MIN_VALUE); 074 075 /** Default BigDecimal scale. */ 076 protected static final int BIGD_SCALE = -1; 077 078 /** Whether this JexlArithmetic instance behaves in strict or lenient mode. */ 079 private final boolean strict; 080 081 /** The big decimal math context. */ 082 private final MathContext mathContext; 083 084 /** The big decimal scale. */ 085 private final int mathScale; 086 087 /** The dynamic constructor. */ 088 private final Constructor<? extends JexlArithmetic> ctor; 089 090 /** 091 * Creates a JexlArithmetic. 092 * <p>If you derive your own arithmetic, implement the 093 * other constructor that may be needed when dealing with options. 094 * 095 * @param astrict whether this arithmetic is strict or lenient 096 */ 097 public JexlArithmetic(final boolean astrict) { 098 this(astrict, null, Integer.MIN_VALUE); 099 } 100 101 /** 102 * Creates a JexlArithmetic. 103 * <p>The constructor to define in derived classes. 104 * 105 * @param astrict whether this arithmetic is lenient or strict 106 * @param bigdContext the math context instance to use for +,-,/,*,% operations on big decimals. 107 * @param bigdScale the scale used for big decimals. 108 */ 109 public JexlArithmetic(final boolean astrict, final MathContext bigdContext, final int bigdScale) { 110 this.strict = astrict; 111 this.mathContext = bigdContext == null ? MathContext.DECIMAL128 : bigdContext; 112 this.mathScale = bigdScale == Integer.MIN_VALUE ? BIGD_SCALE : bigdScale; 113 Constructor<? extends JexlArithmetic> actor = null; 114 try { 115 actor = getClass().getConstructor(boolean.class, MathContext.class, int.class); 116 } catch (final Exception xany) { 117 // ignore 118 } 119 this.ctor = actor; 120 } 121 122 /** 123 * Apply options to this arithmetic which eventually may create another instance. 124 * @see #createWithOptions(boolean, java.math.MathContext, int) 125 * 126 * @param options the {@link JexlEngine.Options} to use 127 * @return an arithmetic with those options set 128 */ 129 public JexlArithmetic options(final JexlOptions options) { 130 if (options != null) { 131 final boolean ostrict = options.isStrictArithmetic(); 132 MathContext bigdContext = options.getMathContext(); 133 if (bigdContext == null) { 134 bigdContext = getMathContext(); 135 } 136 int bigdScale = options.getMathScale(); 137 if (bigdScale == Integer.MIN_VALUE) { 138 bigdScale = getMathScale(); 139 } 140 if (ostrict != isStrict() 141 || bigdScale != getMathScale() 142 || bigdContext != getMathContext()) { 143 return createWithOptions(ostrict, bigdContext, bigdScale); 144 } 145 } 146 return this; 147 } 148 149 /** 150 * Apply options to this arithmetic which eventually may create another instance. 151 * @see #createWithOptions(boolean, java.math.MathContext, int) 152 * 153 * @param options the {@link JexlEngine.Options} to use 154 * @return an arithmetic with those options set 155 * @deprecated 3.2 156 */ 157 @Deprecated 158 public JexlArithmetic options(final JexlEngine.Options options) { 159 if (options != null) { 160 Boolean ostrict = options.isStrictArithmetic(); 161 if (ostrict == null) { 162 ostrict = isStrict(); 163 } 164 MathContext bigdContext = options.getArithmeticMathContext(); 165 if (bigdContext == null) { 166 bigdContext = getMathContext(); 167 } 168 int bigdScale = options.getArithmeticMathScale(); 169 if (bigdScale == Integer.MIN_VALUE) { 170 bigdScale = getMathScale(); 171 } 172 if (ostrict != isStrict() 173 || bigdScale != getMathScale() 174 || bigdContext != getMathContext()) { 175 return createWithOptions(ostrict, bigdContext, bigdScale); 176 } 177 } 178 return this; 179 } 180 181 /** 182 * Apply options to this arithmetic which eventually may create another instance. 183 * @see #createWithOptions(boolean, java.math.MathContext, int) 184 * 185 * @param context the context that may extend {@link JexlContext.OptionsHandle} to use 186 * @return a new arithmetic instance or this 187 * @since 3.1 188 */ 189 public JexlArithmetic options(final JexlContext context) { 190 if (context instanceof JexlContext.OptionsHandle) { 191 return options(((JexlContext.OptionsHandle) context).getEngineOptions()); 192 } 193 if (context instanceof JexlEngine.Options) { 194 return options((JexlEngine.Options) context); 195 } 196 return this; 197 } 198 199 /** 200 * Creates a JexlArithmetic instance. 201 * Called by options(...) method when another instance of the same class of arithmetic is required. 202 * @see #options(org.apache.commons.jexl3.JexlEngine.Options) 203 * 204 * @param astrict whether this arithmetic is lenient or strict 205 * @param bigdContext the math context instance to use for +,-,/,*,% operations on big decimals. 206 * @param bigdScale the scale used for big decimals. 207 * @return default is a new JexlArithmetic instance 208 * @since 3.1 209 */ 210 protected JexlArithmetic createWithOptions(final boolean astrict, final MathContext bigdContext, final int bigdScale) { 211 if (ctor != null) { 212 try { 213 return ctor.newInstance(astrict, bigdContext, bigdScale); 214 } catch (IllegalAccessException | IllegalArgumentException 215 | InstantiationException | InvocationTargetException xany) { 216 // it was worth the try 217 } 218 } 219 return new JexlArithmetic(astrict, bigdContext, bigdScale); 220 } 221 222 /** 223 * The interface that uberspects JexlArithmetic classes. 224 * <p>This allows overloaded operator methods discovery.</p> 225 */ 226 public interface Uberspect { 227 /** 228 * Checks whether this uberspect has overloads for a given operator. 229 * 230 * @param operator the operator to check 231 * @return true if an overload exists, false otherwise 232 */ 233 boolean overloads(JexlOperator operator); 234 235 /** 236 * Gets the most specific method for an operator. 237 * 238 * @param operator the operator 239 * @param arg the arguments 240 * @return the most specific method or null if no specific override could be found 241 */ 242 JexlMethod getOperator(JexlOperator operator, Object... arg); 243 } 244 245 /** 246 * Helper interface used when creating an array literal. 247 * 248 * <p>The default implementation creates an array and attempts to type it strictly.</p> 249 * 250 * <ul> 251 * <li>If all objects are of the same type, the array returned will be an array of that same type</li> 252 * <li>If all objects are Numbers, the array returned will be an array of Numbers</li> 253 * <li>If all objects are convertible to a primitive type, the array returned will be an array 254 * of the primitive type</li> 255 * </ul> 256 */ 257 public interface ArrayBuilder { 258 259 /** 260 * Adds a literal to the array. 261 * 262 * @param value the item to add 263 */ 264 void add(Object value); 265 266 /** 267 * Creates the actual "array" instance. 268 * 269 * @param extended true when the last argument is ', ...' 270 * @return the array 271 */ 272 Object create(boolean extended); 273 } 274 275 /** 276 * Called by the interpreter when evaluating a literal array. 277 * 278 * @param size the number of elements in the array 279 * @return the array builder 280 */ 281 public ArrayBuilder arrayBuilder(final int size) { 282 return new org.apache.commons.jexl3.internal.ArrayBuilder(size); 283 } 284 285 /** 286 * Helper interface used when creating a set literal. 287 * <p>The default implementation creates a java.util.HashSet.</p> 288 */ 289 public interface SetBuilder { 290 /** 291 * Adds a literal to the set. 292 * 293 * @param value the item to add 294 */ 295 void add(Object value); 296 297 /** 298 * Creates the actual "set" instance. 299 * 300 * @return the set 301 */ 302 Object create(); 303 } 304 305 /** 306 * Called by the interpreter when evaluating a literal set. 307 * 308 * @param size the number of elements in the set 309 * @return the array builder 310 */ 311 public SetBuilder setBuilder(final int size) { 312 return new org.apache.commons.jexl3.internal.SetBuilder(size); 313 } 314 315 /** 316 * Helper interface used when creating a map literal. 317 * <p>The default implementation creates a java.util.HashMap.</p> 318 */ 319 public interface MapBuilder { 320 /** 321 * Adds a new entry to the map. 322 * 323 * @param key the map entry key 324 * @param value the map entry value 325 */ 326 void put(Object key, Object value); 327 328 /** 329 * Creates the actual "map" instance. 330 * 331 * @return the map 332 */ 333 Object create(); 334 } 335 336 /** 337 * Called by the interpreter when evaluating a literal map. 338 * 339 * @param size the number of elements in the map 340 * @return the map builder 341 */ 342 public MapBuilder mapBuilder(final int size) { 343 return new org.apache.commons.jexl3.internal.MapBuilder(size); 344 } 345 346 /** 347 * Creates a literal range. 348 * <p>The default implementation only accepts integers and longs.</p> 349 * 350 * @param from the included lower bound value (null if none) 351 * @param to the included upper bound value (null if none) 352 * @return the range as an iterable 353 * @throws ArithmeticException as an option if creation fails 354 */ 355 public Iterable<?> createRange(final Object from, final Object to) throws ArithmeticException { 356 final long lfrom = toLong(from); 357 final long lto = toLong(to); 358 if ((lfrom >= Integer.MIN_VALUE && lfrom <= Integer.MAX_VALUE) 359 && (lto >= Integer.MIN_VALUE && lto <= Integer.MAX_VALUE)) { 360 return org.apache.commons.jexl3.internal.IntegerRange.create((int) lfrom, (int) lto); 361 } 362 return org.apache.commons.jexl3.internal.LongRange.create(lfrom, lto); 363 } 364 365 /** 366 * Checks whether this JexlArithmetic instance 367 * strictly considers null as an error when used as operand unexpectedly. 368 * 369 * @return true if strict, false if lenient 370 */ 371 public boolean isStrict() { 372 return this.strict; 373 } 374 375 /** 376 * The MathContext instance used for +,-,/,*,% operations on big decimals. 377 * 378 * @return the math context 379 */ 380 public MathContext getMathContext() { 381 return mathContext; 382 } 383 384 /** 385 * The BigDecimal scale used for comparison and coericion operations. 386 * 387 * @return the scale 388 */ 389 public int getMathScale() { 390 return mathScale; 391 } 392 393 /** 394 * Ensure a big decimal is rounded by this arithmetic scale and rounding mode. 395 * 396 * @param number the big decimal to round 397 * @return the rounded big decimal 398 */ 399 protected BigDecimal roundBigDecimal(final BigDecimal number) { 400 final int mscale = getMathScale(); 401 if (mscale >= 0) { 402 return number.setScale(mscale, getMathContext().getRoundingMode()); 403 } 404 return number; 405 } 406 407 /** 408 * The result of +,/,-,*,% when both operands are null. 409 * 410 * @return Integer(0) if lenient 411 * @throws ArithmeticException if strict 412 */ 413 protected Object controlNullNullOperands() { 414 if (isStrict()) { 415 throw new NullOperand(); 416 } 417 return 0; 418 } 419 420 /** 421 * Throw a NPE if arithmetic is strict. 422 * 423 * @throws ArithmeticException if strict 424 */ 425 protected void controlNullOperand() { 426 if (isStrict()) { 427 throw new NullOperand(); 428 } 429 } 430 431 /** 432 * The float regular expression pattern. 433 * <p> 434 * The decimal and exponent parts are optional and captured allowing to determine if the number is a real 435 * by checking whether one of these 2 capturing groups is not empty. 436 */ 437 public static final Pattern FLOAT_PATTERN = Pattern.compile("^[+-]?\\d*(\\.\\d*)?([eE][+-]?\\d+)?$"); 438 439 /** 440 * Test if the passed value is a floating point number, i.e. a float, double 441 * or string with ( "." | "E" | "e"). 442 * 443 * @param val the object to be tested 444 * @return true if it is, false otherwise. 445 */ 446 protected boolean isFloatingPointNumber(final Object val) { 447 if (val instanceof Float || val instanceof Double) { 448 return true; 449 } 450 if (val instanceof CharSequence) { 451 final Matcher m = FLOAT_PATTERN.matcher((CharSequence) val); 452 // first group is decimal, second is exponent; 453 // one of them must exist hence start({1,2}) >= 0 454 return m.matches() && (m.start(1) >= 0 || m.start(2) >= 0); 455 } 456 return false; 457 } 458 459 /** 460 * Is Object a floating point number. 461 * 462 * @param o Object to be analyzed. 463 * @return true if it is a Float or a Double. 464 */ 465 protected boolean isFloatingPoint(final Object o) { 466 return o instanceof Float || o instanceof Double; 467 } 468 469 /** 470 * Is Object a whole number. 471 * 472 * @param o Object to be analyzed. 473 * @return true if Integer, Long, Byte, Short or Character. 474 */ 475 protected boolean isNumberable(final Object o) { 476 return o instanceof Integer 477 || o instanceof Long 478 || o instanceof Byte 479 || o instanceof Short 480 || o instanceof Character; 481 } 482 483 /** 484 * Given a Number, return back the value using the smallest type the result 485 * will fit into. 486 * <p>This works hand in hand with parameter 'widening' in java 487 * method calls, e.g. a call to substring(int,int) with an int and a long 488 * will fail, but a call to substring(int,int) with an int and a short will 489 * succeed.</p> 490 * 491 * @param original the original number. 492 * @return a value of the smallest type the original number will fit into. 493 */ 494 public Number narrow(final Number original) { 495 return narrowNumber(original, null); 496 } 497 498 /** 499 * Whether we consider the narrow class as a potential candidate for narrowing the source. 500 * 501 * @param narrow the target narrow class 502 * @param source the original source class 503 * @return true if attempt to narrow source to target is accepted 504 */ 505 protected boolean narrowAccept(final Class<?> narrow, final Class<?> source) { 506 return narrow == null || narrow.equals(source); 507 } 508 509 /** 510 * Given a Number, return back the value attempting to narrow it to a target class. 511 * 512 * @param original the original number 513 * @param narrow the attempted target class 514 * @return the narrowed number or the source if no narrowing was possible 515 */ 516 public Number narrowNumber(final Number original, final Class<?> narrow) { 517 if (original == null) { 518 return null; 519 } 520 Number result = original; 521 if (original instanceof BigDecimal) { 522 final BigDecimal bigd = (BigDecimal) original; 523 // if it's bigger than a double it can't be narrowed 524 if (bigd.compareTo(BIGD_DOUBLE_MAX_VALUE) > 0 525 || bigd.compareTo(BIGD_DOUBLE_MIN_VALUE) < 0) { 526 return original; 527 } 528 try { 529 final long l = bigd.longValueExact(); 530 // coerce to int when possible (int being so often used in method parms) 531 if (narrowAccept(narrow, Integer.class) 532 && l <= Integer.MAX_VALUE 533 && l >= Integer.MIN_VALUE) { 534 return (int) l; 535 } 536 if (narrowAccept(narrow, Long.class)) { 537 return l; 538 } 539 } catch (final ArithmeticException xa) { 540 // ignore, no exact value possible 541 } 542 } 543 if (original instanceof Double || original instanceof Float) { 544 final double value = original.doubleValue(); 545 if (narrowAccept(narrow, Float.class) 546 && value <= Float.MAX_VALUE 547 && value >= Float.MIN_VALUE) { 548 result = result.floatValue(); 549 } 550 // else it fits in a double only 551 } else { 552 if (original instanceof BigInteger) { 553 final BigInteger bigi = (BigInteger) original; 554 // if it's bigger than a Long it can't be narrowed 555 if (bigi.compareTo(BIGI_LONG_MAX_VALUE) > 0 556 || bigi.compareTo(BIGI_LONG_MIN_VALUE) < 0) { 557 return original; 558 } 559 } 560 final long value = original.longValue(); 561 if (narrowAccept(narrow, Byte.class) 562 && value <= Byte.MAX_VALUE 563 && value >= Byte.MIN_VALUE) { 564 // it will fit in a byte 565 result = (byte) value; 566 } else if (narrowAccept(narrow, Short.class) 567 && value <= Short.MAX_VALUE 568 && value >= Short.MIN_VALUE) { 569 result = (short) value; 570 } else if (narrowAccept(narrow, Integer.class) 571 && value <= Integer.MAX_VALUE 572 && value >= Integer.MIN_VALUE) { 573 result = (int) value; 574 } 575 // else it fits in a long 576 } 577 return result; 578 } 579 580 /** 581 * Given a BigInteger, narrow it to an Integer or Long if it fits and the arguments 582 * class allow it. 583 * <p> 584 * The rules are: 585 * if either arguments is a BigInteger, no narrowing will occur 586 * if either arguments is a Long, no narrowing to Integer will occur 587 * </p> 588 * 589 * @param lhs the left hand side operand that lead to the bigi result 590 * @param rhs the right hand side operand that lead to the bigi result 591 * @param bigi the BigInteger to narrow 592 * @return an Integer or Long if narrowing is possible, the original BigInteger otherwise 593 */ 594 protected Number narrowBigInteger(final Object lhs, final Object rhs, final BigInteger bigi) { 595 //coerce to long if possible 596 if (!(lhs instanceof BigInteger || rhs instanceof BigInteger) 597 && bigi.compareTo(BIGI_LONG_MAX_VALUE) <= 0 598 && bigi.compareTo(BIGI_LONG_MIN_VALUE) >= 0) { 599 // coerce to int if possible 600 final long l = bigi.longValue(); 601 // coerce to int when possible (int being so often used in method parms) 602 if (!(lhs instanceof Long || rhs instanceof Long) 603 && l <= Integer.MAX_VALUE 604 && l >= Integer.MIN_VALUE) { 605 return (int) l; 606 } 607 return l; 608 } 609 return bigi; 610 } 611 612 /** 613 * Given a BigDecimal, attempt to narrow it to an Integer or Long if it fits if 614 * one of the arguments is a numberable. 615 * 616 * @param lhs the left hand side operand that lead to the bigd result 617 * @param rhs the right hand side operand that lead to the bigd result 618 * @param bigd the BigDecimal to narrow 619 * @return an Integer or Long if narrowing is possible, the original BigInteger otherwise 620 */ 621 protected Number narrowBigDecimal(final Object lhs, final Object rhs, final BigDecimal bigd) { 622 if (isNumberable(lhs) || isNumberable(rhs)) { 623 try { 624 final long l = bigd.longValueExact(); 625 // coerce to int when possible (int being so often used in method parms) 626 if (l <= Integer.MAX_VALUE && l >= Integer.MIN_VALUE) { 627 return (int) l; 628 } 629 return l; 630 } catch (final ArithmeticException xa) { 631 // ignore, no exact value possible 632 } 633 } 634 return bigd; 635 } 636 637 /** 638 * Replace all numbers in an arguments array with the smallest type that will fit. 639 * 640 * @param args the argument array 641 * @return true if some arguments were narrowed and args array is modified, 642 * false if no narrowing occurred and args array has not been modified 643 */ 644 public boolean narrowArguments(final Object[] args) { 645 boolean narrowed = false; 646 if (args != null) { 647 for (int a = 0; a < args.length; ++a) { 648 final Object arg = args[a]; 649 if (arg instanceof Number) { 650 final Number narg = (Number) arg; 651 final Number narrow = narrow(narg); 652 if (!narg.equals(narrow)) { 653 args[a] = narrow; 654 narrowed = true; 655 } 656 } 657 } 658 } 659 return narrowed; 660 } 661 662 /** 663 * Given a long, attempt to narrow it to an int. 664 * <p>Narrowing will only occur if no operand is a Long. 665 * @param lhs the left hand side operand that lead to the long result 666 * @param rhs the right hand side operand that lead to the long result 667 * @param r the long to narrow 668 * @return an Integer if narrowing is possible, the original Long otherwise 669 */ 670 protected Number narrowLong(final Object lhs, final Object rhs, final long r) { 671 if (!(lhs instanceof Long || rhs instanceof Long) && (int) r == r) { 672 return (int) r; 673 } 674 return r; 675 } 676 677 /** 678 * Checks if value class is a number that can be represented exactly in a long. 679 * 680 * @param value argument 681 * @return true if argument can be represented by a long 682 */ 683 protected Number asLongNumber(final Object value) { 684 return value instanceof Long 685 || value instanceof Integer 686 || value instanceof Short 687 || value instanceof Byte 688 ? (Number) value 689 : null; 690 } 691 692 /** 693 * Add two values together. 694 * <p> 695 * If any numeric add fails on coercion to the appropriate type, 696 * treat as Strings and do concatenation. 697 * </p> 698 * 699 * @param left left argument 700 * @param right right argument 701 * @return left + right. 702 */ 703 public Object add(final Object left, final Object right) { 704 if (left == null && right == null) { 705 return controlNullNullOperands(); 706 } 707 final boolean strconcat = strict 708 ? left instanceof String || right instanceof String 709 : left instanceof String && right instanceof String; 710 if (!strconcat) { 711 try { 712 // if both (non null) args fit as long 713 final Number ln = asLongNumber(left); 714 final Number rn = asLongNumber(right); 715 if (ln != null && rn != null) { 716 final long x = ln.longValue(); 717 final long y = rn.longValue(); 718 final long result = x + y; 719 // detect overflow, see java8 Math.addExact 720 if (((x ^ result) & (y ^ result)) < 0) { 721 return BigInteger.valueOf(x).add(BigInteger.valueOf(y)); 722 } 723 return narrowLong(left, right, result); 724 } 725 // if either are bigdecimal use that type 726 if (left instanceof BigDecimal || right instanceof BigDecimal) { 727 final BigDecimal l = toBigDecimal(left); 728 final BigDecimal r = toBigDecimal(right); 729 final BigDecimal result = l.add(r, getMathContext()); 730 return narrowBigDecimal(left, right, result); 731 } 732 // if either are floating point (double or float) use double 733 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 734 final double l = toDouble(left); 735 final double r = toDouble(right); 736 return l + r; 737 } 738 // otherwise treat as (big) integers 739 final BigInteger l = toBigInteger(left); 740 final BigInteger r = toBigInteger(right); 741 final BigInteger result = l.add(r); 742 return narrowBigInteger(left, right, result); 743 } catch (final java.lang.NumberFormatException nfe) { 744 if (left == null || right == null) { 745 controlNullOperand(); 746 } 747 } 748 } 749 return toString(left).concat(toString(right)); 750 } 751 752 /** 753 * Divide the left value by the right. 754 * 755 * @param left left argument 756 * @param right right argument 757 * @return left / right 758 * @throws ArithmeticException if right == 0 759 */ 760 public Object divide(final Object left, final Object right) { 761 if (left == null && right == null) { 762 return controlNullNullOperands(); 763 } 764 // if both (non null) args fit as long 765 final Number ln = asLongNumber(left); 766 final Number rn = asLongNumber(right); 767 if (ln != null && rn != null) { 768 final long x = ln.longValue(); 769 final long y = rn.longValue(); 770 if (y == 0L) { 771 throw new ArithmeticException("/"); 772 } 773 final long result = x / y; 774 return narrowLong(left, right, result); 775 } 776 // if either are bigdecimal use that type 777 if (left instanceof BigDecimal || right instanceof BigDecimal) { 778 final BigDecimal l = toBigDecimal(left); 779 final BigDecimal r = toBigDecimal(right); 780 if (BigDecimal.ZERO.equals(r)) { 781 throw new ArithmeticException("/"); 782 } 783 final BigDecimal result = l.divide(r, getMathContext()); 784 return narrowBigDecimal(left, right, result); 785 } 786 // if either are floating point (double or float) use double 787 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 788 final double l = toDouble(left); 789 final double r = toDouble(right); 790 if (r == 0.0) { 791 throw new ArithmeticException("/"); 792 } 793 return l / r; 794 } 795 // otherwise treat as integers 796 final BigInteger l = toBigInteger(left); 797 final BigInteger r = toBigInteger(right); 798 if (BigInteger.ZERO.equals(r)) { 799 throw new ArithmeticException("/"); 800 } 801 final BigInteger result = l.divide(r); 802 return narrowBigInteger(left, right, result); 803 } 804 805 /** 806 * left value modulo right. 807 * 808 * @param left left argument 809 * @param right right argument 810 * @return left % right 811 * @throws ArithmeticException if right == 0.0 812 */ 813 public Object mod(final Object left, final Object right) { 814 if (left == null && right == null) { 815 return controlNullNullOperands(); 816 } 817 // if both (non null) args fit as long 818 final Number ln = asLongNumber(left); 819 final Number rn = asLongNumber(right); 820 if (ln != null && rn != null) { 821 final long x = ln.longValue(); 822 final long y = rn.longValue(); 823 if (y == 0L) { 824 throw new ArithmeticException("%"); 825 } 826 final long result = x % y; 827 return narrowLong(left, right, result); 828 } 829 // if either are bigdecimal use that type 830 if (left instanceof BigDecimal || right instanceof BigDecimal) { 831 final BigDecimal l = toBigDecimal(left); 832 final BigDecimal r = toBigDecimal(right); 833 if (BigDecimal.ZERO.equals(r)) { 834 throw new ArithmeticException("%"); 835 } 836 final BigDecimal remainder = l.remainder(r, getMathContext()); 837 return narrowBigDecimal(left, right, remainder); 838 } 839 // if either are floating point (double or float) use double 840 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 841 final double l = toDouble(left); 842 final double r = toDouble(right); 843 if (r == 0.0) { 844 throw new ArithmeticException("%"); 845 } 846 return l % r; 847 } 848 // otherwise treat as integers 849 final BigInteger l = toBigInteger(left); 850 final BigInteger r = toBigInteger(right); 851 if (BigInteger.ZERO.equals(r)) { 852 throw new ArithmeticException("%"); 853 } 854 final BigInteger result = l.mod(r); 855 return narrowBigInteger(left, right, result); 856 } 857 858 /** 859 * Checks if the product of the arguments overflows a {@code long}. 860 * <p>see java8 Math.multiplyExact 861 * @param x the first value 862 * @param y the second value 863 * @param r the product 864 * @return true if product fits a long, false if it overflows 865 */ 866 @SuppressWarnings("MagicNumber") 867 protected static boolean isMultiplyExact(final long x, final long y, final long r) { 868 final long ax = Math.abs(x); 869 final long ay = Math.abs(y); 870 return !(((ax | ay) >>> (Integer.SIZE - 1) != 0) 871 && (((y != 0) && (r / y != x)) 872 || (x == Long.MIN_VALUE && y == -1))); 873 } 874 875 /** 876 * Multiply the left value by the right. 877 * 878 * @param left left argument 879 * @param right right argument 880 * @return left * right. 881 */ 882 public Object multiply(final Object left, final Object right) { 883 if (left == null && right == null) { 884 return controlNullNullOperands(); 885 } 886 // if both (non null) args fit as int 887 final Number ln = asLongNumber(left); 888 final Number rn = asLongNumber(right); 889 if (ln != null && rn != null) { 890 final long x = ln.longValue(); 891 final long y = rn.longValue(); 892 final long result = x * y; 893 // detect overflow 894 if (!isMultiplyExact(x, y, result)) { 895 return BigInteger.valueOf(x).multiply(BigInteger.valueOf(y)); 896 } 897 return narrowLong(left, right, result); 898 } 899 // if either are bigdecimal use that type 900 if (left instanceof BigDecimal || right instanceof BigDecimal) { 901 final BigDecimal l = toBigDecimal(left); 902 final BigDecimal r = toBigDecimal(right); 903 final BigDecimal result = l.multiply(r, getMathContext()); 904 return narrowBigDecimal(left, right, result); 905 } 906 // if either are floating point (double or float) use double 907 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 908 final double l = toDouble(left); 909 final double r = toDouble(right); 910 return l * r; 911 } 912 // otherwise treat as integers 913 final BigInteger l = toBigInteger(left); 914 final BigInteger r = toBigInteger(right); 915 final BigInteger result = l.multiply(r); 916 return narrowBigInteger(left, right, result); 917 } 918 919 /** 920 * Subtract the right value from the left. 921 * 922 * @param left left argument 923 * @param right right argument 924 * @return left - right. 925 */ 926 public Object subtract(final Object left, final Object right) { 927 if (left == null && right == null) { 928 return controlNullNullOperands(); 929 } 930 // if both (non null) args fit as long 931 final Number ln = asLongNumber(left); 932 final Number rn = asLongNumber(right); 933 if (ln != null && rn != null) { 934 final long x = ln.longValue(); 935 final long y = rn.longValue(); 936 final long result = x - y; 937 // detect overflow, see java8 Math.subtractExact 938 if (((x ^ y) & (x ^ result)) < 0) { 939 return BigInteger.valueOf(x).subtract(BigInteger.valueOf(y)); 940 } 941 return narrowLong(left, right, result); 942 } 943 // if either are bigdecimal use that type 944 if (left instanceof BigDecimal || right instanceof BigDecimal) { 945 final BigDecimal l = toBigDecimal(left); 946 final BigDecimal r = toBigDecimal(right); 947 final BigDecimal result = l.subtract(r, getMathContext()); 948 return narrowBigDecimal(left, right, result); 949 } 950 // if either are floating point (double or float) use double 951 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 952 final double l = toDouble(left); 953 final double r = toDouble(right); 954 return l - r; 955 } 956 // otherwise treat as integers 957 final BigInteger l = toBigInteger(left); 958 final BigInteger r = toBigInteger(right); 959 final BigInteger result = l.subtract(r); 960 return narrowBigInteger(left, right, result); 961 } 962 963 /** 964 * Negates a value (unary minus for numbers). 965 * 966 * @param val the value to negate 967 * @return the negated value 968 */ 969 public Object negate(final Object val) { 970 if (val == null) { 971 controlNullOperand(); 972 return null; 973 } 974 if (val instanceof Integer) { 975 return -((Integer) val); 976 } 977 if (val instanceof Double) { 978 return - ((Double) val); 979 } 980 if (val instanceof Long) { 981 return -((Long) val); 982 } 983 if (val instanceof BigDecimal) { 984 return ((BigDecimal) val).negate(); 985 } 986 if (val instanceof BigInteger) { 987 return ((BigInteger) val).negate(); 988 } 989 if (val instanceof Float) { 990 return -((Float) val); 991 } 992 if (val instanceof Short) { 993 return (short) -((Short) val); 994 } 995 if (val instanceof Byte) { 996 return (byte) -((Byte) val); 997 } 998 if (val instanceof Boolean) { 999 return !(Boolean) val; 1000 } 1001 if (val instanceof AtomicBoolean) { 1002 return !((AtomicBoolean) val).get(); 1003 } 1004 throw new ArithmeticException("Object negate:(" + val + ")"); 1005 } 1006 1007 /** 1008 * Whether negate called with a given argument will always return the same result. 1009 * <p>This is used to determine whether negate results on number literals can be cached. 1010 * If the result on calling negate with the same constant argument may change between calls, 1011 * which means the function is not deterministic, this method must return false. 1012 * @see #isNegateStable() 1013 * @return true if negate is idempotent, false otherwise 1014 */ 1015 public boolean isNegateStable() { 1016 return true; 1017 } 1018 1019 /** 1020 * Positivize value (unary plus for numbers). 1021 * <p>C/C++/C#/Java perform integral promotion of the operand, ie 1022 * cast to int if type can represented as int without loss of precision. 1023 * @see #isPositivizeStable() 1024 * @param val the value to positivize 1025 * @return the positive value 1026 */ 1027 public Object positivize(final Object val) { 1028 if (val == null) { 1029 controlNullOperand(); 1030 return null; 1031 } 1032 if (val instanceof Short) { 1033 return ((Short) val).intValue(); 1034 } 1035 if (val instanceof Byte) { 1036 return ((Byte) val).intValue(); 1037 } 1038 if (val instanceof Number) { 1039 return val; 1040 } 1041 if (val instanceof Character) { 1042 return (int) (Character) val; 1043 } 1044 if (val instanceof Boolean) { 1045 return val; 1046 } 1047 if (val instanceof AtomicBoolean) { 1048 return ((AtomicBoolean) val).get(); 1049 } 1050 throw new ArithmeticException("Object positivize:(" + val + ")"); 1051 } 1052 1053 /** 1054 * Whether positivize called with a given argument will always return the same result. 1055 * <p>This is used to determine whether positivize results on number literals can be cached. 1056 * If the result on calling positivize with the same constant argument may change between calls, 1057 * which means the function is not deterministic, this method must return false. 1058 * @return true if positivize is idempotent, false otherwise 1059 */ 1060 public boolean isPositivizeStable() { 1061 return true; 1062 } 1063 1064 /** 1065 * Test if left contains right (right matches/in left). 1066 * <p>Beware that this method arguments are the opposite of the operator arguments. 1067 * 'x in y' means 'y contains x'.</p> 1068 * 1069 * @param container the container 1070 * @param value the value 1071 * @return test result or null if there is no arithmetic solution 1072 */ 1073 public Boolean contains(final Object container, final Object value) { 1074 if (value == null && container == null) { 1075 //if both are null L == R 1076 return true; 1077 } 1078 if (value == null || container == null) { 1079 // we know both aren't null, therefore L != R 1080 return false; 1081 } 1082 // use arithmetic / pattern matching ? 1083 if (container instanceof java.util.regex.Pattern) { 1084 return ((java.util.regex.Pattern) container).matcher(value.toString()).matches(); 1085 } 1086 if (container instanceof CharSequence) { 1087 return value.toString().matches(container.toString()); 1088 } 1089 // try contains on map key 1090 if (container instanceof Map<?, ?>) { 1091 if (value instanceof Map<?, ?>) { 1092 return ((Map<?, ?>) container).keySet().containsAll(((Map<?, ?>) value).keySet()); 1093 } 1094 return ((Map<?, ?>) container).containsKey(value); 1095 } 1096 // try contains on collection 1097 if (container instanceof Collection<?>) { 1098 if (value instanceof Collection<?>) { 1099 return ((Collection<?>) container).containsAll((Collection<?>) value); 1100 } 1101 // left in right ? <=> right.contains(left) ? 1102 return ((Collection<?>) container).contains(value); 1103 } 1104 return null; 1105 } 1106 1107 /** 1108 * Test if left ends with right. 1109 * 1110 * @param left left argument 1111 * @param right right argument 1112 * @return left $= right if there is no arithmetic solution 1113 */ 1114 public Boolean endsWith(final Object left, final Object right) { 1115 if (left == null && right == null) { 1116 //if both are null L == R 1117 return true; 1118 } 1119 if (left == null || right == null) { 1120 // we know both aren't null, therefore L != R 1121 return false; 1122 } 1123 if (left instanceof CharSequence) { 1124 return (toString(left)).endsWith(toString(right)); 1125 } 1126 return null; 1127 } 1128 1129 /** 1130 * Test if left starts with right. 1131 * 1132 * @param left left argument 1133 * @param right right argument 1134 * @return left ^= right or null if there is no arithmetic solution 1135 */ 1136 public Boolean startsWith(final Object left, final Object right) { 1137 if (left == null && right == null) { 1138 //if both are null L == R 1139 return true; 1140 } 1141 if (left == null || right == null) { 1142 // we know both aren't null, therefore L != R 1143 return false; 1144 } 1145 if (left instanceof CharSequence) { 1146 return (toString(left)).startsWith(toString(right)); 1147 } 1148 return null; 1149 } 1150 1151 /** 1152 * Check for emptiness of various types: Number, Collection, Array, Map, String. 1153 * <p>Override or overload this method to add new signatures to the size operators. 1154 * @param object the object to check the emptiness of 1155 * @return the boolean or false if object is not null 1156 * @since 3.2 1157 */ 1158 public Boolean empty(final Object object) { 1159 return object == null || isEmpty(object, false); 1160 } 1161 1162 /** 1163 * Check for emptiness of various types: Number, Collection, Array, Map, String. 1164 * 1165 * @param object the object to check the emptiness of 1166 * @return the boolean or null if there is no arithmetic solution 1167 */ 1168 public Boolean isEmpty(final Object object) { 1169 return isEmpty(object, object == null); 1170 } 1171 1172 /** 1173 * Check for emptiness of various types: Number, Collection, Array, Map, String. 1174 * 1175 * @param object the object to check the emptiness of 1176 * @param def the default value if object emptyness can not be determined 1177 * @return the boolean or null if there is no arithmetic solution 1178 */ 1179 public Boolean isEmpty(final Object object, final Boolean def) { 1180 if (object instanceof Number) { 1181 final double d = ((Number) object).doubleValue(); 1182 return Double.isNaN(d) || d == 0.d; 1183 } 1184 if (object instanceof CharSequence) { 1185 return ((CharSequence) object).length() == 0; 1186 } 1187 if (object.getClass().isArray()) { 1188 return Array.getLength(object) == 0; 1189 } 1190 if (object instanceof Collection<?>) { 1191 return ((Collection<?>) object).isEmpty(); 1192 } 1193 // Map isn't a collection 1194 if (object instanceof Map<?, ?>) { 1195 return ((Map<?, ?>) object).isEmpty(); 1196 } 1197 return def; 1198 } 1199 1200 /** 1201 * Calculate the <code>size</code> of various types: Collection, Array, Map, String. 1202 * 1203 * @param object the object to get the size of 1204 * @return the <i>size</i> of object, 0 if null, 1 if there is no <i>better</i> solution 1205 */ 1206 public Integer size(final Object object) { 1207 return size(object, object == null? 0 : 1); 1208 } 1209 1210 /** 1211 * Calculate the <code>size</code> of various types: Collection, Array, Map, String. 1212 * 1213 * @param object the object to get the size of 1214 * @param def the default value if object size can not be determined 1215 * @return the size of object or null if there is no arithmetic solution 1216 */ 1217 public Integer size(final Object object, final Integer def) { 1218 if (object instanceof CharSequence) { 1219 return ((CharSequence) object).length(); 1220 } 1221 if (object.getClass().isArray()) { 1222 return Array.getLength(object); 1223 } 1224 if (object instanceof Collection<?>) { 1225 return ((Collection<?>) object).size(); 1226 } 1227 if (object instanceof Map<?, ?>) { 1228 return ((Map<?, ?>) object).size(); 1229 } 1230 return def; 1231 } 1232 1233 /** 1234 * Performs a bitwise and. 1235 * 1236 * @param left the left operand 1237 * @param right the right operator 1238 * @return left & right 1239 */ 1240 public Object and(final Object left, final Object right) { 1241 final long l = toLong(left); 1242 final long r = toLong(right); 1243 return l & r; 1244 } 1245 1246 /** 1247 * Performs a bitwise or. 1248 * 1249 * @param left the left operand 1250 * @param right the right operator 1251 * @return left | right 1252 */ 1253 public Object or(final Object left, final Object right) { 1254 final long l = toLong(left); 1255 final long r = toLong(right); 1256 return l | r; 1257 } 1258 1259 /** 1260 * Performs a bitwise xor. 1261 * 1262 * @param left the left operand 1263 * @param right the right operator 1264 * @return left ^ right 1265 */ 1266 public Object xor(final Object left, final Object right) { 1267 final long l = toLong(left); 1268 final long r = toLong(right); 1269 return l ^ r; 1270 } 1271 1272 /** 1273 * Performs a bitwise complement. 1274 * 1275 * @param val the operand 1276 * @return ~val 1277 */ 1278 public Object complement(final Object val) { 1279 final long l = toLong(val); 1280 return ~l; 1281 } 1282 1283 /** 1284 * Performs a logical not. 1285 * 1286 * @param val the operand 1287 * @return !val 1288 */ 1289 public Object not(final Object val) { 1290 return !toBoolean(val); 1291 } 1292 1293 /** 1294 * Performs a comparison. 1295 * 1296 * @param left the left operand 1297 * @param right the right operator 1298 * @param operator the operator 1299 * @return -1 if left < right; +1 if left > right; 0 if left == right 1300 * @throws ArithmeticException if either left or right is null 1301 */ 1302 protected int compare(final Object left, final Object right, final String operator) { 1303 if (left != null && right != null) { 1304 if (left instanceof BigDecimal || right instanceof BigDecimal) { 1305 final BigDecimal l = toBigDecimal(left); 1306 final BigDecimal r = toBigDecimal(right); 1307 return l.compareTo(r); 1308 } 1309 if (left instanceof BigInteger || right instanceof BigInteger) { 1310 final BigInteger l = toBigInteger(left); 1311 final BigInteger r = toBigInteger(right); 1312 return l.compareTo(r); 1313 } 1314 if (isFloatingPoint(left) || isFloatingPoint(right)) { 1315 final double lhs = toDouble(left); 1316 final double rhs = toDouble(right); 1317 if (Double.isNaN(lhs)) { 1318 if (Double.isNaN(rhs)) { 1319 return 0; 1320 } 1321 return -1; 1322 } 1323 if (Double.isNaN(rhs)) { 1324 // lhs is not NaN 1325 return +1; 1326 } 1327 if (lhs < rhs) { 1328 return -1; 1329 } 1330 if (lhs > rhs) { 1331 return +1; 1332 } 1333 return 0; 1334 } 1335 if (isNumberable(left) || isNumberable(right)) { 1336 final long lhs = toLong(left); 1337 final long rhs = toLong(right); 1338 if (lhs < rhs) { 1339 return -1; 1340 } 1341 if (lhs > rhs) { 1342 return +1; 1343 } 1344 return 0; 1345 } 1346 if (left instanceof String || right instanceof String) { 1347 return toString(left).compareTo(toString(right)); 1348 } 1349 if ("==".equals(operator)) { 1350 return left.equals(right) ? 0 : -1; 1351 } 1352 if (left instanceof Comparable<?>) { 1353 @SuppressWarnings("unchecked") // OK because of instanceof check above 1354 final Comparable<Object> comparable = (Comparable<Object>) left; 1355 return comparable.compareTo(right); 1356 } 1357 if (right instanceof Comparable<?>) { 1358 @SuppressWarnings("unchecked") // OK because of instanceof check above 1359 final Comparable<Object> comparable = (Comparable<Object>) right; 1360 return comparable.compareTo(left); 1361 } 1362 } 1363 throw new ArithmeticException("Object comparison:(" + left + " " + operator + " " + right + ")"); 1364 } 1365 1366 /** 1367 * Test if left and right are equal. 1368 * 1369 * @param left left argument 1370 * @param right right argument 1371 * @return the test result 1372 */ 1373 public boolean equals(final Object left, final Object right) { 1374 if (left == right) { 1375 return true; 1376 } 1377 if (left == null || right == null) { 1378 return false; 1379 } 1380 if (left instanceof Boolean || right instanceof Boolean) { 1381 return toBoolean(left) == toBoolean(right); 1382 } 1383 return compare(left, right, "==") == 0; 1384 } 1385 1386 /** 1387 * Test if left < right. 1388 * 1389 * @param left left argument 1390 * @param right right argument 1391 * @return the test result 1392 */ 1393 public boolean lessThan(final Object left, final Object right) { 1394 if ((left == right) || (left == null) || (right == null)) { 1395 return false; 1396 } 1397 return compare(left, right, "<") < 0; 1398 1399 } 1400 1401 /** 1402 * Test if left > right. 1403 * 1404 * @param left left argument 1405 * @param right right argument 1406 * @return the test result 1407 */ 1408 public boolean greaterThan(final Object left, final Object right) { 1409 if ((left == right) || left == null || right == null) { 1410 return false; 1411 } 1412 return compare(left, right, ">") > 0; 1413 } 1414 1415 /** 1416 * Test if left <= right. 1417 * 1418 * @param left left argument 1419 * @param right right argument 1420 * @return the test result 1421 */ 1422 public boolean lessThanOrEqual(final Object left, final Object right) { 1423 if (left == right) { 1424 return true; 1425 } 1426 if (left == null || right == null) { 1427 return false; 1428 } 1429 return compare(left, right, "<=") <= 0; 1430 } 1431 1432 /** 1433 * Test if left >= right. 1434 * 1435 * @param left left argument 1436 * @param right right argument 1437 * @return the test result 1438 */ 1439 public boolean greaterThanOrEqual(final Object left, final Object right) { 1440 if (left == right) { 1441 return true; 1442 } 1443 if (left == null || right == null) { 1444 return false; 1445 } 1446 return compare(left, right, ">=") >= 0; 1447 } 1448 1449 /** 1450 * Coerce to a primitive boolean. 1451 * <p>Double.NaN, null, "false" and empty string coerce to false.</p> 1452 * 1453 * @param val value to coerce 1454 * @return the boolean value if coercion is possible, true if value was not null. 1455 */ 1456 public boolean toBoolean(final Object val) { 1457 if (val == null) { 1458 controlNullOperand(); 1459 return false; 1460 } 1461 if (val instanceof Boolean) { 1462 return ((Boolean) val); 1463 } 1464 if (val instanceof Number) { 1465 final double number = toDouble(val); 1466 return !Double.isNaN(number) && number != 0.d; 1467 } 1468 if (val instanceof AtomicBoolean) { 1469 return ((AtomicBoolean) val).get(); 1470 } 1471 if (val instanceof String) { 1472 final String strval = val.toString(); 1473 return !strval.isEmpty() && !"false".equals(strval); 1474 } 1475 // non null value is true 1476 return true; 1477 } 1478 1479 /** 1480 * Coerce to a primitive int. 1481 * <p>Double.NaN, null and empty string coerce to zero.</p> 1482 * <p>Boolean false is 0, true is 1.</p> 1483 * 1484 * @param val value to coerce 1485 * @return the value coerced to int 1486 * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible 1487 */ 1488 public int toInteger(final Object val) { 1489 if (val == null) { 1490 controlNullOperand(); 1491 return 0; 1492 } 1493 if (val instanceof Double) { 1494 final Double dval = (Double) val; 1495 if (Double.isNaN(dval)) { 1496 return 0; 1497 } 1498 return dval.intValue(); 1499 } 1500 if (val instanceof Number) { 1501 return ((Number) val).intValue(); 1502 } 1503 if (val instanceof String) { 1504 if ("".equals(val)) { 1505 return 0; 1506 } 1507 return Integer.parseInt((String) val); 1508 } 1509 if (val instanceof Boolean) { 1510 return ((Boolean) val) ? 1 : 0; 1511 } 1512 if (val instanceof AtomicBoolean) { 1513 return ((AtomicBoolean) val).get() ? 1 : 0; 1514 } 1515 if (val instanceof Character) { 1516 return ((Character) val); 1517 } 1518 1519 throw new ArithmeticException("Integer coercion: " 1520 + val.getClass().getName() + ":(" + val + ")"); 1521 } 1522 1523 /** 1524 * Coerce to a primitive long. 1525 * <p>Double.NaN, null and empty string coerce to zero.</p> 1526 * <p>Boolean false is 0, true is 1.</p> 1527 * 1528 * @param val value to coerce 1529 * @return the value coerced to long 1530 * @throws ArithmeticException if value is null and mode is strict or if coercion is not possible 1531 */ 1532 public long toLong(final Object val) { 1533 if (val == null) { 1534 controlNullOperand(); 1535 return 0L; 1536 } 1537 if (val instanceof Double) { 1538 final Double dval = (Double) val; 1539 if (Double.isNaN(dval)) { 1540 return 0L; 1541 } 1542 return dval.longValue(); 1543 } 1544 if (val instanceof Number) { 1545 return ((Number) val).longValue(); 1546 } 1547 if (val instanceof String) { 1548 if ("".equals(val)) { 1549 return 0L; 1550 } 1551 return Long.parseLong((String) val); 1552 } 1553 if (val instanceof Boolean) { 1554 return ((Boolean) val) ? 1L : 0L; 1555 } 1556 if (val instanceof AtomicBoolean) { 1557 return ((AtomicBoolean) val).get() ? 1L : 0L; 1558 } 1559 if (val instanceof Character) { 1560 return ((Character) val); 1561 } 1562 1563 throw new ArithmeticException("Long coercion: " 1564 + val.getClass().getName() + ":(" + val + ")"); 1565 } 1566 1567 /** 1568 * Coerce to a BigInteger. 1569 * <p>Double.NaN, null and empty string coerce to zero.</p> 1570 * <p>Boolean false is 0, true is 1.</p> 1571 * 1572 * @param val the object to be coerced. 1573 * @return a BigDecimal 1574 * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible 1575 */ 1576 public BigInteger toBigInteger(final Object val) { 1577 if (val == null) { 1578 controlNullOperand(); 1579 return BigInteger.ZERO; 1580 } 1581 if (val instanceof BigInteger) { 1582 return (BigInteger) val; 1583 } 1584 if (val instanceof Double) { 1585 final Double dval = (Double) val; 1586 if (Double.isNaN(dval)) { 1587 return BigInteger.ZERO; 1588 } 1589 return BigInteger.valueOf(dval.longValue()); 1590 } 1591 if (val instanceof BigDecimal) { 1592 return ((BigDecimal) val).toBigInteger(); 1593 } 1594 if (val instanceof Number) { 1595 return BigInteger.valueOf(((Number) val).longValue()); 1596 } 1597 if (val instanceof Boolean) { 1598 return BigInteger.valueOf(((Boolean) val) ? 1L : 0L); 1599 } 1600 if (val instanceof AtomicBoolean) { 1601 return BigInteger.valueOf(((AtomicBoolean) val).get() ? 1L : 0L); 1602 } 1603 if (val instanceof String) { 1604 final String string = (String) val; 1605 if ("".equals(string)) { 1606 return BigInteger.ZERO; 1607 } 1608 return new BigInteger(string); 1609 } 1610 if (val instanceof Character) { 1611 final int i = ((Character) val); 1612 return BigInteger.valueOf(i); 1613 } 1614 1615 throw new ArithmeticException("BigInteger coercion: " 1616 + val.getClass().getName() + ":(" + val + ")"); 1617 } 1618 1619 /** 1620 * Coerce to a BigDecimal. 1621 * <p>Double.NaN, null and empty string coerce to zero.</p> 1622 * <p>Boolean false is 0, true is 1.</p> 1623 * 1624 * @param val the object to be coerced. 1625 * @return a BigDecimal. 1626 * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible 1627 */ 1628 public BigDecimal toBigDecimal(final Object val) { 1629 if (val instanceof BigDecimal) { 1630 return roundBigDecimal((BigDecimal) val); 1631 } 1632 if (val == null) { 1633 controlNullOperand(); 1634 return BigDecimal.ZERO; 1635 } 1636 if (val instanceof Double) { 1637 if (Double.isNaN(((Double) val))) { 1638 return BigDecimal.ZERO; 1639 } 1640 return roundBigDecimal(new BigDecimal(val.toString(), getMathContext())); 1641 } 1642 if (val instanceof Number) { 1643 return roundBigDecimal(new BigDecimal(val.toString(), getMathContext())); 1644 } 1645 if (val instanceof Boolean) { 1646 return BigDecimal.valueOf(((Boolean) val) ? 1. : 0.); 1647 } 1648 if (val instanceof AtomicBoolean) { 1649 return BigDecimal.valueOf(((AtomicBoolean) val).get() ? 1L : 0L); 1650 } 1651 if (val instanceof String) { 1652 final String string = (String) val; 1653 if ("".equals(string)) { 1654 return BigDecimal.ZERO; 1655 } 1656 return roundBigDecimal(new BigDecimal(string, getMathContext())); 1657 } 1658 if (val instanceof Character) { 1659 final int i = ((Character) val); 1660 return new BigDecimal(i); 1661 } 1662 throw new ArithmeticException("BigDecimal coercion: " 1663 + val.getClass().getName() + ":(" + val + ")"); 1664 } 1665 1666 /** 1667 * Coerce to a primitive double. 1668 * <p>Double.NaN, null and empty string coerce to zero.</p> 1669 * <p>Boolean false is 0, true is 1.</p> 1670 * 1671 * @param val value to coerce. 1672 * @return The double coerced value. 1673 * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible 1674 */ 1675 public double toDouble(final Object val) { 1676 if (val == null) { 1677 controlNullOperand(); 1678 return 0; 1679 } 1680 if (val instanceof Double) { 1681 return ((Double) val); 1682 } 1683 if (val instanceof Number) { 1684 //The below construct is used rather than ((Number)val).doubleValue() to ensure 1685 //equality between comparing new Double( 6.4 / 3 ) and the jexl expression of 6.4 / 3 1686 return Double.parseDouble(String.valueOf(val)); 1687 } 1688 if (val instanceof Boolean) { 1689 return ((Boolean) val) ? 1. : 0.; 1690 } 1691 if (val instanceof AtomicBoolean) { 1692 return ((AtomicBoolean) val).get() ? 1. : 0.; 1693 } 1694 if (val instanceof String) { 1695 final String string = (String) val; 1696 if ("".equals(string)) { 1697 return Double.NaN; 1698 } 1699 // the spec seems to be iffy about this. Going to give it a wack anyway 1700 return Double.parseDouble(string); 1701 } 1702 if (val instanceof Character) { 1703 final int i = ((Character) val); 1704 return i; 1705 } 1706 throw new ArithmeticException("Double coercion: " 1707 + val.getClass().getName() + ":(" + val + ")"); 1708 } 1709 1710 /** 1711 * Coerce to a string. 1712 * <p>Double.NaN coerce to the empty string.</p> 1713 * 1714 * @param val value to coerce. 1715 * @return The String coerced value. 1716 * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible 1717 */ 1718 public String toString(final Object val) { 1719 if (val == null) { 1720 controlNullOperand(); 1721 return ""; 1722 } 1723 if (!(val instanceof Double)) { 1724 return val.toString(); 1725 } 1726 final Double dval = (Double) val; 1727 if (Double.isNaN(dval)) { 1728 return ""; 1729 } 1730 return dval.toString(); 1731 } 1732 1733 /** 1734 * Use or overload and() instead. 1735 * @param lhs left hand side 1736 * @param rhs right hand side 1737 * @return lhs & rhs 1738 * @see JexlArithmetic#and 1739 * @deprecated 1740 */ 1741 @Deprecated 1742 public final Object bitwiseAnd(final Object lhs, final Object rhs) { 1743 return and(lhs, rhs); 1744 } 1745 1746 /** 1747 * Use or overload or() instead. 1748 * 1749 * @param lhs left hand side 1750 * @param rhs right hand side 1751 * @return lhs | rhs 1752 * @see JexlArithmetic#or 1753 * @deprecated 1754 */ 1755 @Deprecated 1756 public final Object bitwiseOr(final Object lhs, final Object rhs) { 1757 return or(lhs, rhs); 1758 } 1759 1760 /** 1761 * Use or overload xor() instead. 1762 * 1763 * @param lhs left hand side 1764 * @param rhs right hand side 1765 * @return lhs ^ rhs 1766 * @see JexlArithmetic#xor 1767 * @deprecated 1768 */ 1769 @Deprecated 1770 public final Object bitwiseXor(final Object lhs, final Object rhs) { 1771 return xor(lhs, rhs); 1772 } 1773 1774 /** 1775 * Use or overload not() instead. 1776 * 1777 * @param arg argument 1778 * @return !arg 1779 * @see JexlArithmetic#not 1780 * @deprecated 1781 */ 1782 @Deprecated 1783 public final Object logicalNot(final Object arg) { 1784 return not(arg); 1785 } 1786 1787 /** 1788 * Use or overload contains() instead. 1789 * 1790 * @param lhs left hand side 1791 * @param rhs right hand side 1792 * @return contains(rhs, lhs) 1793 * @see JexlArithmetic#contains 1794 * @deprecated 1795 */ 1796 @Deprecated 1797 public final Object matches(final Object lhs, final Object rhs) { 1798 return contains(rhs, lhs); 1799 } 1800}