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 & 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 < right; +1 if left > > 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 }