001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.jexl3;
019
020import static java.lang.StrictMath.floor;
021import static org.apache.commons.jexl3.JexlOperator.EQ;
022
023import java.lang.reflect.Array;
024import java.lang.reflect.Constructor;
025import java.lang.reflect.InvocationTargetException;
026import java.lang.reflect.Method;
027import java.math.BigDecimal;
028import java.math.BigInteger;
029import java.math.MathContext;
030import java.util.Collection;
031import java.util.Map;
032import java.util.concurrent.atomic.AtomicBoolean;
033import java.util.function.Supplier;
034import java.util.function.ToLongFunction;
035import java.util.regex.Matcher;
036import java.util.regex.Pattern;
037
038import org.apache.commons.jexl3.introspection.JexlMethod;
039import org.apache.commons.logging.Log;
040
041/**
042 * Perform arithmetic, implements JexlOperator methods.
043 *
044 * <p>This is the class to derive to implement new operator behaviors.</p>
045 *
046 * <p>The 5 base arithmetic operators (+, - , *, /, %) follow the same evaluation rules regarding their arguments.</p>
047 * <ol>
048 *   <li>If both are null, result is 0 if arithmetic (or operator) is non-strict, ArithmeticException is thrown
049 *   otherwise</li>
050 *   <li>If both arguments are numberable - any kind of integer including boolean -, coerce both to Long and coerce
051 *   result to the most precise argument class ({@code boolean < byte < short < int < long});
052 *   if long operation would cause overflow, return a BigInteger</li>
053 *   <li>If either argument is a BigDecimal, coerce both to BigDecimal, operator returns BigDecimal</li>
054 *   <li>If either argument is a floating point number, coerce both to Double, operator returns Double</li>
055 *   <li>Else treat as BigInteger, perform operation and narrow result to the most precise argument class
056 *   </li>
057 * </ol>
058 *
059 * Note that the only exception thrown by JexlArithmetic is and must be ArithmeticException.
060 *
061 * @see JexlOperator
062 * @since 2.0
063 */
064public class JexlArithmetic {
065
066    /**
067     * Helper interface used when creating an array literal.
068     *
069     * <p>The default implementation creates an array and attempts to type it strictly.</p>
070     *
071     * <ul>
072     *   <li>If all objects are of the same type, the array returned will be an array of that same type</li>
073     *   <li>If all objects are Numbers, the array returned will be an array of Numbers</li>
074     *   <li>If all objects are convertible to a primitive type, the array returned will be an array
075     *       of the primitive type</li>
076     * </ul>
077     */
078    public interface ArrayBuilder {
079
080        /**
081         * Adds a literal to the array.
082         *
083         * @param value The item to add
084         */
085        void add(Object value);
086
087        /**
088         * Creates the actual "array" instance.
089         *
090         * @param extended true when the last argument is ', ...'
091         * @return The array
092         */
093        Object create(boolean extended);
094    }
095
096    /** Marker class for coercion operand exceptions. */
097    public static class CoercionException extends ArithmeticException {
098        private static final long serialVersionUID = 202402081150L;
099
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}