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 * https://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 static java.lang.StrictMath.floor; 021import static org.apache.commons.jexl3.JexlOperator.EQ; 022 023import java.lang.reflect.Array; 024import java.lang.reflect.Constructor; 025import java.lang.reflect.InvocationTargetException; 026import java.lang.reflect.Method; 027import java.math.BigDecimal; 028import java.math.BigInteger; 029import java.math.MathContext; 030import java.util.Collection; 031import java.util.Map; 032import java.util.concurrent.atomic.AtomicBoolean; 033import java.util.function.ToLongFunction; 034import java.util.regex.Matcher; 035import java.util.regex.Pattern; 036 037import org.apache.commons.jexl3.introspection.JexlMethod; 038 039/** 040 * Perform arithmetic, implements JexlOperator methods. 041 * 042 * <p>This is the class to derive to implement new operator behaviors.</p> 043 * 044 * <p>The 5 base arithmetic operators (+, - , *, /, %) follow the same evaluation rules regarding their arguments.</p> 045 * <ol> 046 * <li>If both are null, result is 0 if arithmetic (or operator) is non-strict, ArithmeticException is thrown 047 * otherwise</li> 048 * <li>If both arguments are numberable - any kind of integer including boolean -, coerce both to Long and coerce 049 * result to the most precise argument class ({@code boolean < byte < short < int < long}); 050 * if long operation would cause overflow, return a BigInteger</li> 051 * <li>If either argument is a BigDecimal, coerce both to BigDecimal, operator returns BigDecimal</li> 052 * <li>If either argument is a floating point number, coerce both to Double, operator returns Double</li> 053 * <li>Else treat as BigInteger, perform operation and narrow result to the most precise argument class 054 * </li> 055 * </ol> 056 * 057 * Note that the only exception thrown by JexlArithmetic is and must be ArithmeticException. 058 * 059 * @see JexlOperator 060 * @since 2.0 061 */ 062public class JexlArithmetic { 063 064 /** 065 * Helper interface used when creating an array literal. 066 * 067 * <p>The default implementation creates an array and attempts to type it strictly.</p> 068 * 069 * <ul> 070 * <li>If all objects are of the same type, the array returned will be an array of that same type</li> 071 * <li>If all objects are Numbers, the array returned will be an array of Numbers</li> 072 * <li>If all objects are convertible to a primitive type, the array returned will be an array 073 * of the primitive type</li> 074 * </ul> 075 */ 076 public interface ArrayBuilder { 077 078 /** 079 * Adds a literal to the array. 080 * 081 * @param value the item to add 082 */ 083 void add(Object value); 084 085 /** 086 * Creates the actual "array" instance. 087 * 088 * @param extended true when the last argument is ', ...' 089 * @return the array 090 */ 091 Object create(boolean extended); 092 } 093 094 /** Marker class for coercion operand exceptions. */ 095 public static class CoercionException extends ArithmeticException { 096 private static final long serialVersionUID = 202402081150L; 097 098 /** 099 * Constructs a new instance. 100 * 101 * @param msg the detail message. 102 */ 103 public CoercionException(final String msg) { 104 super(msg); 105 } 106 107 /** 108 * Constructs a new instance. 109 * 110 * @param msg the detail message. 111 * @param cause The cause of this Throwable. 112 * @since 3.5.0 113 */ 114 public CoercionException(final String msg, final Throwable cause) { 115 super(msg); 116 initCause(cause); 117 } 118 } 119 120 /** 121 * Helper interface used when creating a map literal. 122 * <p>The default implementation creates a java.util.HashMap.</p> 123 */ 124 public interface MapBuilder { 125 126 /** 127 * Creates the actual "map" instance. 128 * 129 * @return the map 130 */ 131 Object create(); 132 133 /** 134 * Adds a new entry to the map. 135 * 136 * @param key the map entry key 137 * @param value the map entry value 138 */ 139 void put(Object key, Object value); 140 } 141 142 /** Marker class for null operand exceptions. */ 143 public static class NullOperand extends ArithmeticException { 144 private static final long serialVersionUID = 4720876194840764770L; 145 146 /** Default constructor */ 147 public NullOperand() { 148 } // satisfy Javadoc 149 } 150 151 /** 152 * Helper interface used when creating a set literal. 153 * <p>The default implementation creates a java.util.HashSet.</p> 154 */ 155 public interface SetBuilder { 156 157 /** 158 * Adds a literal to the set. 159 * 160 * @param value the item to add 161 */ 162 void add(Object value); 163 164 /** 165 * Creates the actual "set" instance. 166 * 167 * @return the set 168 */ 169 Object create(); 170 } 171 172 /** 173 * The interface that uberspects JexlArithmetic classes. 174 * <p>This allows overloaded operator methods discovery.</p> 175 */ 176 public interface Uberspect { 177 178 /** 179 * Gets the most specific method for an operator. 180 * 181 * @param operator the operator 182 * @param args the arguments 183 * @return the most specific method or null if no specific override could be found 184 */ 185 JexlMethod getOperator(JexlOperator operator, Object... args); 186 187 /** 188 * Checks whether this uberspect has overloads for a given operator. 189 * 190 * @param operator the operator to check 191 * @return true if an overload exists, false otherwise 192 */ 193 boolean overloads(JexlOperator operator); 194 } 195 196 /** Double.MAX_VALUE as BigDecimal. */ 197 protected static final BigDecimal BIGD_DOUBLE_MAX_VALUE = BigDecimal.valueOf(Double.MAX_VALUE); 198 199 /** -Double.MAX_VALUE as BigDecimal. */ 200 protected static final BigDecimal BIGD_DOUBLE_MIN_VALUE = BigDecimal.valueOf(-Double.MAX_VALUE); 201 202 /** Long.MAX_VALUE as BigInteger. */ 203 protected static final BigInteger BIGI_LONG_MAX_VALUE = BigInteger.valueOf(Long.MAX_VALUE); 204 205 /** Long.MIN_VALUE as BigInteger. */ 206 protected static final BigInteger BIGI_LONG_MIN_VALUE = BigInteger.valueOf(Long.MIN_VALUE); 207 208 /** Default BigDecimal scale. */ 209 protected static final int BIGD_SCALE = -1; 210 211 /** 212 * The float regular expression pattern. 213 * <p> 214 * The decimal and exponent parts are optional and captured allowing to determine if the number is a real 215 * by checking whether one of these 2 capturing groups is not empty. 216 */ 217 public static final Pattern FLOAT_PATTERN = Pattern.compile("^[+-]?\\d*(\\.\\d*)?([eE][+-]?\\d+)?$"); 218 219 /** 220 * Attempts transformation of potential array in an abstract list or leave as is. 221 * <p>An array (as in int[]) is not convenient to call methods so when encountered we turn them into lists</p> 222 * 223 * @param container an array or on object 224 * @return an abstract list wrapping the array instance or the initial argument 225 * @see org.apache.commons.jexl3.internal.introspection.ArrayListWrapper 226 */ 227 private static Object arrayWrap(final Object container) { 228 return container.getClass().isArray() 229 ? new org.apache.commons.jexl3.internal.introspection.ArrayListWrapper(container) 230 : container; 231 } 232 233 private static boolean computeCompare321(final JexlArithmetic arithmetic) { 234 Class<?> arithmeticClass = arithmetic.getClass(); 235 while(arithmeticClass != JexlArithmetic.class) { 236 try { 237 final Method cmp = arithmeticClass.getDeclaredMethod("compare", Object.class, Object.class, String.class); 238 if (cmp.getDeclaringClass() != JexlArithmetic.class) { 239 return true; 240 } 241 } catch (final NoSuchMethodException xany) { 242 arithmeticClass = arithmeticClass.getSuperclass(); 243 } 244 } 245 return false; 246 } 247 248 /** 249 * Checks if the product of the arguments overflows a {@code long}. 250 * <p>see java8 Math.multiplyExact 251 * 252 * @param x the first value 253 * @param y the second value 254 * @param r the product 255 * @return true if product fits a long, false if it overflows 256 */ 257 @SuppressWarnings("MagicNumber") 258 protected static boolean isMultiplyExact(final long x, final long y, final long r) { 259 final long ax = Math.abs(x); 260 final long ay = Math.abs(y); 261 // Some bits greater than 2^31 that might cause overflow 262 // Check the result using the divide operator 263 // and check for the special case of Long.MIN_VALUE * -1 264 return !((ax | ay) >>> Integer.SIZE - 1 != 0 265 && (y != 0 && r / y != x 266 || x == Long.MIN_VALUE && y == -1)); 267 } 268 269 /** Whether this JexlArithmetic instance behaves in strict or lenient mode. */ 270 private final boolean strict; 271 272 /** The big decimal math context. */ 273 private final MathContext mathContext; 274 275 /** The big decimal scale. */ 276 private final int mathScale; 277 278 /** The dynamic constructor. */ 279 private final Constructor<? extends JexlArithmetic> ctor; 280 281 /** 282 * Determines if the compare method(Object, Object, String) is overriden in this class or one of its 283 * superclasses. 284 */ 285 private final boolean compare321 = computeCompare321(this); 286 287 /** 288 * Creates a JexlArithmetic. 289 * <p>If you derive your own arithmetic, implement the 290 * other constructor that may be needed when dealing with options. 291 * 292 * @param astrict whether this arithmetic is strict or lenient 293 */ 294 public JexlArithmetic(final boolean astrict) { 295 this(astrict, null, Integer.MIN_VALUE); 296 } 297 298 /** 299 * Creates a JexlArithmetic. 300 * <p>The constructor to define in derived classes. 301 * 302 * @param astrict whether this arithmetic is lenient or strict 303 * @param bigdContext the math context instance to use for +,-,/,*,% operations on big decimals. 304 * @param bigdScale the scale used for big decimals. 305 */ 306 public JexlArithmetic(final boolean astrict, final MathContext bigdContext, final int bigdScale) { 307 this.strict = astrict; 308 this.mathContext = bigdContext == null ? MathContext.DECIMAL128 : bigdContext; 309 this.mathScale = bigdScale == Integer.MIN_VALUE ? BIGD_SCALE : bigdScale; 310 Constructor<? extends JexlArithmetic> actor = null; 311 try { 312 actor = getClass().getConstructor(boolean.class, MathContext.class, int.class); 313 } catch (final Exception xany) { 314 // ignore 315 } 316 this.ctor = actor; 317 } 318 319 /** 320 * Add two values together. 321 * <p> 322 * If any numeric add fails on coercion to the appropriate type, 323 * treat as Strings and do concatenation. 324 * </p> 325 * 326 * @param left left argument 327 * @param right right argument 328 * @return left + right. 329 */ 330 public Object add(final Object left, final Object right) { 331 if (left == null && right == null) { 332 return controlNullNullOperands(JexlOperator.ADD); 333 } 334 final boolean strconcat = strict 335 ? left instanceof String || right instanceof String 336 : left instanceof String && right instanceof String; 337 if (!strconcat) { 338 try { 339 final boolean strictCast = isStrict(JexlOperator.ADD); 340 // if both (non-null) args fit as long 341 final Number ln = asLongNumber(strictCast, left); 342 final Number rn = asLongNumber(strictCast, right); 343 if (ln != null && rn != null) { 344 final long x = ln.longValue(); 345 final long y = rn.longValue(); 346 final long result = x + y; 347 // detect overflow, see java8 Math.addExact 348 if (((x ^ result) & (y ^ result)) < 0) { 349 return BigInteger.valueOf(x).add(BigInteger.valueOf(y)); 350 } 351 return narrowLong(left, right, result); 352 } 353 // if either are BigDecimal, use that type 354 if (left instanceof BigDecimal || right instanceof BigDecimal) { 355 final BigDecimal l = toBigDecimal(strictCast, left); 356 final BigDecimal r = toBigDecimal(strictCast, right); 357 return l.add(r, getMathContext()); 358 } 359 // if either are floating point (double or float), use double 360 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 361 final double l = toDouble(strictCast, left); 362 final double r = toDouble(strictCast, right); 363 return l + r; 364 } 365 // otherwise treat as BigInteger 366 final BigInteger l = toBigInteger(strictCast, left); 367 final BigInteger r = toBigInteger(strictCast, right); 368 final BigInteger result = l.add(r); 369 return narrowBigInteger(left, right, result); 370 } catch (final ArithmeticException nfe) { 371 // ignore and continue in sequence 372 } 373 } 374 return (left == null ? "" : toString(left)).concat(right == null ? "" : toString(right)); 375 } 376 377 /** 378 * Performs a bitwise and. 379 * 380 * @param left the left operand 381 * @param right the right operator 382 * @return left & right 383 */ 384 public Object and(final Object left, final Object right) { 385 final long l = toLong(left); 386 final long r = toLong(right); 387 return l & r; 388 } 389 390 /** 391 * Creates an array builder. 392 * 393 * @param size the number of elements in the array 394 * @return an array builder instance 395 * @deprecated since 3.3.1 396 */ 397 @Deprecated 398 public ArrayBuilder arrayBuilder(final int size) { 399 return arrayBuilder(size, false); 400 } 401 402 /** 403 * Called by the interpreter when evaluating a literal array. 404 * 405 * @param size the number of elements in the array 406 * @param extended whether the map is extended or not 407 * @return the array builder 408 */ 409 public ArrayBuilder arrayBuilder(final int size, final boolean extended) { 410 return new org.apache.commons.jexl3.internal.ArrayBuilder(size, extended); 411 } 412 413 /** 414 * Checks if value class is a number that can be represented exactly in a long. 415 * <p>For convenience, booleans are converted as 1/0 (true/false).</p> 416 * 417 * @param strict whether null argument is converted as 0 or remains null 418 * @param value argument 419 * @return a non-null value if argument can be represented by a long 420 */ 421 protected Number asLongNumber(final boolean strict, final Object value) { 422 if (value instanceof Long 423 || value instanceof Integer 424 || value instanceof Short 425 || value instanceof Byte) { 426 return (Number) value; 427 } 428 if (value instanceof Boolean) { 429 return (boolean) value ? 1L : 0L; 430 } 431 if (value instanceof AtomicBoolean) { 432 final AtomicBoolean b = (AtomicBoolean) value; 433 return b.get() ? 1L : 0L; 434 } 435 if (value == null && !strict) { 436 return 0L; 437 } 438 return null; 439 } 440 441 /** 442 * Checks if value class is a number that can be represented exactly in a long. 443 * <p>For convenience, booleans are converted as 1/0 (true/false).</p> 444 * 445 * @param value argument 446 * @return a non-null value if argument can be represented by a long 447 */ 448 protected Number asLongNumber(final Object value) { 449 return asLongNumber(strict, value); 450 } 451 452 /** 453 * Use or overload and() instead. 454 * 455 * @param lhs left hand side 456 * @param rhs right hand side 457 * @return lhs & rhs 458 * @see JexlArithmetic#and 459 * @deprecated 3.0 460 */ 461 @Deprecated 462 public final Object bitwiseAnd(final Object lhs, final Object rhs) { 463 return and(lhs, rhs); 464 } 465 466 /** 467 * Use or overload or() instead. 468 * 469 * @param lhs left hand side 470 * @param rhs right hand side 471 * @return lhs | rhs 472 * @see JexlArithmetic#or 473 * @deprecated 3.0 474 */ 475 @Deprecated 476 public final Object bitwiseOr(final Object lhs, final Object rhs) { 477 return or(lhs, rhs); 478 } 479 480 /** 481 * Use or overload xor() instead. 482 * 483 * @param lhs left hand side 484 * @param rhs right hand side 485 * @return lhs ^ rhs 486 * @see JexlArithmetic#xor 487 * @deprecated 3.0 488 */ 489 @Deprecated 490 public final Object bitwiseXor(final Object lhs, final Object rhs) { 491 return xor(lhs, rhs); 492 } 493 494 /** 495 * Checks whether a potential collection contains another. 496 * <p>Made protected to make it easier to override if needed.</p> 497 * 498 * @param collection the container which can be a collection or an array (even of primitive) 499 * @param value the value which can be a collection or an array (even of primitive) or a singleton 500 * @return test result or null if there is no arithmetic solution 501 */ 502 protected Boolean collectionContains(final Object collection, final Object value) { 503 // convert arrays if needed 504 final Object left = arrayWrap(collection); 505 if (left instanceof Collection) { 506 final Object right = arrayWrap(value); 507 if (right instanceof Collection) { 508 return ((Collection<?>) left).containsAll((Collection<?>) right); 509 } 510 return ((Collection<?>) left).contains(value); 511 } 512 return null; 513 } 514 515 /** 516 * Performs a comparison. 517 * 518 * @param left the left operand 519 * @param right the right operator 520 * @param operator the operator 521 * @return -1 if left < right; +1 if left > right; 0 if left == right 522 * @throws ArithmeticException if either left or right is null 523 */ 524 protected int compare(final Object left, final Object right, final JexlOperator operator) { 525 // this is a temporary way of allowing pre-3.3 code that overrode compare() to still call 526 // the user method. This method will merge with doCompare in 3.4 and the compare321 flag will disappear. 527 return compare321 528 ? compare(left, right, operator.toString()) 529 : doCompare(left, right, operator); 530 } 531 532 /** 533 * Any override of this method (pre 3.3) should be modified to match the new signature. 534 * 535 * @param left left operand 536 * @param right right operand 537 * @param symbol the operator symbol 538 * @return -1 if left < right; +1 if left > right; 0 if left == right 539 * {@link JexlArithmetic#compare(Object, Object, JexlOperator)} 540 * @deprecated 3.3 541 */ 542 @Deprecated 543 protected int compare(final Object left, final Object right, final String symbol) { 544 JexlOperator operator; 545 try { 546 operator = JexlOperator.valueOf(symbol); 547 } catch (final IllegalArgumentException xill) { 548 // ignore 549 operator = EQ; 550 } 551 return doCompare(left, right, operator); 552 } 553 554 /** 555 * Performs a bitwise complement. 556 * 557 * @param val the operand 558 * @return ~val 559 */ 560 public Object complement(final Object val) { 561 final boolean strictCast = isStrict(JexlOperator.COMPLEMENT); 562 final long l = toLong(strictCast, val); 563 return ~l; 564 } 565 566 /** 567 * Test if left contains right (right matches/in left). 568 * <p>Beware that this "contains " method arguments order is the opposite of the 569 * "in/matches" operator arguments. 570 * {@code x =~ y} means {@code y contains x} thus {@code contains(x, y)}.</p> 571 * <p>When this method returns null during evaluation, the operator code continues trying to find 572 * one through the uberspect.</p> 573 * 574 * @param container the container 575 * @param value the value 576 * @return test result or null if there is no arithmetic solution 577 */ 578 public Boolean contains(final Object container, final Object value) { 579 if (value == null && container == null) { 580 //if both are null L == R 581 return true; 582 } 583 if (value == null || container == null) { 584 // we know both aren't null, therefore L != R 585 return false; 586 } 587 // use arithmetic / pattern matching ? 588 if (container instanceof java.util.regex.Pattern) { 589 return ((java.util.regex.Pattern) container).matcher(value.toString()).matches(); 590 } 591 if (container instanceof CharSequence) { 592 return value.toString().matches(container.toString()); 593 } 594 // try contains on map key 595 if (container instanceof Map<?, ?>) { 596 if (value instanceof Map<?, ?>) { 597 return ((Map<?, ?>) container).keySet().containsAll(((Map<?, ?>) value).keySet()); 598 } 599 return ((Map<?, ?>) container).containsKey(value); 600 } 601 // try contains on collection 602 return collectionContains(container, value); 603 } 604 605 /** 606 * The result of +,/,-,*,% when both operands are null. 607 * 608 * @return Integer(0) if lenient 609 * @throws JexlArithmetic.NullOperand if strict 610 * @deprecated 3.3 611 */ 612 @Deprecated 613 protected Object controlNullNullOperands() { 614 if (isStrict()) { 615 throw new NullOperand(); 616 } 617 return 0; 618 } 619 620 /** 621 * The result of +,/,-,*,% when both operands are null. 622 * 623 * @param operator the actual operator 624 * @return Integer(0) if lenient 625 * @throws JexlArithmetic.NullOperand if strict-cast 626 * @since 3.3 627 */ 628 protected Object controlNullNullOperands(final JexlOperator operator) { 629 if (isStrict(operator)) { 630 throw new NullOperand(); 631 } 632 return 0; 633 } 634 635 /** 636 * Throws an NullOperand exception if arithmetic is strict-cast. 637 * 638 * @throws JexlArithmetic.NullOperand if strict 639 * @deprecated 3.3 640 */ 641 @Deprecated 642 protected void controlNullOperand() { 643 if (isStrict()) { 644 throw new NullOperand(); 645 } 646 } 647 648 /** 649 * Throws an NullOperand exception if arithmetic is strict-cast. 650 * <p>This method is called by the cast methods ({@link #toBoolean(boolean, Object)}, 651 * {@link #toInteger(boolean, Object)}, {@link #toDouble(boolean, Object)}, 652 * {@link #toString(boolean, Object)}, {@link #toBigInteger(boolean, Object)}, 653 * {@link #toBigDecimal(boolean, Object)}) when they encounter a null argument.</p> 654 * 655 * @param strictCast whether strict cast is required 656 * @param defaultValue the default value to return, if not strict 657 * @param <T> the value type 658 * @return the default value is strict is false 659 * @throws JexlArithmetic.NullOperand if strict-cast 660 * @since 3.3 661 */ 662 protected <T> T controlNullOperand(final boolean strictCast, final T defaultValue) { 663 if (strictCast) { 664 throw new NullOperand(); 665 } 666 return defaultValue; 667 } 668 669 /** 670 * The last method called before returning a result from a script execution. 671 * 672 * @param returned the returned value 673 * @return the controlled returned value 674 */ 675 public Object controlReturn(final Object returned) { 676 return returned; 677 } 678 679 /** 680 * Creates a literal range. 681 * <p>The default implementation only accepts integers and longs.</p> 682 * 683 * @param from the included lower bound value (null if none) 684 * @param to the included upper bound value (null if none) 685 * @return the range as an iterable 686 * @throws ArithmeticException as an option if creation fails 687 */ 688 public Iterable<?> createRange(final Object from, final Object to) throws ArithmeticException { 689 final long lfrom = toLong(from); 690 final long lto = toLong(to); 691 if (lfrom >= Integer.MIN_VALUE && lfrom <= Integer.MAX_VALUE 692 && lto >= Integer.MIN_VALUE && lto <= Integer.MAX_VALUE) { 693 return org.apache.commons.jexl3.internal.IntegerRange.create((int) lfrom, (int) lto); 694 } 695 return org.apache.commons.jexl3.internal.LongRange.create(lfrom, lto); 696 } 697 698 /** 699 * Creates a JexlArithmetic instance. 700 * Called by options(...) method when another instance of the same class of arithmetic is required. 701 * 702 * @see #options(org.apache.commons.jexl3.JexlEngine.Options) 703 * @param astrict whether this arithmetic is lenient or strict 704 * @param bigdContext the math context instance to use for +,-,/,*,% operations on big decimals. 705 * @param bigdScale the scale used for big decimals. 706 * @return default is a new JexlArithmetic instance 707 * @since 3.1 708 */ 709 protected JexlArithmetic createWithOptions(final boolean astrict, final MathContext bigdContext, final int bigdScale) { 710 if (ctor != null) { 711 try { 712 return ctor.newInstance(astrict, bigdContext, bigdScale); 713 } catch (IllegalAccessException | IllegalArgumentException 714 | InstantiationException | InvocationTargetException xany) { 715 // it was worth the try 716 } 717 } 718 return new JexlArithmetic(astrict, bigdContext, bigdScale); 719 } 720 721 /** 722 * Decrements argument by 1. 723 * 724 * @param val the argument 725 * @return val - 1 726 */ 727 public Object decrement(final Object val) { 728 return increment(val, -1); 729 } 730 731 /** 732 * Divide the left value by the right. 733 * 734 * @param left left argument 735 * @param right right argument 736 * @return left / right 737 * @throws ArithmeticException if right == 0 738 */ 739 public Object divide(final Object left, final Object right) { 740 if (left == null && right == null) { 741 return controlNullNullOperands(JexlOperator.DIVIDE); 742 } 743 final boolean strictCast = isStrict(JexlOperator.DIVIDE); 744 // if both (non-null) args fit as long 745 final Number ln = asLongNumber(strictCast, left); 746 final Number rn = asLongNumber(strictCast, right); 747 if (ln != null && rn != null) { 748 final long x = ln.longValue(); 749 final long y = rn.longValue(); 750 if (y == 0L) { 751 throw new ArithmeticException("/"); 752 } 753 final long result = x / y; 754 return narrowLong(left, right, result); 755 } 756 // if either are BigDecimal, use that type 757 if (left instanceof BigDecimal || right instanceof BigDecimal) { 758 final BigDecimal l = toBigDecimal(strictCast, left); 759 final BigDecimal r = toBigDecimal(strictCast, right); 760 if (BigDecimal.ZERO.equals(r)) { 761 throw new ArithmeticException("/"); 762 } 763 return l.divide(r, getMathContext()); 764 } 765 // if either are floating point (double or float), use double 766 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 767 final double l = toDouble(strictCast, left); 768 final double r = toDouble(strictCast, right); 769 if (r == 0.0) { 770 throw new ArithmeticException("/"); 771 } 772 return l / r; 773 } 774 // otherwise treat as BigInteger 775 final BigInteger l = toBigInteger(strictCast, left); 776 final BigInteger r = toBigInteger(strictCast, right); 777 if (BigInteger.ZERO.equals(r)) { 778 throw new ArithmeticException("/"); 779 } 780 final BigInteger result = l.divide(r); 781 return narrowBigInteger(left, right, result); 782 } 783 784 private int doCompare(final Object left, final Object right, final JexlOperator operator) { 785 final boolean strictCast = isStrict(operator); 786 if (left != null && right != null) { 787 try { 788 if (left instanceof BigDecimal || right instanceof BigDecimal) { 789 final BigDecimal l = toBigDecimal(strictCast, left); 790 final BigDecimal r = toBigDecimal(strictCast, right); 791 return l.compareTo(r); 792 } 793 if (left instanceof BigInteger || right instanceof BigInteger) { 794 final BigInteger l = toBigInteger(strictCast, left); 795 final BigInteger r = toBigInteger(strictCast, right); 796 return l.compareTo(r); 797 } 798 if (isFloatingPoint(left) || isFloatingPoint(right)) { 799 final double lhs = toDouble(strictCast, left); 800 final double rhs = toDouble(strictCast, right); 801 if (Double.isNaN(lhs)) { 802 if (Double.isNaN(rhs)) { 803 return 0; 804 } 805 return -1; 806 } 807 if (Double.isNaN(rhs)) { 808 // lhs is not NaN 809 return +1; 810 } 811 return Double.compare(lhs, rhs); 812 } 813 if (isNumberable(left) || isNumberable(right)) { 814 final long lhs = toLong(strictCast, left); 815 final long rhs = toLong(strictCast, right); 816 return Long.compare(lhs, rhs); 817 } 818 if (left instanceof String || right instanceof String) { 819 return toString(left).compareTo(toString(right)); 820 } 821 } catch (final CoercionException ignore) { 822 // ignore it, continue in sequence 823 } 824 if (EQ == operator) { 825 return left.equals(right) ? 0 : -1; 826 } 827 if (left instanceof Comparable<?>) { 828 @SuppressWarnings("unchecked") // OK because of instanceof check above 829 final Comparable<Object> comparable = (Comparable<Object>) left; 830 try { 831 return comparable.compareTo(right); 832 } catch (final ClassCastException castException) { 833 // ignore it, continue in sequence 834 } 835 } 836 if (right instanceof Comparable<?>) { 837 @SuppressWarnings("unchecked") // OK because of instanceof check above 838 final Comparable<Object> comparable = (Comparable<Object>) right; 839 try { 840 return -Integer.signum(comparable.compareTo(left)); 841 } catch (final ClassCastException castException) { 842 // ignore it, continue in sequence 843 } 844 } 845 } 846 throw new ArithmeticException("Object comparison:(" + left + 847 " " + operator.getOperatorSymbol() 848 + " " + right + ")"); 849 } 850 851 /** 852 * Check for emptiness of various types: Number, Collection, Array, Map, String. 853 * <p>Override or overload this method to add new signatures to the size operators. 854 * 855 * @param object the object to check the emptiness of 856 * @return the boolean or false if object is not null 857 * @since 3.2 858 */ 859 public Boolean empty(final Object object) { 860 return object == null || isEmpty(object, false); 861 } 862 863 /** 864 * Test if left ends with right. 865 * 866 * @param left left argument 867 * @param right right argument 868 * @return left $= right if there is no arithmetic solution 869 */ 870 public Boolean endsWith(final Object left, final Object right) { 871 if (left == null && right == null) { 872 //if both are null L == R 873 return true; 874 } 875 if (left == null || right == null) { 876 // we know both aren't null, therefore L != R 877 return false; 878 } 879 if (left instanceof CharSequence) { 880 return toString(left).endsWith(toString(right)); 881 } 882 return null; 883 } 884 885 /** 886 * Test if left and right are equal. 887 * 888 * @param left left argument 889 * @param right right argument 890 * @return the test result 891 */ 892 public boolean equals(final Object left, final Object right) { 893 if (left == right) { 894 return true; 895 } 896 if (left == null || right == null) { 897 return false; 898 } 899 final boolean strictCast = isStrict(EQ); 900 if (left instanceof Boolean || right instanceof Boolean) { 901 return toBoolean(left) == toBoolean(strictCast, right); 902 } 903 return compare(left, right, EQ) == 0; 904 } 905 906 /** 907 * The MathContext instance used for +,-,/,*,% operations on big decimals. 908 * 909 * @return the math context 910 */ 911 public MathContext getMathContext() { 912 return mathContext; 913 } 914 915 /** 916 * The BigDecimal scale used for comparison and coercion operations. 917 * 918 * @return the scale 919 */ 920 public int getMathScale() { 921 return mathScale; 922 } 923 924 /** 925 * Test if left > right. 926 * 927 * @param left left argument 928 * @param right right argument 929 * @return the test result 930 */ 931 public boolean greaterThan(final Object left, final Object right) { 932 if (left == right || left == null || right == null) { 933 return false; 934 } 935 return compare(left, right, JexlOperator.GT) > 0; 936 } 937 938 /** 939 * Test if left >= right. 940 * 941 * @param left left argument 942 * @param right right argument 943 * @return the test result 944 */ 945 public boolean greaterThanOrEqual(final Object left, final Object right) { 946 if (left == right) { 947 return true; 948 } 949 if (left == null || right == null) { 950 return false; 951 } 952 return compare(left, right, JexlOperator.GTE) >= 0; 953 } 954 955 /** 956 * Increments argument by 1. 957 * 958 * @param val the argument 959 * @return val + 1 960 */ 961 public Object increment(final Object val) { 962 return increment(val, 1); 963 } 964 965 /** 966 * Add value to number argument. 967 * 968 * @param val the number 969 * @param incr the value to add 970 * @return val + incr 971 */ 972 protected Object increment(final Object val, final int incr) { 973 if (val == null) { 974 return incr; 975 } 976 if (val instanceof Integer) { 977 return (Integer) val + incr; 978 } 979 if (val instanceof Double) { 980 return (Double) val + incr; 981 } 982 if (val instanceof Long) { 983 return (Long) val + incr; 984 } 985 if (val instanceof BigDecimal) { 986 final BigDecimal bd = (BigDecimal) val; 987 return bd.add(BigDecimal.valueOf(incr), this.mathContext); 988 } 989 if (val instanceof BigInteger) { 990 final BigInteger bi = (BigInteger) val; 991 return bi.add(BigInteger.valueOf(incr)); 992 } 993 if (val instanceof Float) { 994 return (Float) val + incr; 995 } 996 if (val instanceof Short) { 997 return (short) ((Short) val + incr); 998 } 999 if (val instanceof Byte) { 1000 return (byte) ((Byte) val + incr); 1001 } 1002 throw new ArithmeticException("Object "+(incr < 0? "decrement":"increment")+":(" + val + ")"); 1003 } 1004 1005 /** 1006 * Check for emptiness of various types: Number, Collection, Array, Map, String. 1007 * 1008 * @param object the object to check the emptiness of 1009 * @return the boolean or null if there is no arithmetic solution 1010 */ 1011 public Boolean isEmpty(final Object object) { 1012 return isEmpty(object, object == null); 1013 } 1014 1015 /** 1016 * Check for emptiness of various types: Number, Collection, Array, Map, String. 1017 * 1018 * @param object the object to check the emptiness of 1019 * @param def the default value if object emptiness cannot be determined 1020 * @return the boolean or null if there is no arithmetic solution 1021 */ 1022 public Boolean isEmpty(final Object object, final Boolean def) { 1023 if (object != null) { 1024 if (object instanceof Number) { 1025 final double d = ((Number) object).doubleValue(); 1026 return Double.isNaN(d) || d == 0.d; 1027 } 1028 if (object instanceof CharSequence) { 1029 return ((CharSequence) object).length() == 0; 1030 } 1031 if (object.getClass().isArray()) { 1032 return Array.getLength(object) == 0; 1033 } 1034 if (object instanceof Collection<?>) { 1035 return ((Collection<?>) object).isEmpty(); 1036 } 1037 // Map isn't a collection 1038 if (object instanceof Map<?, ?>) { 1039 return ((Map<?, ?>) object).isEmpty(); 1040 } 1041 } 1042 return def; 1043 } 1044 1045 /** 1046 * Is Object a floating point number. 1047 * 1048 * @param o Object to be analyzed. 1049 * @return true if it is a Float or a Double. 1050 */ 1051 protected boolean isFloatingPoint(final Object o) { 1052 return o instanceof Float || o instanceof Double; 1053 } 1054 1055 /** 1056 * Test if the passed value is a floating point number, i.e. a float, double 1057 * or string with ( "." | "E" | "e"). 1058 * 1059 * @param val the object to be tested 1060 * @return true if it is, false otherwise. 1061 */ 1062 protected boolean isFloatingPointNumber(final Object val) { 1063 if (val instanceof Float || val instanceof Double) { 1064 return true; 1065 } 1066 if (val instanceof CharSequence) { 1067 final Matcher m = FLOAT_PATTERN.matcher((CharSequence) val); 1068 // first matcher group is decimal, second is exponent 1069 // one of them must exist hence start({1,2}) >= 0 1070 return m.matches() && (m.start(1) >= 0 || m.start(2) >= 0); 1071 } 1072 return false; 1073 } 1074 1075 /** 1076 * Tests whether negate called with a given argument will always return the same result. 1077 * <p>This is used to determine whether negate results on number literals can be cached. 1078 * If the result on calling negate with the same constant argument may change between calls, 1079 * which means the function is not deterministic, this method must return false. 1080 * 1081 * @return true if negate is idempotent, false otherwise 1082 */ 1083 public boolean isNegateStable() { 1084 return true; 1085 } 1086 1087 /** 1088 * Checks if an operand is considered null. 1089 * 1090 * @param value the operand 1091 * @return true if operand is considered null 1092 */ 1093 protected boolean isNullOperand(final Object value) { 1094 return value == null; 1095 } 1096 1097 /** 1098 * Is Object a whole number. 1099 * 1100 * @param o Object to be analyzed. 1101 * @return true if Integer, Long, Byte, Short or Character. 1102 */ 1103 protected boolean isNumberable(final Object o) { 1104 return o instanceof Integer 1105 || o instanceof Long 1106 || o instanceof Byte 1107 || o instanceof Short 1108 || o instanceof Character; 1109 } 1110 1111 /** 1112 * Tests whether positivize called with a given argument will always return the same result. 1113 * <p>This is used to determine whether positivize results on number literals can be cached. 1114 * If the result on calling positivize with the same constant argument may change between calls, 1115 * which means the function is not deterministic, this method must return false. 1116 * 1117 * @return true if positivize is idempotent, false otherwise 1118 */ 1119 public boolean isPositivizeStable() { 1120 return true; 1121 } 1122 1123 /** 1124 * Checks whether this JexlArithmetic instance 1125 * strictly considers null as an error when used as operand unexpectedly. 1126 * 1127 * @return true if strict, false if lenient 1128 */ 1129 public boolean isStrict() { 1130 return strict; 1131 } 1132 1133 /** 1134 * Tests whether this arithmetic considers a given operator as strict or null-safe. 1135 * <p>When an operator is strict, it does <em>not</em> accept null arguments when the arithmetic is strict. 1136 * If null-safe (ie not-strict), the operator does accept null arguments even if the arithmetic itself is strict.</p> 1137 * <p>The default implementation considers equal/not-equal operators as null-safe so one can check for null as in 1138 * <code>if (myvar == null) {...}</code>. Note that this operator is used for equal and not-equal syntax. The complete 1139 * list of operators that are not strict are (==, [], []=, ., .=, empty, size, contains).</p> 1140 * <p>An arithmetic refining its strict behavior handling for more operators must declare which by overriding 1141 * this method.</p> 1142 * 1143 * @param operator the operator to check for null-argument(s) handling 1144 * @return true if operator considers null arguments as errors, false if operator has appropriate semantics 1145 * for null argument(s) 1146 */ 1147 public boolean isStrict(final JexlOperator operator) { 1148 if (operator != null) { 1149 switch (operator) { 1150 case EQ: 1151 case EQSTRICT: 1152 case ARRAY_GET: 1153 case ARRAY_SET: 1154 case PROPERTY_GET: 1155 case PROPERTY_SET: 1156 case EMPTY: 1157 case SIZE: 1158 case CONTAINS: 1159 return false; 1160 default: 1161 return isStrict(); 1162 } 1163 } 1164 return isStrict(); 1165 } 1166 1167 /** 1168 * Tests if left < right. 1169 * 1170 * @param left left argument 1171 * @param right right argument 1172 * @return the test result 1173 */ 1174 public boolean lessThan(final Object left, final Object right) { 1175 if (left == right || left == null || right == null) { 1176 return false; 1177 } 1178 return compare(left, right, JexlOperator.LT) < 0; 1179 1180 } 1181 1182 /** 1183 * Tests if left <= right. 1184 * 1185 * @param left left argument 1186 * @param right right argument 1187 * @return the test result 1188 */ 1189 public boolean lessThanOrEqual(final Object left, final Object right) { 1190 if (left == right) { 1191 return true; 1192 } 1193 if (left == null || right == null) { 1194 return false; 1195 } 1196 return compare(left, right, JexlOperator.LTE) <= 0; 1197 } 1198 1199 /** 1200 * Use or overload not() instead. 1201 * 1202 * @param arg argument 1203 * @return !arg 1204 * @see JexlArithmetic#not 1205 * @deprecated 3.0 1206 */ 1207 @Deprecated 1208 public final Object logicalNot(final Object arg) { 1209 return not(arg); 1210 } 1211 1212 /** 1213 * Creates a map-builder. 1214 * 1215 * @param size the number of elements in the map 1216 * @return a map-builder instance 1217 * @deprecated 3.3 1218 */ 1219 @Deprecated 1220 public MapBuilder mapBuilder(final int size) { 1221 return mapBuilder(size, false); 1222 } 1223 1224 /** 1225 * Called by the interpreter when evaluating a literal map. 1226 * 1227 * @param size the number of elements in the map 1228 * @param extended whether the map is extended or not 1229 * @return the map builder 1230 */ 1231 public MapBuilder mapBuilder(final int size, final boolean extended) { 1232 return new org.apache.commons.jexl3.internal.MapBuilder(size, extended); 1233 } 1234 1235 /** 1236 * Use or overload contains() instead. 1237 * 1238 * @param lhs left hand side 1239 * @param rhs right hand side 1240 * @return contains(rhs, lhs) 1241 * @see JexlArithmetic#contains 1242 * @deprecated 3.0 1243 */ 1244 @Deprecated 1245 public final Object matches(final Object lhs, final Object rhs) { 1246 return contains(rhs, lhs); 1247 } 1248 1249 /** 1250 * left value modulo right. 1251 * 1252 * @param left left argument 1253 * @param right right argument 1254 * @return left % right 1255 * @throws ArithmeticException if right == 0.0 1256 */ 1257 public Object mod(final Object left, final Object right) { 1258 if (left == null && right == null) { 1259 return controlNullNullOperands(JexlOperator.MOD); 1260 } 1261 final boolean strictCast = isStrict(JexlOperator.MOD); 1262 // if both (non-null) args fit as long 1263 final Number ln = asLongNumber(strictCast, left); 1264 final Number rn = asLongNumber(strictCast, right); 1265 if (ln != null && rn != null) { 1266 final long x = ln.longValue(); 1267 final long y = rn.longValue(); 1268 if (y == 0L) { 1269 throw new ArithmeticException("%"); 1270 } 1271 final long result = x % y; 1272 return narrowLong(left, right, result); 1273 } 1274 // if either are BigDecimal, use that type 1275 if (left instanceof BigDecimal || right instanceof BigDecimal) { 1276 final BigDecimal l = toBigDecimal(strictCast, left); 1277 final BigDecimal r = toBigDecimal(strictCast, right); 1278 if (BigDecimal.ZERO.equals(r)) { 1279 throw new ArithmeticException("%"); 1280 } 1281 return l.remainder(r, getMathContext()); 1282 } 1283 // if either are floating point (double or float), use double 1284 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 1285 final double l = toDouble(strictCast, left); 1286 final double r = toDouble(strictCast, right); 1287 if (r == 0.0) { 1288 throw new ArithmeticException("%"); 1289 } 1290 return l % r; 1291 } 1292 // otherwise treat as BigInteger 1293 final BigInteger l = toBigInteger(strictCast, left); 1294 final BigInteger r = toBigInteger(strictCast, right); 1295 if (BigInteger.ZERO.equals(r)) { 1296 throw new ArithmeticException("%"); 1297 } 1298 final BigInteger result = l.mod(r); 1299 return narrowBigInteger(left, right, result); 1300 } 1301 1302 /** 1303 * Multiply the left value by the right. 1304 * 1305 * @param left left argument 1306 * @param right right argument 1307 * @return left * right. 1308 */ 1309 public Object multiply(final Object left, final Object right) { 1310 if (left == null && right == null) { 1311 return controlNullNullOperands(JexlOperator.MULTIPLY); 1312 } 1313 final boolean strictCast = isStrict(JexlOperator.MULTIPLY); 1314 // if both (non-null) args fit as long 1315 final Number ln = asLongNumber(strictCast, left); 1316 final Number rn = asLongNumber(strictCast, right); 1317 if (ln != null && rn != null) { 1318 final long x = ln.longValue(); 1319 final long y = rn.longValue(); 1320 final long result = x * y; 1321 // detect overflow 1322 if (!isMultiplyExact(x, y, result)) { 1323 return BigInteger.valueOf(x).multiply(BigInteger.valueOf(y)); 1324 } 1325 return narrowLong(left, right, result); 1326 } 1327 // if either are BigDecimal, use that type 1328 if (left instanceof BigDecimal || right instanceof BigDecimal) { 1329 final BigDecimal l = toBigDecimal(strictCast, left); 1330 final BigDecimal r = toBigDecimal(strictCast, right); 1331 return l.multiply(r, getMathContext()); 1332 } 1333 // if either are floating point (double or float), use double 1334 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 1335 final double l = toDouble(strictCast, left); 1336 final double r = toDouble(strictCast, right); 1337 return l * r; 1338 } 1339 // otherwise treat as BigInteger 1340 final BigInteger l = toBigInteger(strictCast, left); 1341 final BigInteger r = toBigInteger(strictCast, right); 1342 final BigInteger result = l.multiply(r); 1343 return narrowBigInteger(left, right, result); 1344 } 1345 1346 /** 1347 * Narrows a double to a float if there is no information loss. 1348 * 1349 * @param value the double value 1350 * @param narrow the target narrow class 1351 * @return the narrowed or initial number 1352 */ 1353 private Number narrow(final Class<?> narrow, final double value) { 1354 return narrowAccept(narrow, Float.class) && (float) value == value 1355 ? (float) value 1356 : value; 1357 } 1358 1359 /** 1360 * Given a Number, return the value using the smallest type the result 1361 * will fit into. 1362 * <p>This works hand in hand with parameter 'widening' in Java 1363 * method calls, e.g. a call to substring(int, int) with an int and a long 1364 * will fail, but a call to substring(int, int) with an int and a short will 1365 * succeed.</p> 1366 * 1367 * @param original the original number. 1368 * @return a value of the smallest type the original number will fit into. 1369 */ 1370 public Number narrow(final Number original) { 1371 return narrowNumber(original, null); 1372 } 1373 1374 /** 1375 * Tests whether we consider the narrow class as a potential candidate for narrowing the source. 1376 * 1377 * @param narrow the target narrow class 1378 * @param source the original source class 1379 * @return true if attempt to narrow source to target is accepted 1380 */ 1381 protected boolean narrowAccept(final Class<?> narrow, final Class<?> source) { 1382 return narrow == null || narrow.equals(source); 1383 } 1384 1385 /** 1386 * Replace all numbers in an arguments array with the smallest type that will fit. 1387 * 1388 * @param args the argument array 1389 * @return true if some arguments were narrowed and args array is modified, 1390 * false if no narrowing occurred and args array has not been modified 1391 */ 1392 public boolean narrowArguments(final Object[] args) { 1393 boolean narrowed = false; 1394 if (args != null) { 1395 for (int a = 0; a < args.length; ++a) { 1396 final Object arg = args[a]; 1397 if (arg instanceof Number) { 1398 final Number narg = (Number) arg; 1399 final Number narrow = narrow(narg); 1400 if (!narg.equals(narrow)) { 1401 args[a] = narrow; 1402 narrowed = true; 1403 } 1404 } 1405 } 1406 } 1407 return narrowed; 1408 } 1409 1410 /** 1411 * Given a BigDecimal, attempt to narrow it to an Integer or Long if it fits and 1412 * one of the arguments is numberable. 1413 * 1414 * @param lhs the left-hand side operand that lead to the bigd result 1415 * @param rhs the right-hand side operand that lead to the bigd result 1416 * @param big the BigDecimal to narrow 1417 * @return an Integer or Long if narrowing is possible, the original BigDecimal otherwise 1418 */ 1419 protected Number narrowBigDecimal(final Object lhs, final Object rhs, final BigDecimal big) { 1420 return narrowToLong(lhs, rhs, big, BigDecimal::longValueExact); 1421 } 1422 1423 /** 1424 * Given a BigInteger, narrow it to an Integer or Long if it fits and the arguments 1425 * class allow it. 1426 * <p> 1427 * The rules are: 1428 * if either arguments is a BigInteger, no narrowing will occur 1429 * if either arguments is a Long, no narrowing to Integer will occur 1430 * </p> 1431 * 1432 * @param lhs the left-hand side operand that lead to the bigi result 1433 * @param rhs the right-hand side operand that lead to the bigi result 1434 * @param big the BigInteger to narrow 1435 * @return an Integer or Long if narrowing is possible, the original BigInteger otherwise 1436 */ 1437 protected Number narrowBigInteger(final Object lhs, final Object rhs, final BigInteger big) { 1438 return narrowToLong(lhs, rhs, big, BigInteger::longValueExact); 1439 } 1440 1441 /** 1442 * Given a generic number, attempt to narrow it to an Integer or Long if it fits and 1443 * one of the arguments is numberable. 1444 * 1445 * @param lhs the left-hand side operand that lead to the big result 1446 * @param rhs the right-hand side operand that lead to the big result 1447 * @param big the number to narrow 1448 * @param toLongFunction the function to convert the number to a long 1449 * @param <X> the number type 1450 * @return an Integer or Long if narrowing is possible, the original number otherwise 1451 */ 1452 protected <X extends Number> Number narrowToLong(final Object lhs, final Object rhs, final X big, final ToLongFunction<X> toLongFunction) { 1453 if (isNumberable(lhs) || isNumberable(rhs)) { 1454 try { 1455 final long l = toLongFunction.applyAsLong(big); 1456 // coerce to int when possible (int being so often used in method parms) 1457 if ((int) l == l) { 1458 return (int) l; 1459 } 1460 return l; 1461 } catch (final ArithmeticException xa) { 1462 // ignore, no exact value possible 1463 } 1464 } 1465 return big; 1466 } 1467 1468 /** 1469 * Given a long, attempt to narrow it to an int. 1470 * <p>Narrowing will only occur if no operand is a Long. 1471 * 1472 * @param lhs the left hand side operand that lead to the long result 1473 * @param rhs the right hand side operand that lead to the long result 1474 * @param r the long to narrow 1475 * @return an Integer if narrowing is possible, the original Long otherwise 1476 */ 1477 protected Number narrowLong(final Object lhs, final Object rhs, final long r) { 1478 if (!(lhs instanceof Long || rhs instanceof Long) && (int) r == r) { 1479 return (int) r; 1480 } 1481 return r; 1482 } 1483 1484 /** 1485 * Given a Number, return the value attempting to narrow it to a target class. 1486 * 1487 * @param original the original number 1488 * @param narrow the attempted target class 1489 * @return the narrowed number or the source if no narrowing was possible 1490 */ 1491 public Number narrowNumber(final Number original, final Class<?> narrow) { 1492 if (original != null) { 1493 final long value; 1494 if (original instanceof BigDecimal) { 1495 final BigDecimal big = (BigDecimal) original; 1496 try { 1497 // can it be represented as a long? 1498 value = big.longValueExact(); 1499 // continue in sequence to try and further reduce 1500 } catch (final ArithmeticException xa) { 1501 // if it is bigger than a double, it cannot be narrowed 1502 if (big.compareTo(BIGD_DOUBLE_MAX_VALUE) > 0 1503 || big.compareTo(BIGD_DOUBLE_MIN_VALUE) < 0) { 1504 return original; 1505 } 1506 // represent as double 1507 return narrow(narrow, original.doubleValue()); 1508 } 1509 // this continues with value as long 1510 } else { 1511 if (isFloatingPoint(original)) { 1512 final double doubleValue = original.doubleValue(); 1513 // if it is not equivalent to a Long... 1514 if ((long) doubleValue != doubleValue) { 1515 return narrow(narrow, doubleValue); 1516 } 1517 // else it can be represented as a long 1518 } else if (original instanceof BigInteger) { 1519 final BigInteger bigi = (BigInteger) original; 1520 // if it is bigger than a Long, it cannot be narrowed 1521 if (!BigInteger.valueOf(bigi.longValue()).equals(bigi)) { 1522 return original; 1523 } 1524 // else it can be represented as a long 1525 } 1526 value = original.longValue(); 1527 } 1528 // it can be represented as a long; determine the smallest possible numberable representation 1529 if (narrowAccept(narrow, Byte.class) && (byte) value == value) { 1530 // it will fit in a byte 1531 return (byte) value; 1532 } 1533 if (narrowAccept(narrow, Short.class) && (short) value == value) { 1534 return (short) value; 1535 } 1536 if (narrowAccept(narrow, Integer.class) && (int) value == value) { 1537 return (int) value; 1538 } 1539 } 1540 return original; 1541 } 1542 1543 /** 1544 * Negates a value (unary minus for numbers). 1545 * 1546 * @see #isNegateStable() 1547 * @param val the value to negate 1548 * @return the negated value 1549 */ 1550 public Object negate(final Object val) { 1551 if (val == null) { 1552 return null; 1553 } 1554 if (val instanceof Integer) { 1555 return -((Integer) val); 1556 } 1557 if (val instanceof Double) { 1558 return - ((Double) val); 1559 } 1560 if (val instanceof Long) { 1561 return -((Long) val); 1562 } 1563 if (val instanceof BigDecimal) { 1564 return ((BigDecimal) val).negate(); 1565 } 1566 if (val instanceof BigInteger) { 1567 return ((BigInteger) val).negate(); 1568 } 1569 if (val instanceof Float) { 1570 return -((Float) val); 1571 } 1572 if (val instanceof Short) { 1573 return (short) -((Short) val); 1574 } 1575 if (val instanceof Byte) { 1576 return (byte) -((Byte) val); 1577 } 1578 if (val instanceof Boolean) { 1579 return !(Boolean) val; 1580 } 1581 if (val instanceof AtomicBoolean) { 1582 return !((AtomicBoolean) val).get(); 1583 } 1584 throw new ArithmeticException("Object negate:(" + val + ")"); 1585 } 1586 1587 /** 1588 * Performs a logical not. 1589 * 1590 * @param val the operand 1591 * @return !val 1592 */ 1593 public Object not(final Object val) { 1594 final boolean strictCast = isStrict(JexlOperator.NOT); 1595 return !toBoolean(strictCast, val); 1596 } 1597 1598 /** 1599 * Apply options to this arithmetic which eventually may create another instance. 1600 * 1601 * @see #createWithOptions(boolean, java.math.MathContext, int) 1602 * @param context the context that may extend {@link JexlContext.OptionsHandle} to use 1603 * @return a new arithmetic instance or this 1604 * @since 3.1 1605 */ 1606 public JexlArithmetic options(final JexlContext context) { 1607 if (context instanceof JexlContext.OptionsHandle) { 1608 return options(((JexlContext.OptionsHandle) context).getEngineOptions()); 1609 } 1610 if (context instanceof JexlEngine.Options) { 1611 return options((JexlEngine.Options) context); 1612 } 1613 return this; 1614 } 1615 1616 /** 1617 * Apply options to this arithmetic which eventually may create another instance. 1618 * 1619 * @see #createWithOptions(boolean, java.math.MathContext, int) 1620 * @param options the {@link JexlEngine.Options} to use 1621 * @return an arithmetic with those options set 1622 * @deprecated 3.2 1623 */ 1624 @Deprecated 1625 public JexlArithmetic options(final JexlEngine.Options options) { 1626 if (options != null) { 1627 final boolean isstrict = Boolean.TRUE == options.isStrictArithmetic() || isStrict(); 1628 MathContext bigdContext = options.getArithmeticMathContext(); 1629 if (bigdContext == null) { 1630 bigdContext = getMathContext(); 1631 } 1632 int bigdScale = options.getArithmeticMathScale(); 1633 if (bigdScale == Integer.MIN_VALUE) { 1634 bigdScale = getMathScale(); 1635 } 1636 if (isstrict != isStrict() 1637 || bigdScale != getMathScale() 1638 || bigdContext != getMathContext()) { 1639 return createWithOptions(isstrict, bigdContext, bigdScale); 1640 } 1641 } 1642 return this; 1643 } 1644 1645 /** 1646 * Apply options to this arithmetic which eventually may create another instance. 1647 * 1648 * @see #createWithOptions(boolean, java.math.MathContext, int) 1649 * @param options the {@link JexlEngine.Options} to use 1650 * @return an arithmetic with those options set 1651 */ 1652 public JexlArithmetic options(final JexlOptions options) { 1653 if (options != null) { 1654 final boolean ostrict = options.isStrictArithmetic(); 1655 MathContext bigdContext = options.getMathContext(); 1656 if (bigdContext == null) { 1657 bigdContext = getMathContext(); 1658 } 1659 int bigdScale = options.getMathScale(); 1660 if (bigdScale == Integer.MIN_VALUE) { 1661 bigdScale = getMathScale(); 1662 } 1663 if (ostrict != isStrict() 1664 || bigdScale != getMathScale() 1665 || bigdContext != getMathContext()) { 1666 return createWithOptions(ostrict, bigdContext, bigdScale); 1667 } 1668 } 1669 return this; 1670 } 1671 1672 /** 1673 * Performs a bitwise or. 1674 * 1675 * @param left the left operand 1676 * @param right the right operator 1677 * @return left | right 1678 */ 1679 public Object or(final Object left, final Object right) { 1680 final long l = toLong(left); 1681 final long r = toLong(right); 1682 return l | r; 1683 } 1684 1685 /** 1686 * Convert a string to a BigDecimal. 1687 * <>Empty string is considered as 0.</> 1688 * 1689 * @param arg the arg 1690 * @return a BigDecimal 1691 * @throws CoercionException if the string cannot be coerced into a BigDecimal 1692 */ 1693 private BigDecimal parseBigDecimal(final String arg) throws ArithmeticException { 1694 try { 1695 return arg.isEmpty()? BigDecimal.ZERO : new BigDecimal(arg, getMathContext()); 1696 } catch (final NumberFormatException e) { 1697 throw new CoercionException("BigDecimal coercion: ("+ arg +")", e); 1698 } 1699 } 1700 1701 /** 1702 * Converts a string to a big integer. 1703 * <>Empty string is considered as 0.</> 1704 * 1705 * @param arg the arg 1706 * @return a big integer 1707 * @throws ArithmeticException if the string cannot be coerced into a big integer 1708 */ 1709 private BigInteger parseBigInteger(final String arg) throws ArithmeticException { 1710 try { 1711 return arg.isEmpty()? BigInteger.ZERO : new BigInteger(arg); 1712 } catch (final NumberFormatException e) { 1713 throw new CoercionException("BigDecimal coercion: ("+ arg +")", e); 1714 } 1715 } 1716 1717 /** 1718 * Convert a string to a double. 1719 * <>Empty string is considered as NaN.</> 1720 * 1721 * @param arg the arg 1722 * @return a double 1723 * @throws ArithmeticException if the string cannot be coerced into a double 1724 */ 1725 private double parseDouble(final String arg) throws ArithmeticException { 1726 try { 1727 return arg.isEmpty()? Double.NaN : Double.parseDouble(arg); 1728 } catch (final NumberFormatException e) { 1729 throw new CoercionException("Double coercion: ("+ arg +")", e); 1730 } 1731 } 1732 1733 /** 1734 * Converts a string to an int. 1735 * <p>This ensures the represented number is a natural (not a real).</p> 1736 * 1737 * @param arg the arg 1738 * @return an int 1739 * @throws ArithmeticException if the string cannot be coerced into a long 1740 */ 1741 private int parseInteger(final String arg) throws ArithmeticException { 1742 final long l = parseLong(arg); 1743 final int i = (int) l; 1744 if (i == l) { 1745 return i; 1746 } 1747 throw new CoercionException("Int coercion: ("+ arg +")"); 1748 } 1749 1750 /** 1751 * Converts a string to a long. 1752 * <p>This ensures the represented number is a natural (not a real).</p> 1753 * 1754 * @param arg the arg 1755 * @return a long 1756 * @throws ArithmeticException if the string cannot be coerced into a long 1757 */ 1758 private long parseLong(final String arg) throws ArithmeticException { 1759 final double d = parseDouble(arg); 1760 if (Double.isNaN(d)) { 1761 return 0L; 1762 } 1763 final double f = floor(d); 1764 if (d == f) { 1765 return (long) d; 1766 } 1767 throw new CoercionException("Long coercion: ("+ arg +")"); 1768 } 1769 1770 /** 1771 * Parse an identifier which must be of the form: 1772 * 0|([1-9][0-9]*) 1773 * 1774 * @param id the identifier 1775 * @return an integer or null 1776 */ 1777 public static Integer parseIdentifier(final Object id) { 1778 if (id instanceof Number) { 1779 return ((Number) id).intValue(); 1780 } 1781 // hand coded because the was no way to fail on leading '0's using NumberFormat 1782 if (id instanceof CharSequence) { 1783 final CharSequence str = (CharSequence) id; 1784 final int length = str.length(); 1785 // cannot be empty string and cannot be longer than Integer.MAX_VALUE representation 1786 if (length > 0 && length <= 10) { 1787 int val = 0; 1788 for (int i = 0; i < length; ++i) { 1789 final char c = str.charAt(i); 1790 // leading 0s but no just 0, numeric only 1791 if (c == '0' && val == 0 && length > 1 || c < '0' || c > '9') { 1792 return null; 1793 } 1794 val *= 10; 1795 val += c - '0'; 1796 } 1797 return val; 1798 } 1799 } 1800 return null; 1801 } 1802 1803 /** 1804 * Positivize value (unary plus for numbers). 1805 * <p>C/C++/C#/Java perform integral promotion of the operand, ie 1806 * cast to int if type can be represented as int without loss of precision. 1807 * 1808 * @see #isPositivizeStable() 1809 * @param val the value to positivize 1810 * @return the positive value 1811 */ 1812 public Object positivize(final Object val) { 1813 if (val == null) { 1814 return null; 1815 } 1816 if (val instanceof Short) { 1817 return ((Short) val).intValue(); 1818 } 1819 if (val instanceof Byte) { 1820 return ((Byte) val).intValue(); 1821 } 1822 if (val instanceof Number) { 1823 return val; 1824 } 1825 if (val instanceof Character) { 1826 return (int) (Character) val; 1827 } 1828 if (val instanceof Boolean) { 1829 return val; 1830 } 1831 if (val instanceof AtomicBoolean) { 1832 return ((AtomicBoolean) val).get(); 1833 } 1834 throw new ArithmeticException("Object positivize:(" + val + ")"); 1835 } 1836 1837 /** 1838 * Ensure a big decimal is rounded by this arithmetic scale and rounding mode. 1839 * 1840 * @param number the big decimal to round 1841 * @return the rounded big decimal 1842 */ 1843 protected BigDecimal roundBigDecimal(final BigDecimal number) { 1844 final int mscale = getMathScale(); 1845 if (mscale >= 0) { 1846 return number.setScale(mscale, getMathContext().getRoundingMode()); 1847 } 1848 return number; 1849 } 1850 1851 /** 1852 * Creates a set-builder. 1853 * 1854 * @param size the number of elements in the set 1855 * @return a set-builder instance 1856 * @deprecated since 3.3.1 1857 */ 1858 @Deprecated 1859 public SetBuilder setBuilder(final int size) { 1860 return setBuilder(size, false); 1861 } 1862 1863 /** 1864 * Called by the interpreter when evaluating a literal set. 1865 * 1866 * @param size the number of elements in the set 1867 * @param extended whether the set is extended or not 1868 * @return the array builder 1869 */ 1870 public SetBuilder setBuilder(final int size, final boolean extended) { 1871 return new org.apache.commons.jexl3.internal.SetBuilder(size, extended); 1872 } 1873 1874 /** 1875 * Shifts a bit pattern to the right. 1876 * 1877 * @param left left argument 1878 * @param right right argument 1879 * @return left << right. 1880 */ 1881 public Object shiftLeft(final Object left, final Object right) { 1882 final long l = toLong(left); 1883 final int r = toInteger(right); 1884 return l << r; 1885 } 1886 1887 /** 1888 * Shifts a bit pattern to the right. 1889 * 1890 * @param left left argument 1891 * @param right right argument 1892 * @return left >> right. 1893 */ 1894 public Object shiftRight(final Object left, final Object right) { 1895 final long l = toLong(left); 1896 final long r = toInteger(right); 1897 return l >> r; 1898 } 1899 1900 /** 1901 * Shifts a bit pattern to the right unsigned. 1902 * 1903 * @param left left argument 1904 * @param right right argument 1905 * @return left >>> right. 1906 */ 1907 public Object shiftRightUnsigned(final Object left, final Object right) { 1908 final long l = toLong(left); 1909 final long r = toInteger(right); 1910 return l >>> r; 1911 } 1912 1913 /** 1914 * Calculate the {@code size} of various types: Collection, Array, Map, String. 1915 * 1916 * @param object the object to get the size of 1917 * @return the <em>size</em> of object, 0 if null, 1 if there is no <em>better</em> solution 1918 */ 1919 public Integer size(final Object object) { 1920 return size(object, object == null ? 0 : 1); 1921 } 1922 1923 /** 1924 * Calculate the {@code size} of various types: Collection, Array, Map, String. 1925 * 1926 * @param object the object to get the size of 1927 * @param def the default value if object size cannot be determined 1928 * @return the size of object or null if there is no arithmetic solution 1929 */ 1930 public Integer size(final Object object, final Integer def) { 1931 if (object instanceof CharSequence) { 1932 return ((CharSequence) object).length(); 1933 } 1934 if (object.getClass().isArray()) { 1935 return Array.getLength(object); 1936 } 1937 if (object instanceof Collection<?>) { 1938 return ((Collection<?>) object).size(); 1939 } 1940 if (object instanceof Map<?, ?>) { 1941 return ((Map<?, ?>) object).size(); 1942 } 1943 return def; 1944 } 1945 1946 /** 1947 * Test if left starts with right. 1948 * 1949 * @param left left argument 1950 * @param right right argument 1951 * @return left ^= right or null if there is no arithmetic solution 1952 */ 1953 public Boolean startsWith(final Object left, final Object right) { 1954 if (left == null && right == null) { 1955 //if both are null L == R 1956 return true; 1957 } 1958 if (left == null || right == null) { 1959 // we know both aren't null, therefore L != R 1960 return false; 1961 } 1962 if (left instanceof CharSequence) { 1963 return toString(left).startsWith(toString(right)); 1964 } 1965 return null; 1966 } 1967 1968 /** 1969 * Test if left and right are strictly equal. 1970 * <p>They must have the same class, comparable and the comparison returns 0.</p> 1971 * 1972 * @param left left argument 1973 * @param right right argument 1974 * @return the test result 1975 */ 1976 public boolean strictEquals(final Object left, final Object right) { 1977 if (left == right) { 1978 return true; 1979 } 1980 if (left == null || right == null) { 1981 return false; 1982 } 1983 if (left.getClass().equals(right.getClass())) { 1984 return left.equals(right); 1985 } 1986 return false; 1987 } 1988 1989 /** 1990 * Subtract the right value from the left. 1991 * 1992 * @param left left argument 1993 * @param right right argument 1994 * @return left - right. 1995 */ 1996 public Object subtract(final Object left, final Object right) { 1997 if (left == null && right == null) { 1998 return controlNullNullOperands(JexlOperator.SUBTRACT); 1999 } 2000 final boolean strictCast = isStrict(JexlOperator.SUBTRACT); 2001 // if both (non-null) args fit as long 2002 final Number ln = asLongNumber(strictCast, left); 2003 final Number rn = asLongNumber(strictCast, right); 2004 if (ln != null && rn != null) { 2005 final long x = ln.longValue(); 2006 final long y = rn.longValue(); 2007 final long result = x - y; 2008 // detect overflow, see java8 Math.subtractExact 2009 if (((x ^ y) & (x ^ result)) < 0) { 2010 return BigInteger.valueOf(x).subtract(BigInteger.valueOf(y)); 2011 } 2012 return narrowLong(left, right, result); 2013 } 2014 // if either are BigDecimal, use that type 2015 if (left instanceof BigDecimal || right instanceof BigDecimal) { 2016 final BigDecimal l = toBigDecimal(strictCast, left); 2017 final BigDecimal r = toBigDecimal(strictCast, right); 2018 return l.subtract(r, getMathContext()); 2019 } 2020 // if either are floating point (double or float), use double 2021 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) { 2022 final double l = toDouble(strictCast, left); 2023 final double r = toDouble(strictCast, right); 2024 return l - r; 2025 } 2026 // otherwise treat as BigInteger 2027 final BigInteger l = toBigInteger(strictCast, left); 2028 final BigInteger r = toBigInteger(strictCast, right); 2029 final BigInteger result = l.subtract(r); 2030 return narrowBigInteger(left, right, result); 2031 } 2032 2033 /** 2034 * Test if a condition is true or false. 2035 * 2036 * @param object the object to use as condition 2037 * @return true or false 2038 * @since 3.3 2039 */ 2040 public boolean testPredicate(final Object object) { 2041 final boolean strictCast = isStrict(JexlOperator.CONDITION); 2042 return toBoolean(strictCast, object); 2043 } 2044 2045 /** 2046 * Coerce to a BigDecimal. 2047 * <p>Double.NaN, null and empty string coerce to zero.</p> 2048 * <p>Boolean false is 0, true is 1.</p> 2049 * 2050 * @param strict true if the calling operator or casting is strict, false otherwise 2051 * @param val the object to be coerced. 2052 * @return a BigDecimal. 2053 * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible 2054 * @since 3.3 2055 */ 2056 protected BigDecimal toBigDecimal(final boolean strict, final Object val) { 2057 return isNullOperand(val)? controlNullOperand(strict, BigDecimal.ZERO) : toBigDecimal(val); 2058 } 2059 2060 /** 2061 * Coerce to a BigDecimal. 2062 * <p>Double.NaN, null and empty string coerce to zero.</p> 2063 * <p>Boolean false is 0, true is 1.</p> 2064 * 2065 * @param val the object to be coerced. 2066 * @return a BigDecimal. 2067 * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible 2068 */ 2069 public BigDecimal toBigDecimal(final Object val) { 2070 if (val instanceof BigDecimal) { 2071 return roundBigDecimal((BigDecimal) val); 2072 } 2073 if (val instanceof Double) { 2074 if (Double.isNaN((Double) val)) { 2075 return BigDecimal.ZERO; 2076 } 2077 return roundBigDecimal(new BigDecimal(val.toString(), getMathContext())); 2078 } 2079 if (val instanceof Number) { 2080 return roundBigDecimal(parseBigDecimal(val.toString())); 2081 } 2082 if (val instanceof Boolean) { 2083 return BigDecimal.valueOf((boolean) val ? 1. : 0.); 2084 } 2085 if (val instanceof AtomicBoolean) { 2086 return BigDecimal.valueOf(((AtomicBoolean) val).get() ? 1L : 0L); 2087 } 2088 if (val instanceof String) { 2089 return roundBigDecimal(parseBigDecimal((String) val)); 2090 } 2091 if (val instanceof Character) { 2092 return new BigDecimal((Character) val); 2093 } 2094 if (val == null) { 2095 return controlNullOperand(strict, BigDecimal.ZERO); 2096 } 2097 throw new CoercionException("BigDecimal coercion: " 2098 + val.getClass().getName() + ":(" + val + ")"); 2099 } 2100 2101 /** 2102 * Coerce to a BigInteger. 2103 * <p>Double.NaN, null and empty string coerce to zero.</p> 2104 * <p>Boolean false is 0, true is 1.</p> 2105 * 2106 * @param strict true if the calling operator or casting is strict, false otherwise 2107 * @param val the object to be coerced. 2108 * @return a BigDecimal 2109 * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible 2110 * @since 3.3 2111 */ 2112 protected BigInteger toBigInteger(final boolean strict, final Object val) { 2113 return isNullOperand(val)? controlNullOperand(strict, BigInteger.ZERO) : toBigInteger(val); 2114 } 2115 2116 /** 2117 * Coerce to a BigInteger. 2118 * <p>Double.NaN, null and empty string coerce to zero.</p> 2119 * <p>Boolean false is 0, true is 1.</p> 2120 * 2121 * @param val the object to be coerced. 2122 * @return a BigDecimal 2123 * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible 2124 */ 2125 public BigInteger toBigInteger(final Object val) { 2126 if (val instanceof BigInteger) { 2127 return (BigInteger) val; 2128 } 2129 if (val instanceof Double) { 2130 final Double dval = (Double) val; 2131 if (Double.isNaN(dval)) { 2132 return BigInteger.ZERO; 2133 } 2134 return BigInteger.valueOf(dval.longValue()); 2135 } 2136 if (val instanceof BigDecimal) { 2137 return ((BigDecimal) val).toBigInteger(); 2138 } 2139 if (val instanceof Number) { 2140 return BigInteger.valueOf(((Number) val).longValue()); 2141 } 2142 if (val instanceof Boolean) { 2143 return (boolean) val ? BigInteger.ONE : BigInteger.ZERO; 2144 } 2145 if (val instanceof AtomicBoolean) { 2146 return ((AtomicBoolean) val).get() ? BigInteger.ONE : BigInteger.ZERO; 2147 } 2148 if (val instanceof String) { 2149 return parseBigInteger((String) val); 2150 } 2151 if (val instanceof Character) { 2152 final int i = (Character) val; 2153 return BigInteger.valueOf(i); 2154 } 2155 if (val == null) { 2156 return controlNullOperand(strict, BigInteger.ZERO); 2157 } 2158 throw new CoercionException("BigInteger coercion: " 2159 + val.getClass().getName() + ":(" + val + ")"); 2160 } 2161 2162 /** 2163 * Coerce to a primitive boolean. 2164 * <p>Double.NaN, null, "false" and empty string coerce to false.</p> 2165 * 2166 * @param val value to coerce 2167 * @param strict true if the calling operator or casting is strict, false otherwise 2168 * @return the boolean value if coercion is possible, true if value was not null. 2169 */ 2170 protected boolean toBoolean(final boolean strict, final Object val) { 2171 return isNullOperand(val)? controlNullOperand(strict, false) : toBoolean(val); 2172 } 2173 2174 /** 2175 * Coerce to a primitive boolean. 2176 * <p>Double.NaN, null, "false" and empty string coerce to false.</p> 2177 * 2178 * @param val value to coerce 2179 * @return the boolean value if coercion is possible, true if value was not null. 2180 */ 2181 public boolean toBoolean(final Object val) { 2182 if (val instanceof Boolean) { 2183 return (Boolean) val; 2184 } 2185 if (val instanceof Number) { 2186 final double number = toDouble(strict, val); 2187 return !Double.isNaN(number) && number != 0.d; 2188 } 2189 if (val instanceof AtomicBoolean) { 2190 return ((AtomicBoolean) val).get(); 2191 } 2192 if (val instanceof String) { 2193 final String strval = val.toString(); 2194 return !strval.isEmpty() && !"false".equals(strval); 2195 } 2196 if (val == null) { 2197 return controlNullOperand(strict, false); 2198 } 2199 // non-null value is true 2200 return true; 2201 } 2202 2203 /** 2204 * Coerce to a primitive double. 2205 * <p>Double.NaN, null and empty string coerce to zero.</p> 2206 * <p>Boolean false is 0, true is 1.</p> 2207 * 2208 * @param strict true if the calling operator or casting is strict, false otherwise 2209 * @param val value to coerce. 2210 * @return The double coerced value. 2211 * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible 2212 * @since 3.3 2213 */ 2214 protected double toDouble(final boolean strict, final Object val) { 2215 return isNullOperand(val)? controlNullOperand(strict, 0.d) : toDouble(val); 2216 } 2217 2218 /** 2219 * Coerce to a primitive double. 2220 * <p>Double.NaN, null and empty string coerce to zero.</p> 2221 * <p>Boolean false is 0, true is 1.</p> 2222 * 2223 * @param val value to coerce. 2224 * @return The double coerced value. 2225 * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible 2226 */ 2227 public double toDouble(final Object val) { 2228 if (val instanceof Double) { 2229 return (Double) val; 2230 } 2231 if (val instanceof Number) { 2232 return ((Number) val).doubleValue(); 2233 } 2234 if (val instanceof Boolean) { 2235 return (boolean) val ? 1. : 0.; 2236 } 2237 if (val instanceof AtomicBoolean) { 2238 return ((AtomicBoolean) val).get() ? 1. : 0.; 2239 } 2240 if (val instanceof String) { 2241 return parseDouble((String) val); 2242 } 2243 if (val instanceof Character) { 2244 return (Character) val; 2245 } 2246 if (val == null) { 2247 return controlNullOperand(strict, 0.d); 2248 } 2249 throw new CoercionException("Double coercion: " 2250 + val.getClass().getName() + ":(" + val + ")"); 2251 } 2252 2253 /** 2254 * Coerce to a primitive int. 2255 * <p>Double.NaN, null and empty string coerce to zero.</p> 2256 * <p>Boolean false is 0, true is 1.</p> 2257 * 2258 * @param strict true if the calling operator or casting is strict, false otherwise 2259 * @param val value to coerce 2260 * @return the value coerced to int 2261 * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible 2262 * @since 3.3 2263 */ 2264 protected int toInteger(final boolean strict, final Object val) { 2265 return isNullOperand(val)? controlNullOperand(strict, 0) : toInteger(val); 2266 } 2267 2268 /** 2269 * Coerce to a primitive int. 2270 * <p>Double.NaN, null and empty string coerce to zero.</p> 2271 * <p>Boolean false is 0, true is 1.</p> 2272 * 2273 * @param val value to coerce 2274 * @return the value coerced to int 2275 * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible 2276 */ 2277 public int toInteger(final Object val) { 2278 if (val instanceof Double) { 2279 final double dval = (Double) val; 2280 return Double.isNaN(dval)? 0 : (int) dval; 2281 } 2282 if (val instanceof Number) { 2283 return ((Number) val).intValue(); 2284 } 2285 if (val instanceof String) { 2286 return parseInteger((String) val); 2287 } 2288 if (val instanceof Boolean) { 2289 return (boolean) val ? 1 : 0; 2290 } 2291 if (val instanceof AtomicBoolean) { 2292 return ((AtomicBoolean) val).get() ? 1 : 0; 2293 } 2294 if (val instanceof Character) { 2295 return (Character) val; 2296 } 2297 if (val == null) { 2298 return controlNullOperand(strict, 0); 2299 } 2300 throw new CoercionException("Integer coercion: " 2301 + val.getClass().getName() + ":(" + val + ")"); 2302 } 2303 2304 /** 2305 * Coerce to a primitive long. 2306 * <p>Double.NaN, null and empty string coerce to zero.</p> 2307 * <p>Boolean false is 0, true is 1.</p> 2308 * 2309 * @param strict true if the calling operator or casting is strict, false otherwise 2310 * @param val value to coerce 2311 * @return the value coerced to long 2312 * @throws ArithmeticException if value is null and mode is strict or if coercion is not possible 2313 * @since 3.3 2314 */ 2315 protected long toLong(final boolean strict, final Object val) { 2316 return isNullOperand(val)? controlNullOperand(strict, 0L) : toLong(val); 2317 } 2318 2319 /** 2320 * Coerce to a primitive long. 2321 * <p>Double.NaN, null and empty string coerce to zero.</p> 2322 * <p>Boolean false is 0, true is 1.</p> 2323 * 2324 * @param val value to coerce 2325 * @return the value coerced to long 2326 * @throws ArithmeticException if value is null and mode is strict or if coercion is not possible 2327 */ 2328 public long toLong(final Object val) { 2329 if (val instanceof Double) { 2330 final double dval = (Double) val; 2331 return Double.isNaN(dval)? 0L : (long) dval; 2332 } 2333 if (val instanceof Number) { 2334 return ((Number) val).longValue(); 2335 } 2336 if (val instanceof String) { 2337 return parseLong((String) val); 2338 } 2339 if (val instanceof Boolean) { 2340 return (boolean) val ? 1L : 0L; 2341 } 2342 if (val instanceof AtomicBoolean) { 2343 return ((AtomicBoolean) val).get() ? 1L : 0L; 2344 } 2345 if (val instanceof Character) { 2346 return (Character) val; 2347 } 2348 if (val == null) { 2349 return controlNullOperand(strict, 0L); 2350 } 2351 throw new CoercionException("Long coercion: " 2352 + val.getClass().getName() + ":(" + val + ")"); 2353 } 2354 2355 /** 2356 * Coerce to a string. 2357 * <p>Double.NaN coerce to the empty string.</p> 2358 * 2359 * @param strict true if the calling operator or casting is strict, false otherwise 2360 * @param val value to coerce. 2361 * @return The String coerced value. 2362 * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible 2363 * @since 3.3 2364 */ 2365 protected String toString(final boolean strict, final Object val) { 2366 return isNullOperand(val)? controlNullOperand(strict, "") : toString(val); 2367 } 2368 2369 /** 2370 * Coerce to a string. 2371 * <p>Double.NaN coerce to the empty string.</p> 2372 * 2373 * @param val value to coerce. 2374 * @return The String coerced value. 2375 * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible 2376 */ 2377 public String toString(final Object val) { 2378 if (val instanceof Double) { 2379 final Double dval = (Double) val; 2380 if (Double.isNaN(dval)) { 2381 return ""; 2382 } 2383 return dval.toString(); 2384 } 2385 return val == null ? controlNullOperand(strict, "") : val.toString(); 2386 } 2387 2388 /** 2389 * Performs a bitwise xor. 2390 * 2391 * @param left the left operand 2392 * @param right the right operator 2393 * @return left ^ right 2394 */ 2395 public Object xor(final Object left, final Object right) { 2396 final long l = toLong(left); 2397 final long r = toLong(right); 2398 return l ^ r; 2399 } 2400}