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