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