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    import java.math.MathContext;
024    
025    /**
026     * Perform arithmetic.
027     * <p>
028     * All arithmetic operators (+, - , *, /, %) follow the same rules regarding their arguments.
029     * <ol>
030     * <li>If both are null, result is 0</li>
031     * <li>If either is a BigDecimal, coerce both to BigDecimal and and perform operation</li>
032     * <li>If either is a floating point number, coerce both to Double and perform operation</li>
033     * <li>If both are BigInteger, treat as BigInteger and perform operation</li>
034     * <li>Else treat as BigInteger, perform operation and attempt to narrow result:
035     * <ol>
036     * <li>if both arguments can be narrowed to Integer, narrow result to Integer</li>
037     * <li>if both arguments can be narrowed to Long, narrow result to Long</li>
038     * <li>Else return result as BigInteger</li>
039     * </ol>
040     * </li>
041     * </ol>
042     * </p>
043     * Note that the only exception throw by JexlArithmetic is ArithmeticException.
044     * @since 2.0
045     */
046    public class JexlArithmetic {
047        /** Double.MAX_VALUE as BigDecimal. */
048        protected static final BigDecimal BIGD_DOUBLE_MAX_VALUE = BigDecimal.valueOf(Double.MAX_VALUE);
049        /** Double.MIN_VALUE as BigDecimal. */
050        protected static final BigDecimal BIGD_DOUBLE_MIN_VALUE = BigDecimal.valueOf(Double.MIN_VALUE);
051        /** Long.MAX_VALUE as BigInteger. */
052        protected static final BigInteger BIGI_LONG_MAX_VALUE = BigInteger.valueOf(Long.MAX_VALUE);
053        /** Long.MIN_VALUE as BigInteger. */
054        protected static final BigInteger BIGI_LONG_MIN_VALUE = BigInteger.valueOf(Long.MIN_VALUE);
055        /** 
056         * Default BigDecimal scale. 
057         * @since 2.1 
058         */
059        protected static final int BIGD_SCALE = -1;
060        /** Whether this JexlArithmetic instance behaves in strict or lenient mode.
061         * May be made final in a later version.
062         */
063        private volatile boolean strict;
064        /** 
065         * The big decimal math context.
066         * @since 2.1 
067         */
068        protected final MathContext mathContext;
069        /** 
070         * The big decimal scale.
071         * @since 2.1
072         */
073        protected final int mathScale;
074    
075        /**
076         * Creates a JexlArithmetic.
077         * @param lenient whether this arithmetic is lenient or strict
078         */
079        public JexlArithmetic(boolean lenient) {
080            this(lenient, MathContext.DECIMAL128, BIGD_SCALE);
081        }
082    
083        /**
084         * Creates a JexlArithmetic.
085         * @param lenient whether this arithmetic is lenient or strict
086         * @param bigdContext the math context instance to use for +,-,/,*,% operations on big decimals.
087         * @param bigdScale the scale used for big decimals.
088         * @since 2.1
089         */
090        public JexlArithmetic(boolean lenient, MathContext bigdContext, int bigdScale) {
091            this.strict = !lenient;
092            this.mathContext = bigdContext;
093            this.mathScale = bigdScale;
094        }
095        
096        
097        /**
098         * Sets whether this JexlArithmetic instance triggers errors during evaluation when
099         * null is used as an operand.
100         * <p>This method is <em>not</em> thread safe; it may be called as an optional step by the JexlEngine
101         * in its initialization code before expression creation &amp; evaluation.</p>
102         * @see JexlEngine#setLenient
103         * @see JexlEngine#setSilent
104         * @see JexlEngine#setDebug
105         * @param flag true means no JexlException will occur, false allows them
106         * @deprecated as of 2.1 - may be removed in a later release
107         */
108        @Deprecated
109        void setLenient(boolean flag) {
110            this.strict = !flag;
111        }
112    
113        /**
114         * Checks whether this JexlArithmetic instance triggers errors during evaluation
115         * when null is used as an operand.
116         * @return true if lenient, false if strict
117         */
118        public boolean isLenient() {
119            return !this.strict;
120        }
121    
122        /**
123         * The MathContext instance used for +,-,/,*,% operations on big decimals.
124         * @return the math context
125         * @since 2.1
126         */
127        public MathContext getMathContext() {
128            return mathContext;
129        }
130    
131        /**
132         * The BigDecimal scale used for comparison and coercion operations.
133         * @return the scale
134         * @since 2.1
135         */
136        public int getMathScale() {
137            return mathScale;
138        }
139    
140        /**
141         * Ensure a big decimal is rounded by this arithmetic scale and rounding mode.
142         * @param number the big decimal to round
143         * @return the rounded big decimal
144         * @since 2.1
145         */
146        public BigDecimal roundBigDecimal(final BigDecimal number) {
147            int mscale = getMathScale();
148            if (mscale >= 0) {
149                return number.setScale(mscale, getMathContext().getRoundingMode());
150            } else {
151                return number;
152            }
153        }
154    
155        /**
156         * The result of +,/,-,*,% when both operands are null.
157         * @return Integer(0) if lenient
158         * @throws ArithmeticException if strict
159         */
160        protected Object controlNullNullOperands() {
161            if (!isLenient()) {
162                throw new ArithmeticException(JexlException.NULL_OPERAND);
163            }
164            return Integer.valueOf(0);
165        }
166    
167        /**
168         * Throw a NPE if arithmetic is strict.
169         * @throws ArithmeticException if strict
170         */
171        protected void controlNullOperand() {
172            if (!isLenient()) {
173                throw new ArithmeticException(JexlException.NULL_OPERAND);
174            }
175        }
176    
177        /**
178         * Test if either left or right are either a Float or Double.
179         * @param left one object to test
180         * @param right the other
181         * @return the result of the test.
182         */
183        protected boolean isFloatingPointType(Object left, Object right) {
184            return left instanceof Float || left instanceof Double || right instanceof Float || right instanceof Double;
185        }
186    
187        /**
188         * Test if the passed value is a floating point number, i.e. a float, double
189         * or string with ( "." | "E" | "e").
190         *
191         * @param val the object to be tested
192         * @return true if it is, false otherwise.
193         */
194        protected boolean isFloatingPointNumber(Object val) {
195            if (val instanceof Float || val instanceof Double) {
196                return true;
197            }
198            if (val instanceof String) {
199                String string = (String) val;
200                return string.indexOf('.') != -1 || string.indexOf('e') != -1 || string.indexOf('E') != -1;
201            }
202            return false;
203        }
204    
205        /**
206         * Is Object a floating point number.
207         *
208         * @param o Object to be analyzed.
209         * @return true if it is a Float or a Double.
210         */
211        protected boolean isFloatingPoint(final Object o) {
212            return o instanceof Float || o instanceof Double;
213        }
214    
215        /**
216         * Is Object a whole number.
217         *
218         * @param o Object to be analyzed.
219         * @return true if Integer, Long, Byte, Short or Character.
220         */
221        protected boolean isNumberable(final Object o) {
222            return o instanceof Integer
223                    || o instanceof Long
224                    || o instanceof Byte
225                    || o instanceof Short
226                    || o instanceof Character;
227        }
228    
229        /**
230         * Given a BigInteger, narrow it to an Integer or Long if it fits and the arguments
231         * class allow it.
232         * <p>
233         * The rules are:
234         * if either arguments is a BigInteger, no narrowing will occur
235         * if either arguments is a Long, no narrowing to Integer will occur
236         * </p>
237         * @param lhs the left hand side operand that lead to the bigi result
238         * @param rhs the right hand side operand that lead to the bigi result
239         * @param bigi the BigInteger to narrow
240         * @return an Integer or Long if narrowing is possible, the original BigInteger otherwise
241         */
242        protected Number narrowBigInteger(Object lhs, Object rhs, BigInteger bigi) {
243            //coerce to long if possible
244            if (!(lhs instanceof BigInteger || rhs instanceof BigInteger)
245                    && bigi.compareTo(BIGI_LONG_MAX_VALUE) <= 0
246                    && bigi.compareTo(BIGI_LONG_MIN_VALUE) >= 0) {
247                // coerce to int if possible
248                long l = bigi.longValue();
249                // coerce to int when possible (int being so often used in method parms)
250                if (!(lhs instanceof Long || rhs instanceof Long)
251                        && l <= Integer.MAX_VALUE
252                        && l >= Integer.MIN_VALUE) {
253                    return Integer.valueOf((int) l);
254                }
255                return Long.valueOf(l);
256            }
257            return bigi;
258        }
259    
260        /**
261         * Given a BigDecimal, attempt to narrow it to an Integer or Long if it fits if
262         * one of the arguments is a numberable.
263         * 
264         * @param lhs the left hand side operand that lead to the bigd result
265         * @param rhs the right hand side operand that lead to the bigd result
266         * @param bigd the BigDecimal to narrow
267         * @return an Integer or Long if narrowing is possible, the original BigInteger otherwise
268         * @since 2.1
269         */
270        protected Number narrowBigDecimal(Object lhs, Object rhs, BigDecimal bigd) {
271            if (isNumberable(lhs) || isNumberable(rhs)) {
272                try {
273                    long l = bigd.longValueExact();
274                    // coerce to int when possible (int being so often used in method parms)
275                    if (l <= Integer.MAX_VALUE && l >= Integer.MIN_VALUE) {
276                        return Integer.valueOf((int) l);
277                    } else {
278                        return Long.valueOf(l);
279                    }
280                } catch (ArithmeticException xa) {
281                    // ignore, no exact value possible
282                }
283            }
284            return bigd;
285        }
286    
287        /**
288         * Given an array of objects, attempt to type it more strictly.
289         * <ul>
290         * <li>If all objects are of the same type, the array returned will be an array of that same type</li>
291         * <li>If all objects are Numbers, the array returned will be an array of Numbers</li>
292         * <li>If all objects are convertible to a primitive type, the array returned will be an array
293         * of the primitive type</li>
294         * </ul>
295         * @param untyped an untyped array
296         * @return the original array if the attempt to strictly type the array fails, a typed array otherwise
297         */
298        protected Object narrowArrayType(Object[] untyped) {
299            final int size = untyped.length;
300            Class<?> commonClass = null;
301            if (size > 0) {
302                boolean isNumber = true;
303                // for all children after first...
304                for (int u = 0; u < size && !Object.class.equals(commonClass); ++u) {
305                    if (untyped[u] != null) {
306                        Class<?> eclass = untyped[u].getClass();
307                        // base common class on first non-null entry
308                        if (commonClass == null) {
309                            commonClass = eclass;
310                            isNumber &= Number.class.isAssignableFrom(commonClass);
311                        } else if (!commonClass.equals(eclass)) {
312                            // if both are numbers...
313                            if (isNumber && Number.class.isAssignableFrom(eclass)) {
314                                commonClass = Number.class;
315                            } else {
316                                // attempt to find valid superclass
317                                do {
318                                    eclass = eclass.getSuperclass();
319                                    if (eclass == null) {
320                                        commonClass = Object.class;
321                                        break;
322                                    }
323                                } while (!commonClass.isAssignableFrom(eclass));
324                            }
325                        }
326                    } else {
327                        isNumber = false;
328                    }
329                }
330                // convert array to the common class if not Object.class
331                if (commonClass != null && !Object.class.equals(commonClass)) {
332                    // if the commonClass has an equivalent primitive type, get it
333                    if (isNumber) {
334                        try {
335                            final Field type = commonClass.getField("TYPE");
336                            commonClass = (Class<?>) type.get(null);
337                        } catch (Exception xany) {
338                            // ignore
339                        }
340                    }
341                    // allocate and fill up the typed array
342                    Object typed = Array.newInstance(commonClass, size);
343                    for (int i = 0; i < size; ++i) {
344                        Array.set(typed, i, untyped[i]);
345                    }
346                    return typed;
347                }
348            }
349            return untyped;
350        }
351    
352        /**
353         * Replace all numbers in an arguments array with the smallest type that will fit.
354         * @param args the argument array
355         * @return true if some arguments were narrowed and args array is modified,
356         *         false if no narrowing occured and args array has not been modified
357         */
358        protected boolean narrowArguments(Object[] args) {
359            boolean narrowed = false;
360            for (int a = 0; a < args.length; ++a) {
361                Object arg = args[a];
362                if (arg instanceof Number) {
363                    Object narg = narrow((Number) arg);
364                    if (narg != arg) {
365                        narrowed = true;
366                    }
367                    args[a] = narg;
368                }
369            }
370            return narrowed;
371        }
372    
373        /**
374         * Add two values together.
375         * <p>
376         * If any numeric add fails on coercion to the appropriate type,
377         * treat as Strings and do concatenation.
378         * </p>
379         * @param left first value
380         * @param right second value
381         * @return left + right.
382         */
383        public Object add(Object left, Object right) {
384            if (left == null && right == null) {
385                return controlNullNullOperands();
386            }
387    
388            try {
389                // if either are floating point (double or float) use double
390                if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
391                    double l = toDouble(left);
392                    double r = toDouble(right);
393                    return new Double(l + r);
394                }
395    
396                // if either are bigdecimal use that type 
397                if (left instanceof BigDecimal || right instanceof BigDecimal) {
398                    BigDecimal l = toBigDecimal(left);
399                    BigDecimal r = toBigDecimal(right);
400                    BigDecimal result = l.add(r, getMathContext());
401                    return narrowBigDecimal(left, right, result);
402                }
403    
404                // otherwise treat as integers
405                BigInteger l = toBigInteger(left);
406                BigInteger r = toBigInteger(right);
407                BigInteger result = l.add(r);
408                return narrowBigInteger(left, right, result);
409            } catch (java.lang.NumberFormatException nfe) {
410                // Well, use strings!
411                return toString(left).concat(toString(right));
412            }
413        }
414    
415        /**
416         * Divide the left value by the right.
417         * @param left first value
418         * @param right second value
419         * @return left / right
420         * @throws ArithmeticException if right == 0
421         */
422        public Object divide(Object left, Object right) {
423            if (left == null && right == null) {
424                return controlNullNullOperands();
425            }
426    
427            // if either are floating point (double or float) use double
428            if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
429                double l = toDouble(left);
430                double r = toDouble(right);
431                if (r == 0.0) {
432                    throw new ArithmeticException("/");
433                }
434                return new Double(l / r);
435            }
436    
437            // if either are bigdecimal use that type
438            if (left instanceof BigDecimal || right instanceof BigDecimal) {
439                BigDecimal l = toBigDecimal(left);
440                BigDecimal r = toBigDecimal(right);
441                if (BigDecimal.ZERO.equals(r)) {
442                    throw new ArithmeticException("/");
443                }
444                BigDecimal result = l.divide(r, getMathContext());
445                return narrowBigDecimal(left, right, result);
446            }
447    
448            // otherwise treat as integers
449            BigInteger l = toBigInteger(left);
450            BigInteger r = toBigInteger(right);
451            if (BigInteger.ZERO.equals(r)) {
452                throw new ArithmeticException("/");
453            }
454            BigInteger result = l.divide(r);
455            return narrowBigInteger(left, right, result);
456        }
457    
458        /**
459         * left value mod right.
460         * @param left first value
461         * @param right second value
462         * @return left mod right
463         * @throws ArithmeticException if right == 0.0
464         */
465        public Object mod(Object left, Object right) {
466            if (left == null && right == null) {
467                return controlNullNullOperands();
468            }
469    
470            // if either are floating point (double or float) use double
471            if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
472                double l = toDouble(left);
473                double r = toDouble(right);
474                if (r == 0.0) {
475                    throw new ArithmeticException("%");
476                }
477                return new Double(l % r);
478            }
479    
480            // if either are bigdecimal use that type 
481            if (left instanceof BigDecimal || right instanceof BigDecimal) {
482                BigDecimal l = toBigDecimal(left);
483                BigDecimal r = toBigDecimal(right);
484                if (BigDecimal.ZERO.equals(r)) {
485                    throw new ArithmeticException("%");
486                }
487                BigDecimal remainder = l.remainder(r, getMathContext());
488                return narrowBigDecimal(left, right, remainder);
489            }
490    
491            // otherwise treat as integers
492            BigInteger l = toBigInteger(left);
493            BigInteger r = toBigInteger(right);
494            BigInteger result = l.mod(r);
495            if (BigInteger.ZERO.equals(r)) {
496                throw new ArithmeticException("%");
497            }
498            return narrowBigInteger(left, right, result);
499        }
500    
501        /**
502         * Multiply the left value by the right.
503         * @param left first value
504         * @param right second value
505         * @return left * right.
506         */
507        public Object multiply(Object left, Object right) {
508            if (left == null && right == null) {
509                return controlNullNullOperands();
510            }
511    
512            // if either are floating point (double or float) use double
513            if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
514                double l = toDouble(left);
515                double r = toDouble(right);
516                return new Double(l * r);
517            }
518    
519            // if either are bigdecimal use that type 
520            if (left instanceof BigDecimal || right instanceof BigDecimal) {
521                BigDecimal l = toBigDecimal(left);
522                BigDecimal r = toBigDecimal(right);
523                BigDecimal result = l.multiply(r, getMathContext());
524                return narrowBigDecimal(left, right, result);
525            }
526    
527            // otherwise treat as integers
528            BigInteger l = toBigInteger(left);
529            BigInteger r = toBigInteger(right);
530            BigInteger result = l.multiply(r);
531            return narrowBigInteger(left, right, result);
532        }
533    
534        /**
535         * Subtract the right value from the left.
536         * @param left first value
537         * @param right second value
538         * @return left - right.
539         */
540        public Object subtract(Object left, Object right) {
541            if (left == null && right == null) {
542                return controlNullNullOperands();
543            }
544    
545            // if either are floating point (double or float) use double
546            if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
547                double l = toDouble(left);
548                double r = toDouble(right);
549                return new Double(l - r);
550            }
551    
552            // if either are bigdecimal use that type 
553            if (left instanceof BigDecimal || right instanceof BigDecimal) {
554                BigDecimal l = toBigDecimal(left);
555                BigDecimal r = toBigDecimal(right);
556                BigDecimal result = l.subtract(r, getMathContext());
557                return narrowBigDecimal(left, right, result);
558            }
559    
560            // otherwise treat as integers
561            BigInteger l = toBigInteger(left);
562            BigInteger r = toBigInteger(right);
563            BigInteger result = l.subtract(r);
564            return narrowBigInteger(left, right, result);
565        }
566    
567        /**
568         * Negates a value (unary minus for numbers).
569         * @param val the value to negate
570         * @return the negated value
571         * @since 2.1
572         */
573        public Object negate(Object val) {
574            if (val instanceof Integer) {
575                int valueAsInt = ((Integer) val).intValue();
576                return Integer.valueOf(-valueAsInt);
577            } else if (val instanceof Double) {
578                double valueAsDouble = ((Double) val).doubleValue();
579                return new Double(-valueAsDouble);
580            } else if (val instanceof Long) {
581                long valueAsLong = -((Long) val).longValue();
582                return Long.valueOf(valueAsLong);
583            } else if (val instanceof BigDecimal) {
584                BigDecimal valueAsBigD = (BigDecimal) val;
585                return valueAsBigD.negate();
586            } else if (val instanceof BigInteger) {
587                BigInteger valueAsBigI = (BigInteger) val;
588                return valueAsBigI.negate();
589            } else if (val instanceof Float) {
590                float valueAsFloat = ((Float) val).floatValue();
591                return new Float(-valueAsFloat);
592            } else if (val instanceof Short) {
593                short valueAsShort = ((Short) val).shortValue();
594                return Short.valueOf((short) -valueAsShort);
595            } else if (val instanceof Byte) {
596                byte valueAsByte = ((Byte) val).byteValue();
597                return Byte.valueOf((byte) -valueAsByte);
598            } else if (val instanceof Boolean) {
599                return ((Boolean) val).booleanValue() ? Boolean.FALSE : Boolean.TRUE;
600            }
601            throw new ArithmeticException("Object negation:(" + val + ")");
602        }
603    
604        /**
605         * Test if left regexp matches right.
606         *
607         * @param left first value
608         * @param right second value
609         * @return test result.
610         * @since 2.1
611         */
612        public boolean matches(Object left, Object right) {
613            if (left == null && right == null) {
614                //if both are null L == R
615                return true;
616            }
617            if (left == null || right == null) {
618                // we know both aren't null, therefore L != R
619                return false;
620            }
621            final String arg = left.toString();
622            if (right instanceof java.util.regex.Pattern) {
623                return ((java.util.regex.Pattern) right).matcher(arg).matches();
624            } else {
625                return arg.matches(right.toString());
626            }
627        }
628    
629        /**
630         * Performs a bitwise and.
631         * @param left the left operand
632         * @param right the right operator
633         * @return left & right
634         * @since 2.1
635         */
636        public Object bitwiseAnd(Object left, Object right) {
637            long l = toLong(left);
638            long r = toLong(right);
639            return Long.valueOf(l & r);
640        }
641    
642        /**
643         * Performs a bitwise or.
644         * @param left the left operand
645         * @param right the right operator
646         * @return left | right
647         * @since 2.1
648         */
649        public Object bitwiseOr(Object left, Object right) {
650            long l = toLong(left);
651            long r = toLong(right);
652            return Long.valueOf(l | r);
653        }
654    
655        /**
656         * Performs a bitwise xor.
657         * @param left the left operand
658         * @param right the right operator
659         * @return left  right
660         * @since 2.1
661         */
662        public Object bitwiseXor(Object left, Object right) {
663            long l = toLong(left);
664            long r = toLong(right);
665            return Long.valueOf(l ^ r);
666        }
667    
668        /**
669         * Performs a bitwise complement.
670         * @param val the operand
671         * @return ~val
672         * @since 2.1
673         */
674        public Object bitwiseComplement(Object val) {
675            long l = toLong(val);
676            return Long.valueOf(~l);
677        }
678    
679        /**
680         * Performs a comparison.
681         * @param left the left operand
682         * @param right the right operator
683         * @param operator the operator
684         * @return -1 if left  &lt; right; +1 if left &gt > right; 0 if left == right
685         * @throws ArithmeticException if either left or right is null
686         * @since 2.1
687         */
688        protected int compare(Object left, Object right, String operator) {
689            if (left != null && right != null) {
690                if (left instanceof BigDecimal || right instanceof BigDecimal) {
691                    BigDecimal l = toBigDecimal(left);
692                    BigDecimal r = toBigDecimal(right);
693                    return l.compareTo(r);
694                } else if (left instanceof BigInteger || right instanceof BigInteger) {
695                    BigInteger l = toBigInteger(left);
696                    BigInteger r = toBigInteger(right);
697                    return l.compareTo(r);
698                } else if (isFloatingPoint(left) || isFloatingPoint(right)) {
699                    double lhs = toDouble(left);
700                    double rhs = toDouble(right);
701                    if (Double.isNaN(lhs)) {
702                        if (Double.isNaN(rhs)) {
703                            return 0;
704                        } else {
705                            return -1;
706                        }
707                    } else if (Double.isNaN(rhs)) {
708                        // lhs is not NaN
709                        return +1;
710                    } else if (lhs < rhs) {
711                        return -1;
712                    } else if (lhs > rhs) {
713                        return +1;
714                    } else {
715                        return 0;
716                    }
717                } else if (isNumberable(left) || isNumberable(right)) {
718                    long lhs = toLong(left);
719                    long rhs = toLong(right);
720                    if (lhs < rhs) {
721                        return -1;
722                    } else if (lhs > rhs) {
723                        return +1;
724                    } else {
725                        return 0;
726                    }
727                } else if (left instanceof String || right instanceof String) {
728                    return toString(left).compareTo(toString(right));
729                } else if ("==".equals(operator)) {
730                    return left.equals(right) ? 0 : -1;
731                } else if (left instanceof Comparable<?>) {
732                    @SuppressWarnings("unchecked") // OK because of instanceof check above
733                    final Comparable<Object> comparable = (Comparable<Object>) left;
734                    return comparable.compareTo(right);
735                } else if (right instanceof Comparable<?>) {
736                    @SuppressWarnings("unchecked") // OK because of instanceof check above
737                    final Comparable<Object> comparable = (Comparable<Object>) right;
738                    return comparable.compareTo(left);
739                }
740            }
741            throw new ArithmeticException("Object comparison:(" + left + " " + operator + " " + right + ")");
742        }
743    
744        /**
745         * Test if left and right are equal.
746         *
747         * @param left first value
748         * @param right second value
749         * @return test result.
750         */
751        public boolean equals(Object left, Object right) {
752            if (left == right) {
753                return true;
754            } else if (left == null || right == null) {
755                return false;
756            } else if (left instanceof Boolean || right instanceof Boolean) {
757                return toBoolean(left) == toBoolean(right);
758            } else {
759                return compare(left, right, "==") == 0;
760            }
761        }
762    
763        /**
764         * Test if left < right.
765         *
766         * @param left first value
767         * @param right second value
768         * @return test result.
769         */
770        public boolean lessThan(Object left, Object right) {
771            if ((left == right) || (left == null) || (right == null)) {
772                return false;
773            } else {
774                return compare(left, right, "<") < 0;
775            }
776    
777        }
778    
779        /**
780         * Test if left > right.
781         *
782         * @param left first value
783         * @param right second value
784         * @return test result.
785         */
786        public boolean greaterThan(Object left, Object right) {
787            if ((left == right) || left == null || right == null) {
788                return false;
789            } else {
790                return compare(left, right, ">") > 0;
791            }
792        }
793    
794        /**
795         * Test if left <= right.
796         *
797         * @param left first value
798         * @param right second value
799         * @return test result.
800         */
801        public boolean lessThanOrEqual(Object left, Object right) {
802            if (left == right) {
803                return true;
804            } else if (left == null || right == null) {
805                return false;
806            } else {
807                return compare(left, right, "<=") <= 0;
808            }
809        }
810    
811        /**
812         * Test if left >= right.
813         *
814         * @param left first value
815         * @param right second value
816         * @return test result.
817         */
818        public boolean greaterThanOrEqual(Object left, Object right) {
819            if (left == right) {
820                return true;
821            } else if (left == null || right == null) {
822                return false;
823            } else {
824                return compare(left, right, ">=") >= 0;
825            }
826        }
827    
828        /**
829         * Coerce to a boolean (not a java.lang.Boolean).
830         *
831         * @param val Object to be coerced.
832         * @return The boolean coerced value, or false if none possible.
833         */
834        public boolean toBoolean(Object val) {
835            if (val == null) {
836                controlNullOperand();
837                return false;
838            } else if (val instanceof Boolean) {
839                return ((Boolean) val).booleanValue();
840            } else if (val instanceof Number) {
841                double number = toDouble(val);
842                return !Double.isNaN(number) && number != 0.d;
843            } else if (val instanceof String) {
844                String strval = val.toString();
845                return strval.length() > 0 && !"false".equals(strval);
846            }
847            // TODO: is this a reasonable default?
848            return false;
849        }
850    
851        /**
852         * Coerce to a int.
853         *
854         * @param val Object to be coerced.
855         * @return The int coerced value.
856         */
857        public int toInteger(Object val) {
858            if (val == null) {
859                controlNullOperand();
860                return 0;
861            } else if (val instanceof Double) {
862                if (!Double.isNaN(((Double) val).doubleValue())) {
863                    return 0;
864                } else {
865                    return ((Double) val).intValue();
866                }
867            } else if (val instanceof Number) {
868                return ((Number) val).intValue();
869            } else if (val instanceof String) {
870                if ("".equals(val)) {
871                    return 0;
872                }
873                return Integer.parseInt((String) val);
874            } else if (val instanceof Boolean) {
875                return ((Boolean) val).booleanValue() ? 1 : 0;
876            } else if (val instanceof Character) {
877                return ((Character) val).charValue();
878            }
879    
880            throw new ArithmeticException("Integer coercion: "
881                    + val.getClass().getName() + ":(" + val + ")");
882        }
883    
884        /**
885         * Coerce to a long (not a java.lang.Long).
886         *
887         * @param val Object to be coerced.
888         * @return The long coerced value.
889         */
890        public long toLong(Object val) {
891            if (val == null) {
892                controlNullOperand();
893                return 0L;
894            } else if (val instanceof Double) {
895                if (!Double.isNaN(((Double) val).doubleValue())) {
896                    return 0;
897                } else {
898                    return ((Double) val).longValue();
899                }
900            } else if (val instanceof Number) {
901                return ((Number) val).longValue();
902            } else if (val instanceof String) {
903                if ("".equals(val)) {
904                    return 0;
905                } else {
906                    return Long.parseLong((String) val);
907                }
908            } else if (val instanceof Boolean) {
909                return ((Boolean) val).booleanValue() ? 1L : 0L;
910            } else if (val instanceof Character) {
911                return ((Character) val).charValue();
912            }
913    
914            throw new ArithmeticException("Long coercion: "
915                    + val.getClass().getName() + ":(" + val + ")");
916        }
917    
918        /**
919         * Get a BigInteger from the object passed.
920         * Null and empty string maps to zero.
921         * @param val the object to be coerced.
922         * @return a BigDecimal.
923         * @throws NullPointerException if val is null and mode is strict.
924         */
925        public BigInteger toBigInteger(Object val) {
926            if (val == null) {
927                controlNullOperand();
928                return BigInteger.ZERO;
929            } else if (val instanceof BigInteger) {
930                return (BigInteger) val;
931            } else if (val instanceof Double) {
932                if (!Double.isNaN(((Double) val).doubleValue())) {
933                    return new BigInteger(val.toString());
934                } else {
935                    return BigInteger.ZERO;
936                }
937            } else if (val instanceof Number) {
938                return new BigInteger(val.toString());
939            } else if (val instanceof String) {
940                String string = (String) val;
941                if ("".equals(string.trim())) {
942                    return BigInteger.ZERO;
943                } else {
944                    return new BigInteger(string);
945                }
946            } else if (val instanceof Character) {
947                int i = ((Character) val).charValue();
948                return BigInteger.valueOf(i);
949            }
950    
951            throw new ArithmeticException("BigInteger coercion: "
952                    + val.getClass().getName() + ":(" + val + ")");
953        }
954    
955        /**
956         * Get a BigDecimal from the object passed.
957         * Null and empty string maps to zero.
958         * @param val the object to be coerced.
959         * @return a BigDecimal.
960         * @throws NullPointerException if val is null and mode is strict.
961         */
962        public BigDecimal toBigDecimal(Object val) {
963            if (val instanceof BigDecimal) {
964                return roundBigDecimal((BigDecimal) val);
965            } else if (val == null) {
966                controlNullOperand();
967                return BigDecimal.ZERO;
968            } else if (val instanceof String) {
969                String string = ((String) val).trim();
970                if ("".equals(string)) {
971                    return BigDecimal.ZERO;
972                }
973                return roundBigDecimal(new BigDecimal(string, getMathContext()));
974            } else if (val instanceof Double) {
975                if (!Double.isNaN(((Double) val).doubleValue())) {
976                    return roundBigDecimal(new BigDecimal(val.toString(), getMathContext()));
977                } else {
978                    return BigDecimal.ZERO;
979                }
980            } else if (val instanceof Number) {
981                return roundBigDecimal(new BigDecimal(val.toString(), getMathContext()));
982            } else if (val instanceof Character) {
983                int i = ((Character) val).charValue();
984                return new BigDecimal(i);
985            }
986    
987            throw new ArithmeticException("BigDecimal coercion: "
988                    + val.getClass().getName() + ":(" + val + ")");
989        }
990    
991        /**
992         * Coerce to a double.
993         *
994         * @param val Object to be coerced.
995         * @return The double coerced value.
996         * @throws NullPointerException if val is null and mode is strict.
997         */
998        public double toDouble(Object val) {
999            if (val == null) {
1000                controlNullOperand();
1001                return 0;
1002            } else if (val instanceof Double) {
1003                return ((Double) val).doubleValue();
1004            } else if (val instanceof Number) {
1005                //The below construct is used rather than ((Number)val).doubleValue() to ensure
1006                //equality between comparing new Double( 6.4 / 3 ) and the jexl expression of 6.4 / 3
1007                return Double.parseDouble(String.valueOf(val));
1008            } else if (val instanceof Boolean) {
1009                return ((Boolean) val).booleanValue() ? 1. : 0.;
1010            } else if (val instanceof String) {
1011                String string = ((String) val).trim();
1012                if ("".equals(string)) {
1013                    return Double.NaN;
1014                } else {
1015                    // the spec seems to be iffy about this.  Going to give it a wack anyway
1016                    return Double.parseDouble(string);
1017                }
1018            } else if (val instanceof Character) {
1019                int i = ((Character) val).charValue();
1020                return i;
1021            }
1022    
1023            throw new ArithmeticException("Double coercion: "
1024                    + val.getClass().getName() + ":(" + val + ")");
1025        }
1026    
1027        /**
1028         * Coerce to a string.
1029         *
1030         * @param val Object to be coerced.
1031         * @return The String coerced value.
1032         * @throws NullPointerException if val is null and mode is strict.
1033         */
1034        public String toString(Object val) {
1035            if (val == null) {
1036                controlNullOperand();
1037                return "";
1038            } else if (val instanceof Double) {
1039                Double dval = (Double) val;
1040                if (Double.isNaN(dval.doubleValue())) {
1041                    return "";
1042                } else {
1043                    return dval.toString();
1044                }
1045            } else {
1046                return val.toString();
1047            }
1048        }
1049    
1050        /**
1051         * Given a Number, return back the value using the smallest type the result
1052         * will fit into. This works hand in hand with parameter 'widening' in java
1053         * method calls, e.g. a call to substring(int,int) with an int and a long
1054         * will fail, but a call to substring(int,int) with an int and a short will
1055         * succeed.
1056         *
1057         * @param original the original number.
1058         * @return a value of the smallest type the original number will fit into.
1059         */
1060        public Number narrow(Number original) {
1061            return narrowNumber(original, null);
1062        }
1063    
1064        /**
1065         * Whether we consider the narrow class as a potential candidate for narrowing the source.
1066         * @param narrow the target narrow class
1067         * @param source the orginal source class
1068         * @return true if attempt to narrow source to target is accepted
1069         * @since 2.1
1070         */
1071        protected boolean narrowAccept(Class<?> narrow, Class<?> source) {
1072            return narrow == null || narrow.equals(source);
1073        }
1074    
1075        /**
1076         * Given a Number, return back the value attempting to narrow it to a target class.
1077         * @param original the original number
1078         * @param narrow the attempted target class
1079         * @return  the narrowed number or the source if no narrowing was possible
1080         * @since 2.1
1081         */
1082        protected Number narrowNumber(Number original, Class<?> narrow) {
1083            if (original == null) {
1084                return original;
1085            }
1086            Number result = original;
1087            if (original instanceof BigDecimal) {
1088                BigDecimal bigd = (BigDecimal) original;
1089                // if it's bigger than a double it can't be narrowed
1090                if (bigd.compareTo(BIGD_DOUBLE_MAX_VALUE) > 0) {
1091                    return original;
1092                } else {
1093                    try {
1094                        long l = bigd.longValueExact();
1095                        // coerce to int when possible (int being so often used in method parms)
1096                        if (narrowAccept(narrow, Integer.class)
1097                                && l <= Integer.MAX_VALUE
1098                                && l >= Integer.MIN_VALUE) {
1099                            return Integer.valueOf((int) l);
1100                        } else if (narrowAccept(narrow, Long.class)) {
1101                            return Long.valueOf(l);
1102                        }
1103                    } catch (ArithmeticException xa) {
1104                        // ignore, no exact value possible
1105                    }
1106                }
1107            }
1108            if (original instanceof Double || original instanceof Float || original instanceof BigDecimal) {
1109                double value = original.doubleValue();
1110                if (narrowAccept(narrow, Float.class)
1111                        && value <= Float.MAX_VALUE
1112                        && value >= Float.MIN_VALUE) {
1113                    result = Float.valueOf(result.floatValue());
1114                }
1115                // else it fits in a double only
1116            } else {
1117                if (original instanceof BigInteger) {
1118                    BigInteger bigi = (BigInteger) original;
1119                    // if it's bigger than a Long it can't be narrowed
1120                    if (bigi.compareTo(BIGI_LONG_MAX_VALUE) > 0
1121                            || bigi.compareTo(BIGI_LONG_MIN_VALUE) < 0) {
1122                        return original;
1123                    }
1124                }
1125                long value = original.longValue();
1126                if (narrowAccept(narrow, Byte.class)
1127                        && value <= Byte.MAX_VALUE
1128                        && value >= Byte.MIN_VALUE) {
1129                    // it will fit in a byte
1130                    result = Byte.valueOf((byte) value);
1131                } else if (narrowAccept(narrow, Short.class)
1132                        && value <= Short.MAX_VALUE
1133                        && value >= Short.MIN_VALUE) {
1134                    result = Short.valueOf((short) value);
1135                } else if (narrowAccept(narrow, Integer.class)
1136                        && value <= Integer.MAX_VALUE
1137                        && value >= Integer.MIN_VALUE) {
1138                    result = Integer.valueOf((int) value);
1139                }
1140                // else it fits in a long
1141            }
1142            return result;
1143        }
1144    }