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