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 */
017package org.apache.commons.jexl2;
018
019import java.lang.reflect.Array;
020import java.lang.reflect.Field;
021import java.math.BigDecimal;
022import java.math.BigInteger;
023import java.math.MathContext;
024
025/**
026 * Perform arithmetic.
027 * <p>
028 * All arithmetic operators (+, - , *, /, %) follow the same rules regarding their arguments.
029 * </p>
030 * <ol>
031 * <li>If both are null, result is 0</li>
032 * <li>If either is a BigDecimal, coerce both to BigDecimal and and perform operation</li>
033 * <li>If either is a floating point number, coerce both to Double and perform operation</li>
034 * <li>If both are BigInteger, treat as BigInteger and perform operation</li>
035 * <li>Else treat as BigInteger, perform operation and attempt to narrow result:
036 * <ol>
037 * <li>if both arguments can be narrowed to Integer, narrow result to Integer</li>
038 * <li>if both arguments can be narrowed to Long, narrow result to Long</li>
039 * <li>Else return result as BigInteger</li>
040 * </ol>
041 * </li>
042 * </ol>
043 * Note that the only exception throw by JexlArithmetic is ArithmeticException.
044 * @since 2.0
045 */
046public 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 &amp; 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 &lt; 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 &gt; 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 &le; 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 &ge; 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}