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