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 &amp; 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    }