Source code
Description: source: package: org.apache.commons.jexl3, class: JexlArithmetic, class: CoercionException

    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}