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