    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    package org.apache.commons.jexl2;
    018
    019    import java.lang.reflect.Array;
    020    import java.lang.reflect.Field;
    021    import java.math.BigDecimal;
    022    import java.math.BigInteger;
    023
    024    /**
    025     * Perform arithmetic.
    026     * <p>
    027     * All arithmetic operators (+, - , *, /, %) follow the same rules regarding their arguments.
    028     * <ol>
    029     * <li>If both are null, result is 0</li>
    030     * <li>If either is a floating point number, coerce both to Double and perform operation</li>
    031     * <li>If both are BigInteger, treat as BigInteger and perform operation</li>
    032     * <li>If either is a BigDecimal, coerce both to BigDecimal and and perform operation</li>
    033     * <li>Else treat as BigInteger, perform operation and attempt to narrow result:
    034     * <ol>
    035     * <li>if both arguments can be narrowed to Integer, narrow result to Integer</li>
    036     * <li>if both arguments can be narrowed to Long, narrow result to Long</li>
    037     * <li>Else return result as BigInteger</li>
    038     * </ol>
    039     * </li>
    040     * </ol>
    041     * </p>
    042     * @since 2.0
    043     */
    044    public class JexlArithmetic {
    045        /** Double.MAX_VALUE as BigDecimal. */
    046        protected static final BigDecimal BIGD_DOUBLE_MAX_VALUE = BigDecimal.valueOf(Double.MAX_VALUE);
    047        /** Double.MIN_VALUE as BigDecimal. */
    048        protected static final BigDecimal BIGD_DOUBLE_MIN_VALUE = BigDecimal.valueOf(Double.MIN_VALUE);
    049        /** Long.MAX_VALUE as BigInteger. */
    050        protected static final BigInteger BIGI_LONG_MAX_VALUE = BigInteger.valueOf(Long.MAX_VALUE);
    051        /** Long.MIN_VALUE as BigInteger. */
    052        protected static final BigInteger BIGI_LONG_MIN_VALUE = BigInteger.valueOf(Long.MIN_VALUE);
    053        /** Whether this JexlArithmetic instance behaves in strict or lenient mode. */
    054        private boolean strict;
    055
    056        /**
    057         * Creates a JexlArithmetic.
    058         * @param lenient whether this arithmetic is lenient or strict
    059         */
    060        public JexlArithmetic(boolean lenient) {
    061            this.strict = !lenient;
    062        }
    063
    064        /**
    065         * Sets whether this JexlArithmetic instance triggers errors during evaluation when
    066         * null is used as an operand.
    067         * <p>This method is <em>not</em> thread safe; it may be called as an optional step by the JexlEngine
    068         * in its initialization code before expression creation &amp; evaluation.</p>
    069         * @see JexlEngine#setSilent
    070         * @see JexlEngine#setDebug
    071         * @param lenient true means no JexlException will occur, false allows them
    072         */
    073        void setLenient(boolean lenient) {
    074            this.strict = !lenient;
    075        }
    076
    077        /**
    078         * Checks whether this JexlArithmetic instance triggers errors during evaluation
    079         * when null is used as an operand.
    080         * @return true if lenient, false if strict
    081         */
    082        public boolean isLenient() {
    083            return !this.strict;
    084        }
    085
    086        /**
    087         * The result of +,/,-,*,% when both operands are null.
    088         * @return Integer(0) if lenient
    089         * @throws NullPointerException if strict
    090         */
    091        protected Object controlNullNullOperands() {
    092            if (strict) {
    093                throw new NullPointerException(JexlException.NULL_OPERAND);
    094            }
    095            return Integer.valueOf(0);
    096        }
    097
    098        /**
    099         * Throw a NPE if arithmetic is strict.
    100         * @throws NullPointerException if strict
    101         */
    102        protected void controlNullOperand() {
    103            if (strict) {
    104                throw new NullPointerException(JexlException.NULL_OPERAND);
    105            }
    106        }
    107
    108        /**
    109         * Test if either left or right are either a Float or Double.
    110         * @param left one object to test
    111         * @param right the other
    112         * @return the result of the test.
    113         */
    114        protected boolean isFloatingPointType(Object left, Object right) {
    115            return left instanceof Float || left instanceof Double || right instanceof Float || right instanceof Double;
    116        }
    117
    118        /**
    119         * Test if the passed value is a floating point number, i.e. a float, double
    120         * or string with ( "." | "E" | "e").
    121         *
    122         * @param val the object to be tested
    123         * @return true if it is, false otherwise.
    124         */
    125        protected boolean isFloatingPointNumber(Object val) {
    126            if (val instanceof Float || val instanceof Double) {
    127                return true;
    128            }
    129            if (val instanceof String) {
    130                String string = (String) val;
    131                return string.indexOf('.') != -1 || string.indexOf('e') != -1 || string.indexOf('E') != -1;
    132            }
    133            return false;
    134        }
    135
    136        /**
    137         * Is Object a floating point number.
    138         *
    139         * @param o Object to be analyzed.
    140         * @return true if it is a Float or a Double.
    141         */
    142        protected boolean isFloatingPoint(final Object o) {
    143            return o instanceof Float || o instanceof Double;
    144        }
    145
    146        /**
    147         * Is Object a whole number.
    148         *
    149         * @param o Object to be analyzed.
    150         * @return true if Integer, Long, Byte, Short or Character.
    151         */
    152        protected boolean isNumberable(final Object o) {
    153            return o instanceof Integer
    154                || o instanceof Long
    155                || o instanceof Byte
    156                || o instanceof Short
    157                || o instanceof Character;
    158        }
    159
    160        /**
    161         * Given a BigInteger, narrow it to an Integer or Long if it fits and the arguments
    162         * class allow it.
    163         * <p>
    164         * The rules are:
    165         * if either arguments is a BigInteger, no narrowing will occur
    166         * if either arguments is a Long, no narrowing to Integer will occur
    167         * </p>
    168         * @param lhs the left hand side operand that lead to the bigi result
    169         * @param rhs the right hand side operand that lead to the bigi result
    170         * @param bigi the BigInteger to narrow
    171         * @return an Integer or Long if narrowing is possible, the original BigInteger otherwise
    172         */
    173        protected Number narrowBigInteger(Object lhs, Object rhs, BigInteger bigi) {
    174            //coerce to long if possible
    175            if (!(lhs instanceof BigInteger || rhs instanceof BigInteger)
    176                && bigi.compareTo(BIGI_LONG_MAX_VALUE) <= 0
    177                && bigi.compareTo(BIGI_LONG_MIN_VALUE) >= 0) {
    178                // coerce to int if possible
    179                long l = bigi.longValue();
    180                // coerce to int when possible (int being so often used in method parms)
    181                if (!(lhs instanceof Long || rhs instanceof Long)
    182                    && l <= Integer.MAX_VALUE
    183                    && l >= Integer.MIN_VALUE) {
    184                    return Integer.valueOf((int) l);
    185                }
    186                return Long.valueOf(l);
    187            }
    188            return bigi;
    189        }
    190
    191        /**
    192         * Given an array of objects, attempt to type it more strictly.
    193         * <ul>
    194         * <li>If all objects are of the same type, the array returned will be an array of that same type</li>
    195         * <li>If all objects are Numbers, the array returned will be an array of Numbers</li>
    196         * <li>If all objects are convertible to a primitive type, the array returned will be an array
    197         * of the primitive type</li>
    198         * </ul>
    199         * @param untyped an untyped array
    200         * @return the original array if the attempt to strictly type the array fails, a typed array otherwise
    201         */
    202        protected Object narrowArrayType(Object[] untyped) {
    203            final int size = untyped.length;
    204            Class<?> commonClass = null;
    205            if (size > 0) {
    206                // base common class on first entry
    207                commonClass = untyped[0].getClass();
    208                final boolean isNumber = Number.class.isAssignableFrom(commonClass);
    209                // for all children after first...
    210                for (int i = 1; i < size; i++) {
    211                    Class<?> eclass = untyped[i].getClass();
    212                    // detect same type for all elements in array
    213                    if (!Object.class.equals(commonClass) && !commonClass.equals(eclass)) {
    214                        // if both are numbers...
    215                        if (isNumber && Number.class.isAssignableFrom(eclass)) {
    216                            commonClass = Number.class;
    217                        } else {
    218                            commonClass = Object.class;
    219                        }
    220                    }
    221                }
    222                // convert array to the common class if not Object.class
    223                if (!Object.class.equals(commonClass)) {
    224                    // if the commonClass has an equivalent primitive type, get it
    225                    if (isNumber) {
    226                        try {
    227                            Field TYPE = commonClass.getField("TYPE");
    228                            commonClass = (Class<?>) TYPE.get(null);
    229                        } catch (Exception xany) {
    230                            // ignore
    231                        }
    232                    }
    233                    // allocate and fill up the typed array
    234                    Object typed = Array.newInstance(commonClass, size);
    235                    for(int i = 0; i < size; ++i) {
    236                        Array.set(typed, i, untyped[i]);
    237                    }
    238                    return typed;
    239                }
    240            }
    241            return untyped;
    242        }
    243
    244        /**
    245         * Replace all numbers in an arguments array with the smallest type that will fit.
    246         * @param args the argument array
    247         * @return true if some arguments were narrowed and args array is modified,
    248         *         false if no narrowing occured and args array has not been modified
    249         */
    250        protected boolean narrowArguments(Object[] args) {
    251            boolean narrowed = false;
    252            for (int a = 0; a < args.length; ++a) {
    253                Object arg = args[a];
    254                if (arg instanceof Number) {
    255                    Object narg = narrow((Number) arg);
    256                    if (narg != arg) {
    257                        narrowed = true;
    258                    }
    259                    args[a] = narg;
    260                }
    261            }
    262            return narrowed;
    263        }
    264
    265        /**
    266         * Add two values together.
    267         * <p>
    268         * If any numeric add fails on coercion to the appropriate type,
    269         * treat as Strings and do concatenation.
    270         * </p>
    271         * @param left first value
    272         * @param right second value
    273         * @return left + right.
    274         */
    275        public Object add(Object left, Object right) {
    276            if (left == null && right == null) {
    277                return controlNullNullOperands();
    278            }
    279
    280            try {
    281                // if either are floating point (double or float) use double
    282                if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
    283                    double l = toDouble(left);
    284                    double r = toDouble(right);
    285                    return new Double(l + r);
    286                }
    287
    288                // if both are bigintegers use that type
    289                if (left instanceof BigInteger && right instanceof BigInteger) {
    290                    BigInteger l = toBigInteger(left);
    291                    BigInteger r = toBigInteger(right);
    292                    return l.add(r);
    293                }
    294
    295                // if either are bigdecimal use that type
    296                if (left instanceof BigDecimal || right instanceof BigDecimal) {
    297                    BigDecimal l = toBigDecimal(left);
    298                    BigDecimal r = toBigDecimal(right);
    299                    return l.add(r);
    300                }
    301
    302                // otherwise treat as integers
    303                BigInteger l = toBigInteger(left);
    304                BigInteger r = toBigInteger(right);
    305                BigInteger result = l.add(r);
    306                return narrowBigInteger(left, right, result);
    307            } catch (java.lang.NumberFormatException nfe) {
    308                // Well, use strings!
    309                return toString(left).concat(toString(right));
    310            }
    311        }
    312
    313        /**
    314         * Divide the left value by the right.
    315         * @param left first value
    316         * @param right second value
    317         * @return left / right
    318         * @throws ArithmeticException if right == 0
    319         */
    320        public Object divide(Object left, Object right) {
    321            if (left == null && right == null) {
    322                return controlNullNullOperands();
    323            }
    324
    325            // if either are floating point (double or float) use double
    326            if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
    327                double l = toDouble(left);
    328                double r = toDouble(right);
    329                if (r == 0.0) {
    330                    throw new ArithmeticException("/");
    331                }
    332                return new Double(l / r);
    333            }
    334
    335            // if both are bigintegers use that type
    336            if (left instanceof BigInteger && right instanceof BigInteger) {
    337                BigInteger l = toBigInteger(left);
    338                BigInteger r = toBigInteger(right);
    339                return l.divide(r);
    340            }
    341
    342            // if either are bigdecimal use that type
    343            if (left instanceof BigDecimal || right instanceof BigDecimal) {
    344                BigDecimal l = toBigDecimal(left);
    345                BigDecimal r = toBigDecimal(right);
    346                BigDecimal d = l.divide(r);
    347                return d;
    348            }
    349
    350            // otherwise treat as integers
    351            BigInteger l = toBigInteger(left);
    352            BigInteger r = toBigInteger(right);
    353            BigInteger result = l.divide(r);
    354            return narrowBigInteger(left, right, result);
    355        }
    356
    357        /**
    358         * left value mod right.
    359         * @param left first value
    360         * @param right second value
    361         * @return left mod right
    362         * @throws ArithmeticException if right == 0.0
    363         */
    364        public Object mod(Object left, Object right) {
    365            if (left == null && right == null) {
    366                return controlNullNullOperands();
    367            }
    368
    369            // if either are floating point (double or float) use double
    370            if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
    371                double l = toDouble(left);
    372                double r = toDouble(right);
    373                if (r == 0.0) {
    374                    throw new ArithmeticException("%");
    375                }
    376                return new Double(l % r);
    377            }
    378
    379            // if both are bigintegers use that type
    380            if (left instanceof BigInteger && right instanceof BigInteger) {
    381                BigInteger l = toBigInteger(left);
    382                BigInteger r = toBigInteger(right);
    383                return l.mod(r);
    384            }
    385
    386            // if either are bigdecimal use that type
    387            if (left instanceof BigDecimal || right instanceof BigDecimal) {
    388                BigDecimal l = toBigDecimal(left);
    389                BigDecimal r = toBigDecimal(right);
    390                BigDecimal remainder = l.remainder(r);
    391                return remainder;
    392            }
    393
    394            // otherwise treat as integers
    395            BigInteger l = toBigInteger(left);
    396            BigInteger r = toBigInteger(right);
    397            BigInteger result = l.mod(r);
    398            return narrowBigInteger(left, right, result);
    399        }
    400
    401        /**
    402         * Multiply the left value by the right.
    403         * @param left first value
    404         * @param right second value
    405         * @return left * right.
    406         */
    407        public Object multiply(Object left, Object right) {
    408            if (left == null && right == null) {
    409                return controlNullNullOperands();
    410            }
    411
    412            // if either are floating point (double or float) use double
    413            if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
    414                double l = toDouble(left);
    415                double r = toDouble(right);
    416                return new Double(l * r);
    417            }
    418
    419            // if both are bigintegers use that type
    420            if (left instanceof BigInteger && right instanceof BigInteger) {
    421                BigInteger l = toBigInteger(left);
    422                BigInteger r = toBigInteger(right);
    423                return l.multiply(r);
    424            }
    425
    426            // if either are bigdecimal use that type
    427            if (left instanceof BigDecimal || right instanceof BigDecimal) {
    428                BigDecimal l = toBigDecimal(left);
    429                BigDecimal r = toBigDecimal(right);
    430                return l.multiply(r);
    431            }
    432
    433            // otherwise treat as integers
    434            BigInteger l = toBigInteger(left);
    435            BigInteger r = toBigInteger(right);
    436            BigInteger result = l.multiply(r);
    437            return narrowBigInteger(left, right, result);
    438        }
    439
    440        /**
    441         * Subtract the right value from the left.
    442         * @param left first value
    443         * @param right second value
    444         * @return left - right.
    445         */
    446        public Object subtract(Object left, Object right) {
    447            if (left == null && right == null) {
    448                return controlNullNullOperands();
    449            }
    450
    451            // if either are floating point (double or float) use double
    452            if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
    453                double l = toDouble(left);
    454                double r = toDouble(right);
    455                return new Double(l - r);
    456            }
    457
    458            // if both are bigintegers use that type
    459            if (left instanceof BigInteger && right instanceof BigInteger) {
    460                BigInteger l = toBigInteger(left);
    461                BigInteger r = toBigInteger(right);
    462                return l.subtract(r);
    463            }
    464
    465            // if either are bigdecimal use that type
    466            if (left instanceof BigDecimal || right instanceof BigDecimal) {
    467                BigDecimal l = toBigDecimal(left);
    468                BigDecimal r = toBigDecimal(right);
    469                return l.subtract(r);
    470            }
    471
    472            // otherwise treat as integers
    473            BigInteger l = toBigInteger(left);
    474            BigInteger r = toBigInteger(right);
    475            BigInteger result = l.subtract(r);
    476            return narrowBigInteger(left, right, result);
    477        }
    478
    479        /**
    480         * Test if left regexp matches right.
    481         *
    482         * @param left first value
    483         * @param right second value
    484         * @return test result.
    485         */
    486        public boolean matches(Object left, Object right) {
    487            if (left == null && right == null) {
    488                //if both are null L == R
    489                return true;
    490            }
    491            if (left == null || right == null) {
    492                // we know both aren't null, therefore L != R
    493                return false;
    494            }
    495            final String arg = left.toString();
    496            if (right instanceof java.util.regex.Pattern) {
    497                return ((java.util.regex.Pattern) right).matcher(arg).matches();
    498            } else {
    499                return arg.matches(right.toString());
    500            }
    501        }
    502
    503        /**
    504         * Test if left and right are equal.
    505         *
    506         * @param left first value
    507         * @param right second value
    508         * @return test result.
    509         */
    510        public boolean equals(Object left, Object right) {
    511            if (left == null && right == null) {
    512                /*
    513                 * if both are null L == R
    514                 */
    515                return true;
    516            } else if (left == null || right == null) {
    517                /*
    518                 * we know both aren't null, therefore L != R
    519                 */
    520                return false;
    521            } else if (left.getClass().equals(right.getClass())) {
    522                return left.equals(right);
    523            } else if (left instanceof BigDecimal || right instanceof BigDecimal) {
    524                return toBigDecimal(left).compareTo(toBigDecimal(right)) == 0;
    525            } else if (isFloatingPointType(left, right)) {
    526                return toDouble(left) == toDouble(right);
    527            } else if (left instanceof Number || right instanceof Number || left instanceof Character
    528                || right instanceof Character) {
    529                return toLong(left) == toLong(right);
    530            } else if (left instanceof Boolean || right instanceof Boolean) {
    531                return toBoolean(left) == toBoolean(right);
    532            } else if (left instanceof java.lang.String || right instanceof String) {
    533                return left.toString().equals(right.toString());
    534            }
    535
    536            return left.equals(right);
    537        }
    538
    539
    540        /**
    541         * Test if left < right.
    542         *
    543         * @param left first value
    544         * @param right second value
    545         * @return test result.
    546         */
    547        public boolean lessThan(Object left, Object right) {
    548            if ((left == right) || (left == null) || (right == null)) {
    549                return false;
    550            } else if (isFloatingPoint(left) || isFloatingPoint(right)) {
    551                double leftDouble = toDouble(left);
    552                double rightDouble = toDouble(right);
    553                return leftDouble < rightDouble;
    554            } else if (left instanceof BigDecimal || right instanceof BigDecimal) {
    555                BigDecimal l  = toBigDecimal(left);
    556                BigDecimal r  = toBigDecimal(right);
    557                return l.compareTo(r) < 0;
    558            } else if (isNumberable(left) || isNumberable(right)) {
    559                long leftLong = toLong(left);
    560                long rightLong = toLong(right);
    561                return leftLong < rightLong;
    562            } else if (left instanceof String || right instanceof String) {
    563                String leftString = left.toString();
    564                String rightString = right.toString();
    565                return leftString.compareTo(rightString) < 0;
    566            } else if (left instanceof Comparable<?>) {
    567                @SuppressWarnings("unchecked") // OK because of instanceof check above
    568                final Comparable<Object> comparable = (Comparable<Object>) left;
    569                return comparable.compareTo(right) < 0;
    570            } else if (right instanceof Comparable<?>) {
    571                @SuppressWarnings("unchecked") // OK because of instanceof check above
    572                final Comparable<Object> comparable = (Comparable<Object>) right;
    573                return comparable.compareTo(left) > 0;
    574            }
    575
    576            throw new IllegalArgumentException("Invalid comparison : comparing cardinality for left: " + left
    577                + " and right: " + right);
    578
    579        }
    580
    581        /**
    582         * Test if left > right.
    583         *
    584         * @param left first value
    585         * @param right second value
    586         * @return test result.
    587         */
    588        public boolean greaterThan(Object left, Object right) {
    589            if (left == null || right == null) {
    590                return false;
    591            }
    592            return !equals(left, right) && !lessThan(left, right);
    593        }
    594
    595        /**
    596         * Test if left <= right.
    597         *
    598         * @param left first value
    599         * @param right second value
    600         * @return test result.
    601         */
    602        public boolean lessThanOrEqual(Object left, Object right) {
    603            return equals(left, right) || lessThan(left, right);
    604        }
    605
    606        /**
    607         * Test if left >= right.
    608         *
    609         * @param left first value
    610         * @param right second value
    611         * @return test result.
    612         */
    613        public boolean greaterThanOrEqual(Object left, Object right) {
    614            return equals(left, right) || greaterThan(left, right);
    615        }
    616
    617        /**
    618         * Coerce to a boolean (not a java.lang.Boolean).
    619         *
    620         * @param val Object to be coerced.
    621         * @return The boolean coerced value, or false if none possible.
    622         */
    623        public boolean toBoolean(Object val) {
    624            if (val == null) {
    625                controlNullOperand();
    626                return false;
    627            } else if (val instanceof Boolean) {
    628                return ((Boolean) val).booleanValue();
    629            } else if (val instanceof String) {
    630                return Boolean.valueOf((String) val).booleanValue();
    631            }
    632            // TODO: is this a reasonable default?
    633            return false;
    634        }
    635
    636        /**
    637         * Coerce to a int.
    638         *
    639         * @param val Object to be coerced.
    640         * @return The int coerced value.
    641         */
    642        public int toInteger(Object val) {
    643            if (val == null) {
    644                controlNullOperand();
    645                return 0;
    646            } else if (val instanceof String) {
    647                if ("".equals(val)) {
    648                    return 0;
    649                }
    650                return Integer.parseInt((String) val);
    651            } else if (val instanceof Character) {
    652                return ((Character) val).charValue();
    653            } else if (val instanceof Boolean) {
    654                throw new IllegalArgumentException("Boolean->Integer coercion exception");
    655            } else if (val instanceof Number) {
    656                return ((Number) val).intValue();
    657            }
    658
    659            throw new IllegalArgumentException("Integer coercion exception. Can't coerce type: "
    660                    + val.getClass().getName());
    661        }
    662
    663
    664        /**
    665         * Coerce to a long (not a java.lang.Long).
    666         *
    667         * @param val Object to be coerced.
    668         * @return The long coerced value.
    669         */
    670        public long toLong(Object val) {
    671            if (val == null) {
    672                controlNullOperand();
    673                return 0L;
    674            } else if (val instanceof String) {
    675                if ("".equals(val)) {
    676                    return 0;
    677                }
    678                return Long.parseLong((String) val);
    679            } else if (val instanceof Character) {
    680                return ((Character) val).charValue();
    681            } else if (val instanceof Boolean) {
    682                throw new NumberFormatException("Boolean->Long coercion exception");
    683            } else if (val instanceof Number) {
    684                return ((Number) val).longValue();
    685            }
    686
    687            throw new NumberFormatException("Long coercion exception. Can't coerce type: " + val.getClass().getName());
    688        }
    689
    690        /**
    691         * Get a BigInteger from the object passed.
    692         * Null and empty string maps to zero.
    693         * @param val the object to be coerced.
    694         * @return a BigDecimal.
    695         * @throws NullPointerException if val is null and mode is strict.
    696         */
    697        public BigInteger toBigInteger(Object val) {
    698            if (val instanceof BigInteger) {
    699                return (BigInteger) val;
    700            } else if (val == null) {
    701                controlNullOperand();
    702                return BigInteger.valueOf(0);
    703            } else if (val instanceof String) {
    704                String string = (String) val;
    705                if ("".equals(string.trim())) {
    706                    return BigInteger.ZERO;
    707                }
    708                return new BigInteger(string);
    709            } else if (val instanceof Number) {
    710                return new BigInteger(val.toString());
    711            } else if (val instanceof Character) {
    712                int i = ((Character) val).charValue();
    713                return BigInteger.valueOf(i);
    714            }
    715
    716            throw new IllegalArgumentException("BigInteger coercion exception. Can't coerce type: "
    717                    + val.getClass().getName());
    718        }
    719
    720        /**
    721         * Get a BigDecimal from the object passed.
    722         * Null and empty string maps to zero.
    723         * @param val the object to be coerced.
    724         * @return a BigDecimal.
    725         * @throws NullPointerException if val is null and mode is strict.
    726         */
    727        public BigDecimal toBigDecimal(Object val) {
    728            if (val instanceof BigDecimal) {
    729                return (BigDecimal) val;
    730            } else if (val == null) {
    731                controlNullOperand();
    732                return BigDecimal.ZERO;
    733            } else if (val instanceof String) {
    734                String string = (String) val;
    735                if ("".equals(string.trim())) {
    736                    return BigDecimal.valueOf(0);
    737                }
    738                return new BigDecimal(string);
    739            } else if (val instanceof Number) {
    740                return new BigDecimal(val.toString());
    741            } else if (val instanceof Character) {
    742                int i = ((Character) val).charValue();
    743                return new BigDecimal(i);
    744            }
    745
    746            throw new IllegalArgumentException("BigDecimal coercion exception. Can't coerce type: "
    747                    + val.getClass().getName());
    748        }
    749
    750        /**
    751         * Coerce to a double.
    752         *
    753         * @param val Object to be coerced.
    754         * @return The double coerced value.
    755         * @throws NullPointerException if val is null and mode is strict.
    756         */
    757        public double toDouble(Object val) {
    758            if (val == null) {
    759                controlNullOperand();
    760                return 0;
    761            } else if (val instanceof String) {
    762                String string = (String) val;
    763                if ("".equals(string.trim())) {
    764                    return 0;
    765                }
    766                // the spec seems to be iffy about this.  Going to give it a wack anyway
    767                return Double.parseDouble(string);
    768            } else if (val instanceof Character) {
    769                int i = ((Character) val).charValue();
    770
    771                return i;
    772            } else if (val instanceof Double) {
    773                return ((Double) val).doubleValue();
    774            } else if (val instanceof Number) {
    775                //The below construct is used rather than ((Number)val).doubleValue() to ensure
    776                //equality between comparing new Double( 6.4 / 3 ) and the jexl expression of 6.4 / 3
    777                return Double.parseDouble(String.valueOf(val));
    778            } else if (val instanceof Boolean) {
    779                throw new IllegalArgumentException("Boolean->Double coercion exception");
    780            }
    781
    782            throw new IllegalArgumentException("Double coercion exception. Can't coerce type: "
    783                    + val.getClass().getName());
    784        }
    785
    786
    787        /**
    788         * Coerce to a string.
    789         *
    790         * @param val Object to be coerced.
    791         * @return The String coerced value.
    792         * @throws NullPointerException if val is null and mode is strict.
    793         */
    794        public String toString(Object val) {
    795            if (val == null) {
    796                controlNullOperand();
    797                val = "";
    798            }
    799            return val.toString();
    800        }
    801
    802        /**
    803         * Given a Number, return back the value using the smallest type the result
    804         * will fit into. This works hand in hand with parameter 'widening' in java
    805         * method calls, e.g. a call to substring(int,int) with an int and a long
    806         * will fail, but a call to substring(int,int) with an int and a short will
    807         * succeed.
    808         *
    809         * @param original the original number.
    810         * @return a value of the smallest type the original number will fit into.
    811         */
    812        public Number narrow(Number original) {
    813            if (original == null) {
    814                return original;
    815            }
    816            Number result = original;
    817            if (original instanceof BigDecimal) {
    818                BigDecimal bigd = (BigDecimal) original;
    819                // if it's bigger than a double it can't be narrowed
    820                if (bigd.compareTo(BIGD_DOUBLE_MAX_VALUE) > 0) {
    821                    return original;
    822                }
    823            }
    824            if (original instanceof Double || original instanceof Float || original instanceof BigDecimal) {
    825                double value = original.doubleValue();
    826                if (value <= Float.MAX_VALUE && value >= Float.MIN_VALUE) {
    827                    result = Float.valueOf(result.floatValue());
    828                }
    829                // else it fits in a double only
    830            } else {
    831                if (original instanceof BigInteger) {
    832                    BigInteger bigi = (BigInteger) original;
    833                    // if it's bigger than a Long it can't be narrowed
    834                    if (bigi.compareTo(BIGI_LONG_MAX_VALUE) > 0
    835                        || bigi.compareTo(BIGI_LONG_MIN_VALUE) < 0) {
    836                        return original;
    837                    }
    838                }
    839                long value = original.longValue();
    840                if (value <= Byte.MAX_VALUE && value >= Byte.MIN_VALUE) {
    841                    // it will fit in a byte
    842                    result = Byte.valueOf((byte) value);
    843                } else if (value <= Short.MAX_VALUE && value >= Short.MIN_VALUE) {
    844                    result = Short.valueOf((short) value);
    845                } else if (value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE) {
    846                    result = Integer.valueOf((int) value);
    847                }
    848                // else it fits in a long
    849            }
    850            return result;
    851        }
    852
    853    }