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