View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jexl2;
18  
19  import java.lang.reflect.Array;
20  import java.lang.reflect.Field;
21  import java.math.BigDecimal;
22  import java.math.BigInteger;
23  import java.math.MathContext;
24  
25  /**
26   * Perform arithmetic.
27   * <p>
28   * All arithmetic operators (+, - , *, /, %) follow the same rules regarding their arguments.
29   * <ol>
30   * <li>If both are null, result is 0</li>
31   * <li>If either is a BigDecimal, coerce both to BigDecimal and and perform operation</li>
32   * <li>If either is a floating point number, coerce both to Double and perform operation</li>
33   * <li>If both are BigInteger, treat as BigInteger and perform operation</li>
34   * <li>Else treat as BigInteger, perform operation and attempt to narrow result:
35   * <ol>
36   * <li>if both arguments can be narrowed to Integer, narrow result to Integer</li>
37   * <li>if both arguments can be narrowed to Long, narrow result to Long</li>
38   * <li>Else return result as BigInteger</li>
39   * </ol>
40   * </li>
41   * </ol>
42   * </p>
43   * Note that the only exception throw by JexlArithmetic is ArithmeticException.
44   * @since 2.0
45   */
46  public class JexlArithmetic {
47      /** Double.MAX_VALUE as BigDecimal. */
48      protected static final BigDecimal BIGD_DOUBLE_MAX_VALUE = BigDecimal.valueOf(Double.MAX_VALUE);
49      /** Double.MIN_VALUE as BigDecimal. */
50      protected static final BigDecimal BIGD_DOUBLE_MIN_VALUE = BigDecimal.valueOf(Double.MIN_VALUE);
51      /** Long.MAX_VALUE as BigInteger. */
52      protected static final BigInteger BIGI_LONG_MAX_VALUE = BigInteger.valueOf(Long.MAX_VALUE);
53      /** Long.MIN_VALUE as BigInteger. */
54      protected static final BigInteger BIGI_LONG_MIN_VALUE = BigInteger.valueOf(Long.MIN_VALUE);
55      /** 
56       * Default BigDecimal scale. 
57       * @since 2.1 
58       */
59      protected static final int BIGD_SCALE = -1;
60      /** Whether this JexlArithmetic instance behaves in strict or lenient mode.
61       * May be made final in a later version.
62       */
63      private volatile boolean strict;
64      /** 
65       * The big decimal math context.
66       * @since 2.1 
67       */
68      protected final MathContext mathContext;
69      /** 
70       * The big decimal scale.
71       * @since 2.1
72       */
73      protected final int mathScale;
74  
75      /**
76       * Creates a JexlArithmetic.
77       * @param lenient whether this arithmetic is lenient or strict
78       */
79      public JexlArithmetic(boolean lenient) {
80          this(lenient, MathContext.DECIMAL128, BIGD_SCALE);
81      }
82  
83      /**
84       * Creates a JexlArithmetic.
85       * @param lenient whether this arithmetic is lenient or strict
86       * @param bigdContext the math context instance to use for +,-,/,*,% operations on big decimals.
87       * @param bigdScale the scale used for big decimals.
88       * @since 2.1
89       */
90      public JexlArithmetic(boolean lenient, MathContext bigdContext, int bigdScale) {
91          this.strict = !lenient;
92          this.mathContext = bigdContext;
93          this.mathScale = bigdScale;
94      }
95      
96      
97      /**
98       * Sets whether this JexlArithmetic instance triggers errors during evaluation when
99       * 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 }