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