View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.jexl3;
19  
20  import static java.lang.StrictMath.floor;
21  import static org.apache.commons.jexl3.JexlOperator.EQ;
22  
23  import java.lang.reflect.Array;
24  import java.lang.reflect.Constructor;
25  import java.lang.reflect.InvocationTargetException;
26  import java.lang.reflect.Method;
27  import java.math.BigDecimal;
28  import java.math.BigInteger;
29  import java.math.MathContext;
30  import java.util.Collection;
31  import java.util.Map;
32  import java.util.concurrent.atomic.AtomicBoolean;
33  import java.util.regex.Matcher;
34  import java.util.regex.Pattern;
35  
36  import org.apache.commons.jexl3.introspection.JexlMethod;
37  
38  /**
39   * Perform arithmetic, implements JexlOperator methods.
40   *
41   * <p>This is the class to derive to implement new operator behaviors.</p>
42   *
43   * <p>The 5 base arithmetic operators (+, - , *, /, %) follow the same evaluation rules regarding their arguments.</p>
44   * <ol>
45   *   <li>If both are null, result is 0 if arithmetic (or operator) is non-strict, ArithmeticException is thrown
46   *   otherwise</li>
47   *   <li>If both arguments are numberable - any kind of integer including boolean -, coerce both to Long and coerce
48   *   result to the most precise argument class ({@code boolean < byte < short < int < long});
49   *   if long operation would cause overflow, return a BigInteger</li>
50   *   <li>If either argument is a BigDecimal, coerce both to BigDecimal, operator returns BigDecimal</li>
51   *   <li>If either argument is a floating point number, coerce both to Double, operator returns Double</li>
52   *   <li>Else treat as BigInteger, perform operation and narrow result to the most precise argument class
53   *   </li>
54   * </ol>
55   *
56   * Note that the only exception thrown by JexlArithmetic is and must be ArithmeticException.
57   *
58   * @see JexlOperator
59   * @since 2.0
60   */
61  public class JexlArithmetic {
62      /**
63       * Helper interface used when creating an array literal.
64       *
65       * <p>The default implementation creates an array and attempts to type it strictly.</p>
66       *
67       * <ul>
68       *   <li>If all objects are of the same type, the array returned will be an array of that same type</li>
69       *   <li>If all objects are Numbers, the array returned will be an array of Numbers</li>
70       *   <li>If all objects are convertible to a primitive type, the array returned will be an array
71       *       of the primitive type</li>
72       * </ul>
73       */
74      public interface ArrayBuilder {
75  
76          /**
77           * Adds a literal to the array.
78           *
79           * @param value the item to add
80           */
81          void add(Object value);
82  
83          /**
84           * Creates the actual "array" instance.
85           *
86           * @param extended true when the last argument is ', ...'
87           * @return the array
88           */
89          Object create(boolean extended);
90      }
91  
92      /** Marker class for coercion operand exceptions. */
93      public static class CoercionException extends ArithmeticException {
94          private static final long serialVersionUID = 202402081150L;
95  
96          /**
97           * Constructs a new instance.
98           *
99           * @param msg the detail message.
100          */
101         public CoercionException(final String msg) {
102             super(msg);
103         }
104 
105         /**
106          * Constructs a new instance.
107          *
108          * @param msg the detail message.
109          * @param cause The cause of this Throwable.
110          * @since 3.5.0
111          */
112         public CoercionException(final String msg, final Throwable cause) {
113             super(msg);
114             initCause(cause);
115         }
116     }
117 
118     /**
119      * Helper interface used when creating a map literal.
120      * <p>The default implementation creates a java.util.HashMap.</p>
121      */
122     public interface MapBuilder {
123         /**
124          * Creates the actual "map" instance.
125          *
126          * @return the map
127          */
128         Object create();
129 
130         /**
131          * Adds a new entry to the map.
132          *
133          * @param key   the map entry key
134          * @param value the map entry value
135          */
136         void put(Object key, Object value);
137     }
138 
139     /** Marker class for null operand exceptions. */
140     public static class NullOperand extends ArithmeticException {
141         private static final long serialVersionUID = 4720876194840764770L;
142 
143         /** Default constructor */
144         public NullOperand() {
145         } // satisfy Javadoc
146     }
147 
148     /**
149      * Helper interface used when creating a set literal.
150      * <p>The default implementation creates a java.util.HashSet.</p>
151      */
152     public interface SetBuilder {
153         /**
154          * Adds a literal to the set.
155          *
156          * @param value the item to add
157          */
158         void add(Object value);
159 
160         /**
161          * Creates the actual "set" instance.
162          *
163          * @return the set
164          */
165         Object create();
166     }
167 
168     /**
169      * The interface that uberspects JexlArithmetic classes.
170      * <p>This allows overloaded operator methods discovery.</p>
171      */
172     public interface Uberspect {
173         /**
174          * Gets the most specific method for an operator.
175          *
176          * @param operator the operator
177          * @param args     the arguments
178          * @return the most specific method or null if no specific override could be found
179          */
180         JexlMethod getOperator(JexlOperator operator, Object... args);
181 
182         /**
183          * Checks whether this uberspect has overloads for a given operator.
184          *
185          * @param operator the operator to check
186          * @return true if an overload exists, false otherwise
187          */
188         boolean overloads(JexlOperator operator);
189     }
190 
191     /** Double.MAX_VALUE as BigDecimal. */
192     protected static final BigDecimal BIGD_DOUBLE_MAX_VALUE = BigDecimal.valueOf(Double.MAX_VALUE);
193 
194     /** -Double.MAX_VALUE as BigDecimal. */
195     protected static final BigDecimal BIGD_DOUBLE_MIN_VALUE = BigDecimal.valueOf(-Double.MAX_VALUE);
196 
197     /** Long.MAX_VALUE as BigInteger. */
198     protected static final BigInteger BIGI_LONG_MAX_VALUE = BigInteger.valueOf(Long.MAX_VALUE);
199 
200     /** Long.MIN_VALUE as BigInteger. */
201     protected static final BigInteger BIGI_LONG_MIN_VALUE = BigInteger.valueOf(Long.MIN_VALUE);
202 
203     /** Default BigDecimal scale. */
204     protected static final int BIGD_SCALE = -1;
205 
206     /**
207      * The float regular expression pattern.
208      * <p>
209      * The decimal and exponent parts are optional and captured allowing to determine if the number is a real
210      * by checking whether one of these 2 capturing groups is not empty.
211      */
212     public static final Pattern FLOAT_PATTERN = Pattern.compile("^[+-]?\\d*(\\.\\d*)?([eE][+-]?\\d+)?$");
213 
214     /**
215      * Attempts transformation of potential array in an abstract list or leave as is.
216      * <p>An array (as in int[]) is not convenient to call methods so when encountered we turn them into lists</p>
217      * @param container an array or on object
218      * @return an abstract list wrapping the array instance or the initial argument
219      * @see org.apache.commons.jexl3.internal.introspection.ArrayListWrapper
220      */
221     private static Object arrayWrap(final Object container) {
222         return container.getClass().isArray()
223                 ? new org.apache.commons.jexl3.internal.introspection.ArrayListWrapper(container)
224                 : container;
225     }
226 
227     private static boolean computeCompare321(final JexlArithmetic arithmetic) {
228         Class<?> arithmeticClass = arithmetic.getClass();
229         while(arithmeticClass != JexlArithmetic.class) {
230             try {
231                 final Method cmp = arithmeticClass.getDeclaredMethod("compare", Object.class, Object.class, String.class);
232                if (cmp.getDeclaringClass() != JexlArithmetic.class) {
233                    return true;
234                }
235             } catch (final NoSuchMethodException xany) {
236                 arithmeticClass = arithmeticClass.getSuperclass();
237             }
238         }
239         return false;
240     }
241 
242     /**
243      * Checks if the product of the arguments overflows a {@code long}.
244      * <p>see java8 Math.multiplyExact
245      * @param x the first value
246      * @param y the second value
247      * @param r the product
248      * @return true if product fits a long, false if it overflows
249      */
250     @SuppressWarnings("MagicNumber")
251     protected static boolean isMultiplyExact(final long x, final long y, final long r) {
252         final long ax = Math.abs(x);
253         final long ay = Math.abs(y);
254         // Some bits greater than 2^31 that might cause overflow
255         // Check the result using the divide operator
256         // and check for the special case of Long.MIN_VALUE * -1
257         return !((ax | ay) >>> Integer.SIZE - 1 != 0
258                  && (y != 0 && r / y != x
259                      || x == Long.MIN_VALUE && y == -1));
260     }
261 
262     /** Whether this JexlArithmetic instance behaves in strict or lenient mode. */
263     private final boolean strict;
264 
265     /** The big decimal math context. */
266     private final MathContext mathContext;
267 
268     /** The big decimal scale. */
269     private final int mathScale;
270 
271     /** The dynamic constructor. */
272     private final Constructor<? extends JexlArithmetic> ctor;
273 
274     /**
275      * Determines if the compare method(Object, Object, String) is overriden in this class or one of its
276      * superclasses.
277      */
278     private final boolean compare321 = computeCompare321(this);
279 
280     /**
281      * Creates a JexlArithmetic.
282      * <p>If you derive your own arithmetic, implement the
283      * other constructor that may be needed when dealing with options.
284      *
285      * @param astrict whether this arithmetic is strict or lenient
286      */
287     public JexlArithmetic(final boolean astrict) {
288         this(astrict, null, Integer.MIN_VALUE);
289     }
290 
291     /**
292      * Creates a JexlArithmetic.
293      * <p>The constructor to define in derived classes.
294      *
295      * @param astrict     whether this arithmetic is lenient or strict
296      * @param bigdContext the math context instance to use for +,-,/,*,% operations on big decimals.
297      * @param bigdScale   the scale used for big decimals.
298      */
299     public JexlArithmetic(final boolean astrict, final MathContext bigdContext, final int bigdScale) {
300         this.strict = astrict;
301         this.mathContext = bigdContext == null ? MathContext.DECIMAL128 : bigdContext;
302         this.mathScale = bigdScale == Integer.MIN_VALUE ? BIGD_SCALE : bigdScale;
303         Constructor<? extends JexlArithmetic> actor = null;
304         try {
305             actor = getClass().getConstructor(boolean.class, MathContext.class, int.class);
306         } catch (final Exception xany) {
307             // ignore
308         }
309         this.ctor = actor;
310     }
311 
312     /**
313      * Add two values together.
314      * <p>
315      * If any numeric add fails on coercion to the appropriate type,
316      * treat as Strings and do concatenation.
317      * </p>
318      *
319      * @param left  left argument
320      * @param right  right argument
321      * @return left + right.
322      */
323     public Object add(final Object left, final Object right) {
324         if (left == null && right == null) {
325             return controlNullNullOperands(JexlOperator.ADD);
326         }
327         final boolean strconcat = strict
328                             ? left instanceof String || right instanceof String
329                             : left instanceof String && right instanceof String;
330         if (!strconcat) {
331             try {
332                 final boolean strictCast = isStrict(JexlOperator.ADD);
333                 // if both (non-null) args fit as long
334                 final Number ln = asLongNumber(strictCast, left);
335                 final Number rn = asLongNumber(strictCast, right);
336                 if (ln != null && rn != null) {
337                     final long x = ln.longValue();
338                     final long y = rn.longValue();
339                     final long result = x + y;
340                     // detect overflow, see java8 Math.addExact
341                     if (((x ^ result) & (y ^ result)) < 0) {
342                         return BigInteger.valueOf(x).add(BigInteger.valueOf(y));
343                     }
344                     return narrowLong(left, right, result);
345                 }
346                 // if either are BigDecimal, use that type
347                 if (left instanceof BigDecimal || right instanceof BigDecimal) {
348                     final BigDecimal l = toBigDecimal(strictCast, left);
349                     final BigDecimal r = toBigDecimal(strictCast, right);
350                     return l.add(r, getMathContext());
351                 }
352                 // if either are floating point (double or float), use double
353                 if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
354                     final double l = toDouble(strictCast, left);
355                     final double r = toDouble(strictCast, right);
356                     return l + r;
357                 }
358                 // otherwise treat as BigInteger
359                 final BigInteger l = toBigInteger(strictCast, left);
360                 final BigInteger r = toBigInteger(strictCast, right);
361                 final BigInteger result = l.add(r);
362                 return narrowBigInteger(left, right, result);
363             } catch (final ArithmeticException nfe) {
364                 // ignore and continue in sequence
365             }
366         }
367         return (left == null ? "" : toString(left)).concat(right == null ? "" : toString(right));
368     }
369 
370     /**
371      * Performs a bitwise and.
372      *
373      * @param left  the left operand
374      * @param right the right operator
375      * @return left &amp; right
376      */
377     public Object and(final Object left, final Object right) {
378         final long l = toLong(left);
379         final long r = toLong(right);
380         return l & r;
381     }
382 
383     /**
384      * Creates an array builder.
385      * @param size the number of elements in the array
386      * @return an array builder instance
387      * @deprecated since 3.3.1
388      */
389     @Deprecated
390     public ArrayBuilder arrayBuilder(final int size) {
391         return arrayBuilder(size, false);
392     }
393 
394     /**
395      * Called by the interpreter when evaluating a literal array.
396      *
397      * @param size the number of elements in the array
398      * @param extended whether the map is extended or not
399      * @return the array builder
400      */
401     public ArrayBuilder arrayBuilder(final int size, final boolean extended) {
402         return new org.apache.commons.jexl3.internal.ArrayBuilder(size, extended);
403     }
404 
405     /**
406      * Checks if value class is a number that can be represented exactly in a long.
407      * <p>For convenience, booleans are converted as 1/0 (true/false).</p>
408      *
409      * @param strict whether null argument is converted as 0 or remains null
410      * @param value  argument
411      * @return a non-null value if argument can be represented by a long
412      */
413     protected Number asLongNumber(final boolean strict, final Object value) {
414         if (value instanceof Long
415             || value instanceof Integer
416             || value instanceof Short
417             || value instanceof Byte) {
418             return (Number) value;
419         }
420         if (value instanceof Boolean) {
421             return (boolean) value ? 1L : 0L;
422         }
423         if (value instanceof AtomicBoolean) {
424             final AtomicBoolean b = (AtomicBoolean) value;
425             return b.get() ? 1L : 0L;
426         }
427         if (value == null && !strict) {
428             return 0L;
429         }
430         return null;
431     }
432 
433     /**
434      * Checks if value class is a number that can be represented exactly in a long.
435      * <p>For convenience, booleans are converted as 1/0 (true/false).</p>
436      *
437      * @param value  argument
438      * @return a non-null value if argument can be represented by a long
439      */
440     protected Number asLongNumber(final Object value) {
441         return asLongNumber(strict, value);
442     }
443 
444     /**
445      * Use or overload and() instead.
446      * @param lhs left hand side
447      * @param rhs right hand side
448      * @return lhs &amp; rhs
449      * @see JexlArithmetic#and
450      * @deprecated 3.0
451      */
452     @Deprecated
453     public final Object bitwiseAnd(final Object lhs, final Object rhs) {
454         return and(lhs, rhs);
455     }
456 
457     /**
458      * Use or overload or() instead.
459      *
460      * @param lhs left hand side
461      * @param rhs right hand side
462      * @return lhs | rhs
463      * @see JexlArithmetic#or
464      * @deprecated 3.0
465      */
466     @Deprecated
467     public final Object bitwiseOr(final Object lhs, final Object rhs) {
468         return or(lhs, rhs);
469     }
470 
471     /**
472      * Use or overload xor() instead.
473      *
474      * @param lhs left hand side
475      * @param rhs right hand side
476      * @return lhs ^ rhs
477      * @see JexlArithmetic#xor
478      * @deprecated 3.0
479      */
480     @Deprecated
481     public final Object bitwiseXor(final Object lhs, final Object rhs) {
482         return xor(lhs, rhs);
483     }
484 
485     /**
486      * Checks whether a potential collection contains another.
487      * <p>Made protected to make it easier to override if needed.</p>
488      * @param collection the container which can be a collection or an array (even of primitive)
489      * @param value the value which can be a collection or an array (even of primitive) or a singleton
490      * @return test result or null if there is no arithmetic solution
491      */
492     protected Boolean collectionContains(final Object collection, final Object value) {
493         // convert arrays if needed
494         final Object left = arrayWrap(collection);
495         if (left instanceof Collection) {
496             final Object right = arrayWrap(value);
497             if (right instanceof Collection) {
498                 return ((Collection<?>) left).containsAll((Collection<?>) right);
499             }
500             return ((Collection<?>) left).contains(value);
501         }
502         return null;
503     }
504 
505     /**
506      * Performs a comparison.
507      *
508      * @param left     the left operand
509      * @param right    the right operator
510      * @param operator the operator
511      * @return -1 if left &lt; right; +1 if left &gt; right; 0 if left == right
512      * @throws ArithmeticException if either left or right is null
513      */
514     protected int compare(final Object left, final Object right, final JexlOperator operator) {
515         // this is a temporary way of allowing pre-3.3 code that overrode compare() to still call
516         // the user method. This method will merge with doCompare in 3.4 and the compare321 flag will disappear.
517         return compare321
518                 ? compare(left, right, operator.toString())
519                 : doCompare(left, right, operator);
520     }
521 
522     /**
523      * Any override of this method (pre 3.3) should be modified to match the new signature.
524      * @param left left operand
525      * @param right right operand
526      * @param symbol the operator symbol
527      * @return -1 if left &lt; right; +1 if left &gt; right; 0 if left == right
528      * {@link JexlArithmetic#compare(Object, Object, JexlOperator)}
529      * @deprecated 3.3
530      */
531     @Deprecated
532     protected int compare(final Object left, final Object right, final String symbol) {
533         JexlOperator operator;
534         try {
535             operator = JexlOperator.valueOf(symbol);
536         } catch (final IllegalArgumentException xill) {
537             // ignore
538             operator = EQ;
539         }
540         return doCompare(left, right, operator);
541     }
542 
543     /**
544      * Performs a bitwise complement.
545      *
546      * @param val the operand
547      * @return ~val
548      */
549     public Object complement(final Object val) {
550         final boolean strictCast = isStrict(JexlOperator.COMPLEMENT);
551         final long l = toLong(strictCast, val);
552         return ~l;
553     }
554 
555     /**
556      * Test if left contains right (right matches/in left).
557      * <p>Beware that this &quot;contains &quot; method arguments order is the opposite of the
558      * &quot;in/matches&quot; operator arguments.
559      * {@code x =~ y} means {@code y contains x} thus {@code contains(x, y)}.</p>
560      * <p>When this method returns null during evaluation, the operator code continues trying to find
561      * one through the uberspect.</p>
562      * @param container the container
563      * @param value the value
564      * @return test result or null if there is no arithmetic solution
565      */
566     public Boolean contains(final Object container, final Object value) {
567         if (value == null && container == null) {
568             //if both are null L == R
569             return true;
570         }
571         if (value == null || container == null) {
572             // we know both aren't null, therefore L != R
573             return false;
574         }
575         // use arithmetic / pattern matching ?
576         if (container instanceof java.util.regex.Pattern) {
577             return ((java.util.regex.Pattern) container).matcher(value.toString()).matches();
578         }
579         if (container instanceof CharSequence) {
580             return value.toString().matches(container.toString());
581         }
582         // try contains on map key
583         if (container instanceof Map<?, ?>) {
584             if (value instanceof Map<?, ?>) {
585                 return ((Map<?, ?>) container).keySet().containsAll(((Map<?, ?>) value).keySet());
586             }
587             return ((Map<?, ?>) container).containsKey(value);
588         }
589         // try contains on collection
590         return collectionContains(container, value);
591     }
592 
593     /**
594      * The result of +,/,-,*,% when both operands are null.
595      *
596      * @return Integer(0) if lenient
597      * @throws JexlArithmetic.NullOperand if strict
598      * @deprecated 3.3
599      */
600     @Deprecated
601     protected Object controlNullNullOperands() {
602         if (isStrict()) {
603             throw new NullOperand();
604         }
605         return 0;
606     }
607 
608     /**
609      * The result of +,/,-,*,% when both operands are null.
610      * @param operator the actual operator
611      * @return Integer(0) if lenient
612      * @throws  JexlArithmetic.NullOperand if strict-cast
613      * @since 3.3
614      */
615     protected Object controlNullNullOperands(final JexlOperator operator) {
616         if (isStrict(operator)) {
617             throw new NullOperand();
618         }
619         return 0;
620     }
621 
622     /**
623      * Throws an NullOperand exception if arithmetic is strict-cast.
624      *
625      * @throws  JexlArithmetic.NullOperand if strict
626      * @deprecated 3.3
627      */
628     @Deprecated
629     protected void controlNullOperand() {
630         if (isStrict()) {
631             throw new NullOperand();
632         }
633     }
634 
635     /**
636      * Throws an NullOperand exception if arithmetic is strict-cast.
637      * <p>This method is called by the cast methods ({@link #toBoolean(boolean, Object)},
638      * {@link #toInteger(boolean, Object)}, {@link #toDouble(boolean, Object)},
639      * {@link #toString(boolean, Object)}, {@link #toBigInteger(boolean, Object)},
640      * {@link #toBigDecimal(boolean, Object)}) when they encounter a null argument.</p>
641      *
642      * @param strictCast whether strict cast is required
643      * @param defaultValue the default value to return, if not strict
644      * @param <T> the value type
645      * @return the default value is strict is false
646      * @throws JexlArithmetic.NullOperand if strict-cast
647      * @since 3.3
648      */
649     protected <T> T controlNullOperand(final boolean strictCast, final T defaultValue) {
650         if (strictCast) {
651             throw new NullOperand();
652         }
653         return defaultValue;
654     }
655 
656     /**
657      * The last method called before returning a result from a script execution.
658      * @param returned the returned value
659      * @return the controlled returned value
660      */
661     public Object controlReturn(final Object returned) {
662         return returned;
663     }
664 
665     /**
666      * Creates a literal range.
667      * <p>The default implementation only accepts integers and longs.</p>
668      *
669      * @param from the included lower bound value (null if none)
670      * @param to   the included upper bound value (null if none)
671      * @return the range as an iterable
672      * @throws ArithmeticException as an option if creation fails
673      */
674     public Iterable<?> createRange(final Object from, final Object to) throws ArithmeticException {
675         final long lfrom = toLong(from);
676         final long lto = toLong(to);
677         if (lfrom >= Integer.MIN_VALUE && lfrom <= Integer.MAX_VALUE
678                 && lto >= Integer.MIN_VALUE && lto <= Integer.MAX_VALUE) {
679             return org.apache.commons.jexl3.internal.IntegerRange.create((int) lfrom, (int) lto);
680         }
681         return org.apache.commons.jexl3.internal.LongRange.create(lfrom, lto);
682     }
683 
684     /**
685      * Creates a JexlArithmetic instance.
686      * Called by options(...) method when another instance of the same class of arithmetic is required.
687      * @see #options(org.apache.commons.jexl3.JexlEngine.Options)
688      * @param astrict     whether this arithmetic is lenient or strict
689      * @param bigdContext the math context instance to use for +,-,/,*,% operations on big decimals.
690      * @param bigdScale   the scale used for big decimals.
691      * @return default is a new JexlArithmetic instance
692      * @since 3.1
693      */
694     protected JexlArithmetic createWithOptions(final boolean astrict, final MathContext bigdContext, final int bigdScale) {
695         if (ctor != null) {
696             try {
697                 return ctor.newInstance(astrict, bigdContext, bigdScale);
698             } catch (IllegalAccessException | IllegalArgumentException
699                     | InstantiationException | InvocationTargetException xany) {
700                 // it was worth the try
701             }
702         }
703         return new JexlArithmetic(astrict, bigdContext, bigdScale);
704     }
705 
706     /**
707      * Decrements argument by 1.
708      * @param val the argument
709      * @return val - 1
710      */
711     public Object decrement(final Object val) {
712         return increment(val, -1);
713     }
714 
715     /**
716      * Divide the left value by the right.
717      *
718      * @param left  left argument
719      * @param right  right argument
720      * @return left / right
721      * @throws ArithmeticException if right == 0
722      */
723     public Object divide(final Object left, final Object right) {
724         if (left == null && right == null) {
725             return controlNullNullOperands(JexlOperator.DIVIDE);
726         }
727         final boolean strictCast = isStrict(JexlOperator.DIVIDE);
728         // if both (non-null) args fit as long
729         final Number ln = asLongNumber(strictCast, left);
730         final Number rn = asLongNumber(strictCast, right);
731         if (ln != null && rn != null) {
732             final long x = ln.longValue();
733             final long y = rn.longValue();
734             if (y == 0L) {
735                 throw new ArithmeticException("/");
736             }
737             final long result = x  / y;
738             return narrowLong(left, right, result);
739         }
740         // if either are BigDecimal, use that type
741         if (left instanceof BigDecimal || right instanceof BigDecimal) {
742             final BigDecimal l = toBigDecimal(strictCast, left);
743             final BigDecimal r = toBigDecimal(strictCast, right);
744             if (BigDecimal.ZERO.equals(r)) {
745                 throw new ArithmeticException("/");
746             }
747             return l.divide(r, getMathContext());
748         }
749         // if either are floating point (double or float), use double
750         if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
751             final double l = toDouble(strictCast, left);
752             final double r = toDouble(strictCast, right);
753             if (r == 0.0) {
754                 throw new ArithmeticException("/");
755             }
756             return l / r;
757         }
758         // otherwise treat as BigInteger
759         final BigInteger l = toBigInteger(strictCast, left);
760         final BigInteger r = toBigInteger(strictCast, right);
761         if (BigInteger.ZERO.equals(r)) {
762             throw new ArithmeticException("/");
763         }
764         final BigInteger result = l.divide(r);
765         return narrowBigInteger(left, right, result);
766     }
767 
768     private int doCompare(final Object left, final Object right, final JexlOperator operator) {
769         final boolean strictCast = isStrict(operator);
770         if (left != null && right != null) {
771             try {
772                 if (left instanceof BigDecimal || right instanceof BigDecimal) {
773                     final BigDecimal l = toBigDecimal(strictCast, left);
774                     final BigDecimal r = toBigDecimal(strictCast, right);
775                     return l.compareTo(r);
776                 }
777                 if (left instanceof BigInteger || right instanceof BigInteger) {
778                     final BigInteger l = toBigInteger(strictCast, left);
779                     final BigInteger r = toBigInteger(strictCast, right);
780                     return l.compareTo(r);
781                 }
782                 if (isFloatingPoint(left) || isFloatingPoint(right)) {
783                     final double lhs = toDouble(strictCast, left);
784                     final double rhs = toDouble(strictCast, right);
785                     if (Double.isNaN(lhs)) {
786                         if (Double.isNaN(rhs)) {
787                             return 0;
788                         }
789                         return -1;
790                     }
791                     if (Double.isNaN(rhs)) {
792                         // lhs is not NaN
793                         return +1;
794                     }
795                     return Double.compare(lhs, rhs);
796                 }
797                 if (isNumberable(left) || isNumberable(right)) {
798                     final long lhs = toLong(strictCast, left);
799                     final long rhs = toLong(strictCast, right);
800                     return Long.compare(lhs, rhs);
801                 }
802                 if (left instanceof String || right instanceof String) {
803                     return toString(left).compareTo(toString(right));
804                 }
805             } catch (final CoercionException ignore) {
806                 // ignore it, continue in sequence
807             }
808             if (EQ == operator) {
809                 return left.equals(right) ? 0 : -1;
810             }
811             if (left instanceof Comparable<?>) {
812                 @SuppressWarnings("unchecked") // OK because of instanceof check above
813                 final Comparable<Object> comparable = (Comparable<Object>) left;
814                 try {
815                     return comparable.compareTo(right);
816                 } catch (final ClassCastException castException) {
817                     // ignore it, continue in sequence
818                 }
819             }
820             if (right instanceof Comparable<?>) {
821                 @SuppressWarnings("unchecked") // OK because of instanceof check above
822                 final Comparable<Object> comparable = (Comparable<Object>) right;
823                 try {
824                     return -Integer.signum(comparable.compareTo(left));
825                 } catch (final ClassCastException castException) {
826                     // ignore it, continue in sequence
827                 }
828             }
829         }
830         throw new ArithmeticException("Object comparison:(" + left +
831                 " " + operator.getOperatorSymbol()
832                 + " " + right + ")");
833     }
834 
835     /**
836      * Check for emptiness of various types: Number, Collection, Array, Map, String.
837      * <p>Override or overload this method to add new signatures to the size operators.
838      * @param object the object to check the emptiness of
839      * @return the boolean or false if object is not null
840      * @since 3.2
841      */
842     public Boolean empty(final Object object) {
843         return object == null || isEmpty(object, false);
844     }
845 
846     /**
847      * Test if left ends with right.
848      *
849      * @param left  left argument
850      * @param right  right argument
851      * @return left $= right if there is no arithmetic solution
852      */
853     public Boolean endsWith(final Object left, final Object right) {
854         if (left == null && right == null) {
855             //if both are null L == R
856             return true;
857         }
858         if (left == null || right == null) {
859             // we know both aren't null, therefore L != R
860             return false;
861         }
862         if (left instanceof CharSequence) {
863             return toString(left).endsWith(toString(right));
864         }
865         return null;
866     }
867 
868     /**
869      * Test if left and right are equal.
870      *
871      * @param left  left argument
872      * @param right right argument
873      * @return the test result
874      */
875     public boolean equals(final Object left, final Object right) {
876         if (left == right) {
877             return true;
878         }
879         if (left == null || right == null) {
880             return false;
881         }
882         final boolean strictCast = isStrict(EQ);
883         if (left instanceof Boolean || right instanceof Boolean) {
884             return toBoolean(left) == toBoolean(strictCast, right);
885         }
886         return compare(left, right, EQ) == 0;
887     }
888 
889     /**
890      * The MathContext instance used for +,-,/,*,% operations on big decimals.
891      *
892      * @return the math context
893      */
894     public MathContext getMathContext() {
895         return mathContext;
896     }
897 
898     /**
899      * The BigDecimal scale used for comparison and coercion operations.
900      *
901      * @return the scale
902      */
903     public int getMathScale() {
904         return mathScale;
905     }
906 
907     /**
908      * Test if left &gt; right.
909      *
910      * @param left  left argument
911      * @param right right argument
912      * @return the test result
913      */
914     public boolean greaterThan(final Object left, final Object right) {
915         if (left == right || left == null || right == null) {
916             return false;
917         }
918         return compare(left, right, JexlOperator.GT) > 0;
919     }
920 
921     /**
922      * Test if left &gt;= right.
923      *
924      * @param left  left argument
925      * @param right right argument
926      * @return the test result
927      */
928     public boolean greaterThanOrEqual(final Object left, final Object right) {
929         if (left == right) {
930             return true;
931         }
932         if (left == null || right == null) {
933             return false;
934         }
935         return compare(left, right, JexlOperator.GTE) >= 0;
936     }
937 
938     /**
939      * Increments argument by 1.
940      * @param val the argument
941      * @return val + 1
942      */
943     public Object increment(final Object val) {
944         return increment(val, 1);
945     }
946 
947     /**
948      * Add value to number argument.
949      * @param val the number
950      * @param incr the value to add
951      * @return val + incr
952      */
953     protected Object increment(final Object val, final int incr) {
954         if (val == null) {
955             return incr;
956         }
957         if (val instanceof Integer) {
958             return (Integer) val + incr;
959         }
960         if (val instanceof Double) {
961             return (Double) val + incr;
962         }
963         if (val instanceof Long) {
964             return (Long) val + incr;
965         }
966         if (val instanceof BigDecimal) {
967             final BigDecimal bd = (BigDecimal) val;
968             return bd.add(BigDecimal.valueOf(incr), this.mathContext);
969         }
970         if (val instanceof BigInteger) {
971             final BigInteger bi = (BigInteger) val;
972             return bi.add(BigInteger.valueOf(incr));
973         }
974         if (val instanceof Float) {
975             return (Float) val + incr;
976         }
977         if (val instanceof Short) {
978             return (short) ((Short) val + incr);
979         }
980         if (val instanceof Byte) {
981             return (byte) ((Byte) val + incr);
982         }
983         throw new ArithmeticException("Object "+(incr < 0? "decrement":"increment")+":(" + val + ")");
984     }
985 
986     /**
987      * Check for emptiness of various types: Number, Collection, Array, Map, String.
988      *
989      * @param object the object to check the emptiness of
990      * @return the boolean or null if there is no arithmetic solution
991      */
992     public Boolean isEmpty(final Object object) {
993         return isEmpty(object, object == null);
994     }
995 
996     /**
997      * Check for emptiness of various types: Number, Collection, Array, Map, String.
998      *
999      * @param object the object to check the emptiness of
1000      * @param def the default value if object emptiness cannot be determined
1001      * @return the boolean or null if there is no arithmetic solution
1002      */
1003     public Boolean isEmpty(final Object object, final Boolean def) {
1004         if (object != null) {
1005             if (object instanceof Number) {
1006                 final double d = ((Number) object).doubleValue();
1007                 return Double.isNaN(d) || d == 0.d;
1008             }
1009             if (object instanceof CharSequence) {
1010                 return ((CharSequence) object).length() == 0;
1011             }
1012             if (object.getClass().isArray()) {
1013                 return Array.getLength(object) == 0;
1014             }
1015             if (object instanceof Collection<?>) {
1016                 return ((Collection<?>) object).isEmpty();
1017             }
1018             // Map isn't a collection
1019             if (object instanceof Map<?, ?>) {
1020                 return ((Map<?, ?>) object).isEmpty();
1021             }
1022         }
1023         return def;
1024     }
1025 
1026     /**
1027      * Is Object a floating point number.
1028      *
1029      * @param o Object to be analyzed.
1030      * @return true if it is a Float or a Double.
1031      */
1032     protected boolean isFloatingPoint(final Object o) {
1033         return o instanceof Float || o instanceof Double;
1034     }
1035 
1036     /**
1037      * Test if the passed value is a floating point number, i.e. a float, double
1038      * or string with ( "." | "E" | "e").
1039      *
1040      * @param val the object to be tested
1041      * @return true if it is, false otherwise.
1042      */
1043     protected boolean isFloatingPointNumber(final Object val) {
1044         if (val instanceof Float || val instanceof Double) {
1045             return true;
1046         }
1047         if (val instanceof CharSequence) {
1048             final Matcher m = FLOAT_PATTERN.matcher((CharSequence) val);
1049             // first matcher group is decimal, second is exponent
1050             // one of them must exist hence start({1,2}) >= 0
1051             return m.matches() && (m.start(1) >= 0 || m.start(2) >= 0);
1052         }
1053         return false;
1054     }
1055 
1056     /**
1057      * Tests whether negate called with a given argument will always return the same result.
1058      * <p>This is used to determine whether negate results on number literals can be cached.
1059      * If the result on calling negate with the same constant argument may change between calls,
1060      * which means the function is not deterministic, this method must return false.
1061      * @return true if negate is idempotent, false otherwise
1062      */
1063     public boolean isNegateStable() {
1064         return true;
1065     }
1066 
1067     /**
1068      * Checks if an operand is considered null.
1069      * @param value the operand
1070      * @return true if operand is considered null
1071      */
1072     protected boolean isNullOperand(final Object value) {
1073         return value == null;
1074     }
1075 
1076     /**
1077      * Is Object a whole number.
1078      *
1079      * @param o Object to be analyzed.
1080      * @return true if Integer, Long, Byte, Short or Character.
1081      */
1082     protected boolean isNumberable(final Object o) {
1083         return o instanceof Integer
1084                 || o instanceof Long
1085                 || o instanceof Byte
1086                 || o instanceof Short
1087                 || o instanceof Character;
1088     }
1089 
1090     /**
1091      * Tests whether positivize called with a given argument will always return the same result.
1092      * <p>This is used to determine whether positivize results on number literals can be cached.
1093      * If the result on calling positivize with the same constant argument may change between calls,
1094      * which means the function is not deterministic, this method must return false.
1095      * @return true if positivize is idempotent, false otherwise
1096      */
1097     public boolean isPositivizeStable() {
1098         return true;
1099     }
1100 
1101     /**
1102      * Checks whether this JexlArithmetic instance
1103      * strictly considers null as an error when used as operand unexpectedly.
1104      *
1105      * @return true if strict, false if lenient
1106      */
1107     public boolean isStrict() {
1108         return strict;
1109     }
1110 
1111     /**
1112      * Tests whether this arithmetic considers a given operator as strict or null-safe.
1113      * <p>When an operator is strict, it does <em>not</em> accept null arguments when the arithmetic is strict.
1114      * If null-safe (ie not-strict), the operator does accept null arguments even if the arithmetic itself is strict.</p>
1115      * <p>The default implementation considers equal/not-equal operators as null-safe so one can check for null as in
1116      * <code>if (myvar == null) {...}</code>. Note that this operator is used for equal and not-equal syntax. The complete
1117      * list of operators that are not strict are (==, [], []=, ., .=, empty, size, contains).</p>
1118      * <p>An arithmetic refining its strict behavior handling for more operators must declare which by overriding
1119      * this method.</p>
1120      * @param operator the operator to check for null-argument(s) handling
1121      * @return true if operator considers null arguments as errors, false if operator has appropriate semantics
1122      * for null argument(s)
1123      */
1124     public boolean isStrict(final JexlOperator operator) {
1125         if (operator != null) {
1126             switch (operator) {
1127                 case EQ:
1128                 case EQSTRICT:
1129                 case ARRAY_GET:
1130                 case ARRAY_SET:
1131                 case PROPERTY_GET:
1132                 case PROPERTY_SET:
1133                 case EMPTY:
1134                 case SIZE:
1135                 case CONTAINS:
1136                     return false;
1137                 default:
1138                     return isStrict();
1139             }
1140         }
1141         return isStrict();
1142     }
1143 
1144     /**
1145      * Tests if left &lt; right.
1146      *
1147      * @param left  left argument
1148      * @param right right argument
1149      * @return the test result
1150      */
1151     public boolean lessThan(final Object left, final Object right) {
1152         if (left == right || left == null || right == null) {
1153             return false;
1154         }
1155         return compare(left, right, JexlOperator.LT) < 0;
1156 
1157     }
1158 
1159     /**
1160      * Tests if left &lt;= right.
1161      *
1162      * @param left  left argument
1163      * @param right right argument
1164      * @return the test result
1165      */
1166     public boolean lessThanOrEqual(final Object left, final Object right) {
1167         if (left == right) {
1168             return true;
1169         }
1170         if (left == null || right == null) {
1171             return false;
1172         }
1173         return compare(left, right, JexlOperator.LTE) <= 0;
1174     }
1175 
1176     /**
1177      * Use or overload not() instead.
1178      *
1179      * @param arg argument
1180      * @return !arg
1181      * @see JexlArithmetic#not
1182      * @deprecated 3.0
1183      */
1184     @Deprecated
1185     public final Object logicalNot(final Object arg) {
1186         return not(arg);
1187     }
1188 
1189     /**
1190      * Creates a map-builder.
1191      * @param size the number of elements in the map
1192      * @return a map-builder instance
1193      * @deprecated 3.3
1194      */
1195     @Deprecated
1196     public MapBuilder mapBuilder(final int size) {
1197         return mapBuilder(size, false);
1198     }
1199 
1200     /**
1201      * Called by the interpreter when evaluating a literal map.
1202      *
1203      * @param size the number of elements in the map
1204      * @param extended whether the map is extended or not
1205      * @return the map builder
1206      */
1207     public MapBuilder mapBuilder(final int size, final boolean extended) {
1208         return new org.apache.commons.jexl3.internal.MapBuilder(size, extended);
1209     }
1210 
1211     /**
1212      * Use or overload contains() instead.
1213      *
1214      * @param lhs left hand side
1215      * @param rhs right hand side
1216      * @return contains(rhs, lhs)
1217      * @see JexlArithmetic#contains
1218      * @deprecated 3.0
1219      */
1220     @Deprecated
1221     public final Object matches(final Object lhs, final Object rhs) {
1222         return contains(rhs, lhs);
1223     }
1224 
1225     /**
1226      * left value modulo right.
1227      *
1228      * @param left  left argument
1229      * @param right  right argument
1230      * @return left % right
1231      * @throws ArithmeticException if right == 0.0
1232      */
1233     public Object mod(final Object left, final Object right) {
1234         if (left == null && right == null) {
1235             return controlNullNullOperands(JexlOperator.MOD);
1236         }
1237         final boolean strictCast = isStrict(JexlOperator.MOD);
1238         // if both (non-null) args fit as long
1239         final Number ln = asLongNumber(strictCast, left);
1240         final Number rn = asLongNumber(strictCast, right);
1241         if (ln != null && rn != null) {
1242             final long x = ln.longValue();
1243             final long y = rn.longValue();
1244             if (y == 0L) {
1245                 throw new ArithmeticException("%");
1246             }
1247             final long result = x % y;
1248             return narrowLong(left, right,  result);
1249         }
1250         // if either are BigDecimal, use that type
1251         if (left instanceof BigDecimal || right instanceof BigDecimal) {
1252             final BigDecimal l = toBigDecimal(strictCast, left);
1253             final BigDecimal r = toBigDecimal(strictCast, right);
1254             if (BigDecimal.ZERO.equals(r)) {
1255                 throw new ArithmeticException("%");
1256             }
1257             return l.remainder(r, getMathContext());
1258         }
1259         // if either are floating point (double or float), use double
1260         if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
1261             final double l = toDouble(strictCast, left);
1262             final double r = toDouble(strictCast, right);
1263             if (r == 0.0) {
1264                 throw new ArithmeticException("%");
1265             }
1266             return l % r;
1267         }
1268         // otherwise treat as BigInteger
1269         final BigInteger l = toBigInteger(strictCast, left);
1270         final BigInteger r = toBigInteger(strictCast, right);
1271         if (BigInteger.ZERO.equals(r)) {
1272             throw new ArithmeticException("%");
1273         }
1274         final BigInteger result = l.mod(r);
1275         return narrowBigInteger(left, right, result);
1276     }
1277 
1278     /**
1279      * Multiply the left value by the right.
1280      *
1281      * @param left  left argument
1282      * @param right  right argument
1283      * @return left * right.
1284      */
1285     public Object multiply(final Object left, final Object right) {
1286         if (left == null && right == null) {
1287             return controlNullNullOperands(JexlOperator.MULTIPLY);
1288         }
1289         final boolean strictCast = isStrict(JexlOperator.MULTIPLY);
1290         // if both (non-null) args fit as long
1291         final Number ln = asLongNumber(strictCast, left);
1292         final Number rn = asLongNumber(strictCast, right);
1293         if (ln != null && rn != null) {
1294             final long x = ln.longValue();
1295             final long y = rn.longValue();
1296             final long result = x * y;
1297             // detect overflow
1298             if (!isMultiplyExact(x, y, result)) {
1299                 return BigInteger.valueOf(x).multiply(BigInteger.valueOf(y));
1300             }
1301             return narrowLong(left, right, result);
1302         }
1303         // if either are BigDecimal, use that type
1304         if (left instanceof BigDecimal || right instanceof BigDecimal) {
1305             final BigDecimal l = toBigDecimal(strictCast, left);
1306             final BigDecimal r = toBigDecimal(strictCast, right);
1307             return l.multiply(r, getMathContext());
1308         }
1309         // if either are floating point (double or float), use double
1310         if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
1311             final double l = toDouble(strictCast, left);
1312             final double r = toDouble(strictCast, right);
1313             return l * r;
1314         }
1315         // otherwise treat as BigInteger
1316         final BigInteger l = toBigInteger(strictCast, left);
1317         final BigInteger r = toBigInteger(strictCast, right);
1318         final BigInteger result = l.multiply(r);
1319         return narrowBigInteger(left, right, result);
1320     }
1321 
1322     /**
1323      * Narrows a double to a float if there is no information loss.
1324      * @param value the double value
1325      * @param narrow the target narrow class
1326      * @return the narrowed or initial number
1327      */
1328     private Number narrow(final Class<?> narrow, final double value) {
1329         return narrowAccept(narrow, Float.class) && (float) value == value
1330             ? (float) value
1331             : value;
1332     }
1333 
1334     /**
1335      * Given a Number, return the value using the smallest type the result
1336      * will fit into.
1337      * <p>This works hand in hand with parameter 'widening' in Java
1338      * method calls, e.g. a call to substring(int,int) with an int and a long
1339      * will fail, but a call to substring(int,int) with an int and a short will
1340      * succeed.</p>
1341      *
1342      * @param original the original number.
1343      * @return a value of the smallest type the original number will fit into.
1344      */
1345     public Number narrow(final Number original) {
1346         return narrowNumber(original, null);
1347     }
1348 
1349     /**
1350      * Tests whether we consider the narrow class as a potential candidate for narrowing the source.
1351      *
1352      * @param narrow the target narrow class
1353      * @param source the original source class
1354      * @return true if attempt to narrow source to target is accepted
1355      */
1356     protected boolean narrowAccept(final Class<?> narrow, final Class<?> source) {
1357         return narrow == null || narrow.equals(source);
1358     }
1359 
1360     /**
1361      * Replace all numbers in an arguments array with the smallest type that will fit.
1362      *
1363      * @param args the argument array
1364      * @return true if some arguments were narrowed and args array is modified,
1365      *         false if no narrowing occurred and args array has not been modified
1366      */
1367     public boolean narrowArguments(final Object[] args) {
1368         boolean narrowed = false;
1369         if (args != null) {
1370             for (int a = 0; a < args.length; ++a) {
1371                 final Object arg = args[a];
1372                 if (arg instanceof Number) {
1373                     final Number narg = (Number) arg;
1374                     final Number narrow = narrow(narg);
1375                     if (!narg.equals(narrow)) {
1376                         args[a] = narrow;
1377                         narrowed = true;
1378                     }
1379                 }
1380             }
1381         }
1382         return narrowed;
1383     }
1384 
1385     /**
1386      * Given a BigDecimal, attempt to narrow it to an Integer or Long if it fits and
1387      * one of the arguments is numberable.
1388      *
1389      * @param lhs  the left-hand side operand that lead to the bigd result
1390      * @param rhs  the right-hand side operand that lead to the bigd result
1391      * @param big the BigDecimal to narrow
1392      * @return an Integer or Long if narrowing is possible, the original BigDecimal otherwise
1393      */
1394     protected Number narrowBigDecimal(final Object lhs, final Object rhs, final BigDecimal big) {
1395         if (isNumberable(lhs) || isNumberable(rhs)) {
1396             try {
1397                 final long l = big.longValueExact();
1398                 // coerce to int when possible (int being so often used in method parms)
1399                 if ((int) l == l) {
1400                     return (int) l;
1401                 }
1402                 return l;
1403             } catch (final ArithmeticException xa) {
1404                 // ignore, no exact value possible
1405             }
1406         }
1407         return big;
1408     }
1409 
1410     /**
1411      * Given a BigInteger, narrow it to an Integer or Long if it fits and the arguments
1412      * class allow it.
1413      * <p>
1414      * The rules are:
1415      * if either arguments is a BigInteger, no narrowing will occur
1416      * if either arguments is a Long, no narrowing to Integer will occur
1417      * </p>
1418      *
1419      * @param lhs  the left-hand side operand that lead to the bigi result
1420      * @param rhs  the right-hand side operand that lead to the bigi result
1421      * @param big the BigInteger to narrow
1422      * @return an Integer or Long if narrowing is possible, the original BigInteger otherwise
1423      */
1424     protected Number narrowBigInteger(final Object lhs, final Object rhs, final BigInteger big) {
1425         if (isNumberable(lhs) || isNumberable(rhs)) {
1426             try {
1427                 final long l = big.longValueExact();
1428                 // coerce to int when possible (int being so often used in method parms)
1429                 if ((int) l == l) {
1430                     return (int) l;
1431                 }
1432                 return l;
1433             } catch (final ArithmeticException xa) {
1434                 // ignore, no exact value possible
1435             }
1436         }
1437         return big;
1438     }
1439 
1440     /**
1441      * Given a long, attempt to narrow it to an int.
1442      * <p>Narrowing will only occur if no operand is a Long.
1443      * @param lhs  the left hand side operand that lead to the long result
1444      * @param rhs  the right hand side operand that lead to the long result
1445      * @param r the long to narrow
1446      * @return an Integer if narrowing is possible, the original Long otherwise
1447      */
1448     protected Number narrowLong(final Object lhs, final Object rhs, final long r) {
1449         if (!(lhs instanceof Long || rhs instanceof Long) && (int) r == r) {
1450             return (int) r;
1451         }
1452         return r;
1453     }
1454 
1455     /**
1456      * Given a Number, return the value attempting to narrow it to a target class.
1457      *
1458      * @param original the original number
1459      * @param narrow   the attempted target class
1460      * @return the narrowed number or the source if no narrowing was possible
1461      */
1462     public Number narrowNumber(final Number original, final Class<?> narrow) {
1463         if (original != null) {
1464             final long value;
1465             if (original instanceof BigDecimal) {
1466                 final BigDecimal big = (BigDecimal) original;
1467                 try {
1468                     // can it be represented as a long?
1469                     value = big.longValueExact();
1470                     // continue in sequence to try and further reduce
1471                 } catch (final ArithmeticException xa) {
1472                     // if it is bigger than a double, it cannot be narrowed
1473                     if (big.compareTo(BIGD_DOUBLE_MAX_VALUE) > 0
1474                         || big.compareTo(BIGD_DOUBLE_MIN_VALUE) < 0) {
1475                         return original;
1476                     }
1477                     // represent as double
1478                     return narrow(narrow, original.doubleValue());
1479                 }
1480                 // this continues with value as long
1481             } else {
1482                 if (isFloatingPoint(original)) {
1483                     final double doubleValue = original.doubleValue();
1484                     // if it is not equivalent to a Long...
1485                     if ((long) doubleValue != doubleValue) {
1486                         return narrow(narrow, doubleValue);
1487                     }
1488                     // else it can be represented as a long
1489                 } else if (original instanceof BigInteger) {
1490                     final BigInteger bigi = (BigInteger) original;
1491                     // if it is bigger than a Long, it cannot be narrowed
1492                     if (!BigInteger.valueOf(bigi.longValue()).equals(bigi)) {
1493                         return original;
1494                     }
1495                     // else it can be represented as a long
1496                 }
1497                 value = original.longValue();
1498             }
1499             // it can be represented as a long; determine the smallest possible numberable representation
1500             if (narrowAccept(narrow, Byte.class) && (byte) value == value) {
1501                 // it will fit in a byte
1502                 return (byte) value;
1503             }
1504             if (narrowAccept(narrow, Short.class) && (short) value == value) {
1505                 return (short) value;
1506             }
1507             if (narrowAccept(narrow, Integer.class) && (int) value == value) {
1508                 return (int) value;
1509             }
1510         }
1511         return original;
1512     }
1513 
1514     /**
1515      * Negates a value (unary minus for numbers).
1516      *
1517      * @see #isNegateStable()
1518      * @param val the value to negate
1519      * @return the negated value
1520      */
1521     public Object negate(final Object val) {
1522         if (val == null) {
1523             return null;
1524         }
1525         if (val instanceof Integer) {
1526             return -((Integer) val);
1527         }
1528         if (val instanceof Double) {
1529             return - ((Double) val);
1530         }
1531         if (val instanceof Long) {
1532             return -((Long) val);
1533         }
1534         if (val instanceof BigDecimal) {
1535             return ((BigDecimal) val).negate();
1536         }
1537         if (val instanceof BigInteger) {
1538             return ((BigInteger) val).negate();
1539         }
1540         if (val instanceof Float) {
1541             return -((Float) val);
1542         }
1543         if (val instanceof Short) {
1544             return (short) -((Short) val);
1545         }
1546         if (val instanceof Byte) {
1547             return (byte) -((Byte) val);
1548         }
1549         if (val instanceof Boolean) {
1550             return !(Boolean) val;
1551         }
1552         if (val instanceof AtomicBoolean) {
1553             return !((AtomicBoolean) val).get();
1554         }
1555         throw new ArithmeticException("Object negate:(" + val + ")");
1556     }
1557 
1558     /**
1559      * Performs a logical not.
1560      *
1561      * @param val the operand
1562      * @return !val
1563      */
1564     public Object not(final Object val) {
1565         final boolean strictCast = isStrict(JexlOperator.NOT);
1566         return !toBoolean(strictCast, val);
1567     }
1568 
1569     /**
1570      * Apply options to this arithmetic which eventually may create another instance.
1571      * @see #createWithOptions(boolean, java.math.MathContext, int)
1572      * @param context the context that may extend {@link JexlContext.OptionsHandle} to use
1573      * @return a new arithmetic instance or this
1574      * @since 3.1
1575      */
1576     public JexlArithmetic options(final JexlContext context) {
1577         if (context instanceof JexlContext.OptionsHandle) {
1578             return options(((JexlContext.OptionsHandle) context).getEngineOptions());
1579         }
1580         if (context instanceof JexlEngine.Options) {
1581             return options((JexlEngine.Options) context);
1582         }
1583         return this;
1584     }
1585 
1586     /**
1587      * Apply options to this arithmetic which eventually may create another instance.
1588      * @see #createWithOptions(boolean, java.math.MathContext, int)
1589      * @param options the {@link JexlEngine.Options} to use
1590      * @return an arithmetic with those options set
1591      * @deprecated 3.2
1592      */
1593     @Deprecated
1594     public JexlArithmetic options(final JexlEngine.Options options) {
1595         if (options != null) {
1596             final boolean isstrict = Boolean.TRUE == options.isStrictArithmetic() || isStrict();
1597             MathContext bigdContext = options.getArithmeticMathContext();
1598             if (bigdContext == null) {
1599                 bigdContext = getMathContext();
1600             }
1601             int bigdScale = options.getArithmeticMathScale();
1602             if (bigdScale == Integer.MIN_VALUE) {
1603                 bigdScale = getMathScale();
1604             }
1605             if (isstrict != isStrict()
1606                 || bigdScale != getMathScale()
1607                 || bigdContext != getMathContext()) {
1608                 return createWithOptions(isstrict, bigdContext, bigdScale);
1609             }
1610         }
1611         return this;
1612     }
1613 
1614     /**
1615      * Apply options to this arithmetic which eventually may create another instance.
1616      * @see #createWithOptions(boolean, java.math.MathContext, int)
1617      * @param options the {@link JexlEngine.Options} to use
1618      * @return an arithmetic with those options set
1619      */
1620     public JexlArithmetic options(final JexlOptions options) {
1621         if (options != null) {
1622             final boolean ostrict = options.isStrictArithmetic();
1623             MathContext bigdContext = options.getMathContext();
1624             if (bigdContext == null) {
1625                 bigdContext = getMathContext();
1626             }
1627             int bigdScale = options.getMathScale();
1628             if (bigdScale == Integer.MIN_VALUE) {
1629                 bigdScale = getMathScale();
1630             }
1631             if (ostrict != isStrict()
1632                 || bigdScale != getMathScale()
1633                 || bigdContext != getMathContext()) {
1634                 return createWithOptions(ostrict, bigdContext, bigdScale);
1635             }
1636         }
1637         return this;
1638     }
1639 
1640     /**
1641      * Performs a bitwise or.
1642      *
1643      * @param left  the left operand
1644      * @param right the right operator
1645      * @return left | right
1646      */
1647     public Object or(final Object left, final Object right) {
1648         final long l = toLong(left);
1649         final long r = toLong(right);
1650         return l | r;
1651     }
1652 
1653     /**
1654      * Convert a string to a BigDecimal.
1655      * <>Empty string is considered as 0.</>
1656      * @param arg the arg
1657      * @return a BigDecimal
1658      * @throws CoercionException if the string cannot be coerced into a BigDecimal
1659      */
1660     private BigDecimal parseBigDecimal(final String arg) throws ArithmeticException {
1661         try {
1662             return arg.isEmpty()? BigDecimal.ZERO : new BigDecimal(arg, getMathContext());
1663         } catch (final NumberFormatException e) {
1664             throw new CoercionException("BigDecimal coercion: ("+ arg +")", e);
1665         }
1666     }
1667 
1668     /**
1669      * Converts a string to a big integer.
1670      * <>Empty string is considered as 0.</>
1671      * @param arg the arg
1672      * @return a big integer
1673      * @throws ArithmeticException if the string cannot be coerced into a big integer
1674      */
1675     private BigInteger parseBigInteger(final String arg) throws ArithmeticException {
1676         try {
1677             return arg.isEmpty()? BigInteger.ZERO : new BigInteger(arg);
1678         } catch (final NumberFormatException e) {
1679             throw new CoercionException("BigDecimal coercion: ("+ arg +")", e);
1680         }
1681     }
1682 
1683     /**
1684      * Convert a string to a double.
1685      * <>Empty string is considered as NaN.</>
1686      * @param arg the arg
1687      * @return a double
1688      * @throws ArithmeticException if the string cannot be coerced into a double
1689      */
1690     private double parseDouble(final String arg) throws ArithmeticException {
1691         try {
1692             return arg.isEmpty()? Double.NaN : Double.parseDouble(arg);
1693         } catch (final NumberFormatException e) {
1694             throw new CoercionException("Double coercion: ("+ arg +")", e);
1695         }
1696     }
1697 
1698     /**
1699      * Converts a string to an int.
1700      * <p>This ensure the represented number is a natural (not a real).</p>
1701      * @param arg the arg
1702      * @return an int
1703      * @throws ArithmeticException if the string cannot be coerced into a long
1704      */
1705     private int parseInteger(final String arg) throws ArithmeticException {
1706         final long l = parseLong(arg);
1707         final int i = (int) l;
1708         if (i == l) {
1709             return i;
1710         }
1711         throw new CoercionException("Int coercion: ("+ arg +")");
1712     }
1713 
1714     /**
1715      * Converts a string to a long.
1716      * <p>This ensure the represented number is a natural (not a real).</p>
1717      * @param arg the arg
1718      * @return a long
1719      * @throws ArithmeticException if the string cannot be coerced into a long
1720      */
1721     private long parseLong(final String arg) throws ArithmeticException {
1722         final double d = parseDouble(arg);
1723         if (Double.isNaN(d)) {
1724             return 0L;
1725         }
1726         final double f = floor(d);
1727         if (d == f) {
1728             return (long) d;
1729         }
1730         throw new CoercionException("Long coercion: ("+ arg +")");
1731     }
1732 
1733     /**
1734      * Parse an identifier which must be of the form:
1735      * 0|([1-9][0-9]*)
1736      * @param id the identifier
1737      * @return an integer or null
1738      */
1739     public static Integer parseIdentifier(final Object id) {
1740         if (id instanceof Number) {
1741             return ((Number) id).intValue();
1742         }
1743         // hand coded because the was no way to fail on leading '0's using NumberFormat
1744         if (id instanceof CharSequence) {
1745             final CharSequence str = (CharSequence) id;
1746             final int length = str.length();
1747             // cannot be empty string and cannot be longer than Integer.MAX_VALUE representation
1748             if (length > 0 && length <= 10) {
1749                 int val = 0;
1750                 for (int i = 0; i < length; ++i) {
1751                     final char c = str.charAt(i);
1752                     // leading 0s but no just 0, numeric only
1753                     if (c == '0' && val == 0 && length > 1 || c < '0' || c > '9') {
1754                         return null;
1755                     }
1756                     val *= 10;
1757                     val += c - '0';
1758                 }
1759                 return val;
1760             }
1761         }
1762         return null;
1763     }
1764 
1765     /**
1766      * Positivize value (unary plus for numbers).
1767      * <p>C/C++/C#/Java perform integral promotion of the operand, ie
1768      * cast to int if type can be represented as int without loss of precision.
1769      * @see #isPositivizeStable()
1770      * @param val the value to positivize
1771      * @return the positive value
1772      */
1773     public Object positivize(final Object val) {
1774         if (val == null) {
1775             return null;
1776         }
1777         if (val instanceof Short) {
1778             return ((Short) val).intValue();
1779         }
1780         if (val instanceof Byte) {
1781             return ((Byte) val).intValue();
1782         }
1783         if (val instanceof Number) {
1784             return val;
1785         }
1786         if (val instanceof Character) {
1787             return (int) (Character) val;
1788         }
1789         if (val instanceof Boolean) {
1790             return val;
1791         }
1792         if (val instanceof AtomicBoolean) {
1793             return ((AtomicBoolean) val).get();
1794         }
1795         throw new ArithmeticException("Object positivize:(" + val + ")");
1796     }
1797 
1798     /**
1799      * Ensure a big decimal is rounded by this arithmetic scale and rounding mode.
1800      *
1801      * @param number the big decimal to round
1802      * @return the rounded big decimal
1803      */
1804     protected BigDecimal roundBigDecimal(final BigDecimal number) {
1805         final int mscale = getMathScale();
1806         if (mscale >= 0) {
1807             return number.setScale(mscale, getMathContext().getRoundingMode());
1808         }
1809         return number;
1810     }
1811 
1812     /**
1813      * Creates a set-builder.
1814      * @param size the number of elements in the set
1815      * @return a set-builder instance
1816      * @deprecated since 3.3.1
1817      */
1818     @Deprecated
1819     public SetBuilder setBuilder(final int size) {
1820         return setBuilder(size, false);
1821     }
1822 
1823     /**
1824      * Called by the interpreter when evaluating a literal set.
1825      *
1826      * @param size the number of elements in the set
1827      * @param extended whether the set is extended or not
1828      * @return the array builder
1829      */
1830     public SetBuilder setBuilder(final int size, final boolean extended) {
1831         return new org.apache.commons.jexl3.internal.SetBuilder(size, extended);
1832     }
1833 
1834     /**
1835      * Shifts a bit pattern to the right.
1836      *
1837      * @param left  left argument
1838      * @param right  right argument
1839      * @return left &lt;&lt; right.
1840      */
1841     public Object shiftLeft(final Object left, final Object right) {
1842         final long l = toLong(left);
1843         final int r = toInteger(right);
1844         return l << r;
1845     }
1846 
1847     /**
1848      * Shifts a bit pattern to the right.
1849      *
1850      * @param left  left argument
1851      * @param right  right argument
1852      * @return left &gt;&gt; right.
1853      */
1854     public Object shiftRight(final Object left, final Object right) {
1855         final long l = toLong(left);
1856         final long r = toInteger(right);
1857         return l >> r;
1858     }
1859 
1860     /**
1861      * Shifts a bit pattern to the right unsigned.
1862      *
1863      * @param left  left argument
1864      * @param right  right argument
1865      * @return left &gt;&gt;&gt; right.
1866      */
1867     public Object shiftRightUnsigned(final Object left, final Object right) {
1868         final long l = toLong(left);
1869         final long r = toInteger(right);
1870         return l >>> r;
1871     }
1872 
1873     /**
1874      * Calculate the {@code size} of various types: Collection, Array, Map, String.
1875      *
1876      * @param object the object to get the size of
1877      * @return the <em>size</em> of object, 0 if null, 1 if there is no <em>better</em> solution
1878      */
1879     public Integer size(final Object object) {
1880         return size(object, object == null ? 0 : 1);
1881     }
1882 
1883     /**
1884      * Calculate the {@code size} of various types: Collection, Array, Map, String.
1885      *
1886      * @param object the object to get the size of
1887      * @param def the default value if object size cannot be determined
1888      * @return the size of object or null if there is no arithmetic solution
1889      */
1890     public Integer size(final Object object, final Integer def) {
1891         if (object instanceof CharSequence) {
1892             return ((CharSequence) object).length();
1893         }
1894         if (object.getClass().isArray()) {
1895             return Array.getLength(object);
1896         }
1897         if (object instanceof Collection<?>) {
1898             return ((Collection<?>) object).size();
1899         }
1900         if (object instanceof Map<?, ?>) {
1901             return ((Map<?, ?>) object).size();
1902         }
1903         return def;
1904     }
1905     /**
1906      * Test if left starts with right.
1907      *
1908      * @param left  left argument
1909      * @param right  right argument
1910      * @return left ^= right or null if there is no arithmetic solution
1911      */
1912     public Boolean startsWith(final Object left, final Object right) {
1913         if (left == null && right == null) {
1914             //if both are null L == R
1915             return true;
1916         }
1917         if (left == null || right == null) {
1918             // we know both aren't null, therefore L != R
1919             return false;
1920         }
1921         if (left instanceof CharSequence) {
1922             return toString(left).startsWith(toString(right));
1923         }
1924         return null;
1925     }
1926 
1927     /**
1928      * Test if left and right are strictly equal.
1929      * <p>They must have the same class, comparable and the comparison returns 0.</p>
1930      *
1931      * @param left  left argument
1932      * @param right right argument
1933      * @return the test result
1934      */
1935     public boolean strictEquals(final Object left, final Object right) {
1936         if (left == right) {
1937             return true;
1938         }
1939         if (left == null || right == null) {
1940             return false;
1941         }
1942         if (left.getClass().equals(right.getClass())) {
1943             return left.equals(right);
1944         }
1945         return false;
1946     }
1947 
1948     /**
1949      * Subtract the right value from the left.
1950      *
1951      * @param left  left argument
1952      * @param right  right argument
1953      * @return left - right.
1954      */
1955     public Object subtract(final Object left, final Object right) {
1956         if (left == null && right == null) {
1957             return controlNullNullOperands(JexlOperator.SUBTRACT);
1958         }
1959         final boolean strictCast = isStrict(JexlOperator.SUBTRACT);
1960         // if both (non-null) args fit as long
1961         final Number ln = asLongNumber(strictCast, left);
1962         final Number rn = asLongNumber(strictCast, right);
1963         if (ln != null && rn != null) {
1964             final long x = ln.longValue();
1965             final long y = rn.longValue();
1966             final long result = x - y;
1967             // detect overflow, see java8 Math.subtractExact
1968             if (((x ^ y) & (x ^ result)) < 0) {
1969                 return BigInteger.valueOf(x).subtract(BigInteger.valueOf(y));
1970             }
1971             return narrowLong(left, right, result);
1972         }
1973         // if either are BigDecimal, use that type
1974         if (left instanceof BigDecimal || right instanceof BigDecimal) {
1975             final BigDecimal l = toBigDecimal(strictCast, left);
1976             final BigDecimal r = toBigDecimal(strictCast, right);
1977             return l.subtract(r, getMathContext());
1978         }
1979         // if either are floating point (double or float), use double
1980         if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
1981             final double l = toDouble(strictCast, left);
1982             final double r = toDouble(strictCast, right);
1983             return l - r;
1984         }
1985         // otherwise treat as BigInteger
1986         final BigInteger l = toBigInteger(strictCast, left);
1987         final BigInteger r = toBigInteger(strictCast, right);
1988         final BigInteger result = l.subtract(r);
1989         return narrowBigInteger(left, right, result);
1990     }
1991 
1992     /**
1993      * Test if a condition is true or false.
1994      * @param object the object to use as condition
1995      * @return true or false
1996      * @since 3.3
1997      */
1998     public boolean testPredicate(final Object object) {
1999         final boolean strictCast = isStrict(JexlOperator.CONDITION);
2000         return toBoolean(strictCast, object);
2001     }
2002 
2003     /**
2004      * Coerce to a BigDecimal.
2005      * <p>Double.NaN, null and empty string coerce to zero.</p>
2006      * <p>Boolean false is 0, true is 1.</p>
2007      *
2008      * @param strict true if the calling operator or casting is strict, false otherwise
2009      * @param val the object to be coerced.
2010      * @return a BigDecimal.
2011      * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2012      * @since 3.3
2013      */
2014     protected BigDecimal toBigDecimal(final boolean strict, final Object val) {
2015         return isNullOperand(val)? controlNullOperand(strict, BigDecimal.ZERO) : toBigDecimal(val);
2016     }
2017 
2018     /**
2019      * Coerce to a BigDecimal.
2020      * <p>Double.NaN, null and empty string coerce to zero.</p>
2021      * <p>Boolean false is 0, true is 1.</p>
2022      *
2023      * @param val the object to be coerced.
2024      * @return a BigDecimal.
2025      * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2026      */
2027     public BigDecimal toBigDecimal(final Object val) {
2028         if (val instanceof BigDecimal) {
2029             return roundBigDecimal((BigDecimal) val);
2030         }
2031         if (val instanceof Double) {
2032             if (Double.isNaN((Double) val)) {
2033                 return BigDecimal.ZERO;
2034             }
2035             return roundBigDecimal(new BigDecimal(val.toString(), getMathContext()));
2036         }
2037         if (val instanceof Number) {
2038             return roundBigDecimal(parseBigDecimal(val.toString()));
2039         }
2040         if (val instanceof Boolean) {
2041             return BigDecimal.valueOf((boolean) val ? 1. : 0.);
2042         }
2043         if (val instanceof AtomicBoolean) {
2044             return BigDecimal.valueOf(((AtomicBoolean) val).get() ? 1L : 0L);
2045         }
2046         if (val instanceof String) {
2047             return roundBigDecimal(parseBigDecimal((String) val));
2048         }
2049         if (val instanceof Character) {
2050             return new BigDecimal((Character) val);
2051         }
2052         if (val == null) {
2053             return controlNullOperand(strict, BigDecimal.ZERO);
2054         }
2055         throw new CoercionException("BigDecimal coercion: "
2056                 + val.getClass().getName() + ":(" + val + ")");
2057     }
2058 
2059     /**
2060      * Coerce to a BigInteger.
2061      * <p>Double.NaN, null and empty string coerce to zero.</p>
2062      * <p>Boolean false is 0, true is 1.</p>
2063      *
2064      * @param strict true if the calling operator or casting is strict, false otherwise
2065      * @param val the object to be coerced.
2066      * @return a BigDecimal
2067      * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2068      * @since 3.3
2069      */
2070     protected BigInteger toBigInteger(final boolean strict, final Object val) {
2071         return isNullOperand(val)? controlNullOperand(strict, BigInteger.ZERO) : toBigInteger(val);
2072     }
2073 
2074     /**
2075      * Coerce to a BigInteger.
2076      * <p>Double.NaN, null and empty string coerce to zero.</p>
2077      * <p>Boolean false is 0, true is 1.</p>
2078      *
2079      * @param val the object to be coerced.
2080      * @return a BigDecimal
2081      * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2082      */
2083     public BigInteger toBigInteger(final Object val) {
2084         if (val instanceof BigInteger) {
2085             return (BigInteger) val;
2086         }
2087         if (val instanceof Double) {
2088             final Double dval = (Double) val;
2089             if (Double.isNaN(dval)) {
2090                 return BigInteger.ZERO;
2091             }
2092             return BigInteger.valueOf(dval.longValue());
2093         }
2094         if (val instanceof BigDecimal) {
2095             return ((BigDecimal) val).toBigInteger();
2096         }
2097         if (val instanceof Number) {
2098             return BigInteger.valueOf(((Number) val).longValue());
2099         }
2100         if (val instanceof Boolean) {
2101             return BigInteger.valueOf((boolean) val ? 1L : 0L);
2102         }
2103         if (val instanceof AtomicBoolean) {
2104             return BigInteger.valueOf(((AtomicBoolean) val).get() ? 1L : 0L);
2105         }
2106         if (val instanceof String) {
2107             return parseBigInteger((String) val);
2108         }
2109         if (val instanceof Character) {
2110             final int i = (Character) val;
2111             return BigInteger.valueOf(i);
2112         }
2113         if (val == null) {
2114             return controlNullOperand(strict, BigInteger.ZERO);
2115         }
2116         throw new CoercionException("BigInteger coercion: "
2117                 + val.getClass().getName() + ":(" + val + ")");
2118     }
2119 
2120     /**
2121      * Coerce to a primitive boolean.
2122      * <p>Double.NaN, null, "false" and empty string coerce to false.</p>
2123      *
2124      * @param val value to coerce
2125      * @param strict true if the calling operator or casting is strict, false otherwise
2126      * @return the boolean value if coercion is possible, true if value was not null.
2127      */
2128     protected boolean toBoolean(final boolean strict, final Object val) {
2129         return isNullOperand(val)? controlNullOperand(strict, false) : toBoolean(val);
2130     }
2131 
2132     /**
2133      * Coerce to a primitive boolean.
2134      * <p>Double.NaN, null, "false" and empty string coerce to false.</p>
2135      *
2136      * @param val value to coerce
2137      * @return the boolean value if coercion is possible, true if value was not null.
2138      */
2139     public boolean toBoolean(final Object val) {
2140         if (val instanceof Boolean) {
2141             return (Boolean) val;
2142         }
2143         if (val instanceof Number) {
2144             final double number = toDouble(strict, val);
2145             return !Double.isNaN(number) && number != 0.d;
2146         }
2147         if (val instanceof AtomicBoolean) {
2148             return ((AtomicBoolean) val).get();
2149         }
2150         if (val instanceof String) {
2151             final String strval = val.toString();
2152             return !strval.isEmpty() && !"false".equals(strval);
2153         }
2154         if (val == null) {
2155             return controlNullOperand(strict, false);
2156         }
2157         // non-null value is true
2158         return true;
2159     }
2160 
2161     /**
2162      * Coerce to a primitive double.
2163      * <p>Double.NaN, null and empty string coerce to zero.</p>
2164      * <p>Boolean false is 0, true is 1.</p>
2165      *
2166      * @param strict true if the calling operator or casting is strict, false otherwise
2167      * @param val value to coerce.
2168      * @return The double coerced value.
2169      * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2170      * @since 3.3
2171      */
2172     protected double toDouble(final boolean strict, final Object val) {
2173         return isNullOperand(val)? controlNullOperand(strict, 0.d) : toDouble(val);
2174     }
2175 
2176     /**
2177      * Coerce to a primitive double.
2178      * <p>Double.NaN, null and empty string coerce to zero.</p>
2179      * <p>Boolean false is 0, true is 1.</p>
2180      *
2181      * @param val value to coerce.
2182      * @return The double coerced value.
2183      * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2184      */
2185     public double toDouble(final Object val) {
2186         if (val instanceof Double) {
2187             return (Double) val;
2188         }
2189         if (val instanceof Number) {
2190             return ((Number) val).doubleValue();
2191         }
2192         if (val instanceof Boolean) {
2193             return (boolean) val ? 1. : 0.;
2194         }
2195         if (val instanceof AtomicBoolean) {
2196             return ((AtomicBoolean) val).get() ? 1. : 0.;
2197         }
2198         if (val instanceof String) {
2199             return parseDouble((String) val);
2200         }
2201         if (val instanceof Character) {
2202             return (Character) val;
2203         }
2204         if (val == null) {
2205             return controlNullOperand(strict, 0.d);
2206         }
2207         throw new CoercionException("Double coercion: "
2208                 + val.getClass().getName() + ":(" + val + ")");
2209     }
2210 
2211     /**
2212      * Coerce to a primitive int.
2213      * <p>Double.NaN, null and empty string coerce to zero.</p>
2214      * <p>Boolean false is 0, true is 1.</p>
2215      *
2216      * @param strict true if the calling operator or casting is strict, false otherwise
2217      * @param val value to coerce
2218      * @return the value coerced to int
2219      * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2220      * @since 3.3
2221      */
2222     protected int toInteger(final boolean strict, final Object val) {
2223         return isNullOperand(val)? controlNullOperand(strict, 0) : toInteger(val);
2224     }
2225 
2226     /**
2227      * Coerce to a primitive int.
2228      * <p>Double.NaN, null and empty string coerce to zero.</p>
2229      * <p>Boolean false is 0, true is 1.</p>
2230      *
2231      * @param val value to coerce
2232      * @return the value coerced to int
2233      * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2234      */
2235     public int toInteger(final Object val) {
2236         if (val instanceof Double) {
2237             final double dval = (Double) val;
2238             return Double.isNaN(dval)? 0 : (int) dval;
2239         }
2240         if (val instanceof Number) {
2241             return ((Number) val).intValue();
2242         }
2243         if (val instanceof String) {
2244             return parseInteger((String) val);
2245         }
2246         if (val instanceof Boolean) {
2247             return (boolean) val ? 1 : 0;
2248         }
2249         if (val instanceof AtomicBoolean) {
2250             return ((AtomicBoolean) val).get() ? 1 : 0;
2251         }
2252         if (val instanceof Character) {
2253             return (Character) val;
2254         }
2255         if (val == null) {
2256             return controlNullOperand(strict, 0);
2257         }
2258         throw new CoercionException("Integer coercion: "
2259                 + val.getClass().getName() + ":(" + val + ")");
2260     }
2261 
2262     /**
2263      * Coerce to a primitive long.
2264      * <p>Double.NaN, null and empty string coerce to zero.</p>
2265      * <p>Boolean false is 0, true is 1.</p>
2266      *
2267      * @param strict true if the calling operator or casting is strict, false otherwise
2268      * @param val value to coerce
2269      * @return the value coerced to long
2270      * @throws ArithmeticException if value is null and mode is strict or if coercion is not possible
2271      * @since 3.3
2272      */
2273     protected long toLong(final boolean strict, final Object val) {
2274         return isNullOperand(val)? controlNullOperand(strict, 0L) : toLong(val);
2275     }
2276 
2277     /**
2278      * Coerce to a primitive long.
2279      * <p>Double.NaN, null and empty string coerce to zero.</p>
2280      * <p>Boolean false is 0, true is 1.</p>
2281      *
2282      * @param val value to coerce
2283      * @return the value coerced to long
2284      * @throws ArithmeticException if value is null and mode is strict or if coercion is not possible
2285      */
2286     public long toLong(final Object val) {
2287         if (val instanceof Double) {
2288             final double dval = (Double) val;
2289             return Double.isNaN(dval)? 0L : (long) dval;
2290         }
2291         if (val instanceof Number) {
2292             return ((Number) val).longValue();
2293         }
2294         if (val instanceof String) {
2295             return parseLong((String) val);
2296         }
2297         if (val instanceof Boolean) {
2298             return (boolean) val ? 1L : 0L;
2299         }
2300         if (val instanceof AtomicBoolean) {
2301             return ((AtomicBoolean) val).get() ? 1L : 0L;
2302         }
2303         if (val instanceof Character) {
2304             return (Character) val;
2305         }
2306         if (val == null) {
2307             return controlNullOperand(strict, 0L);
2308         }
2309         throw new CoercionException("Long coercion: "
2310                 + val.getClass().getName() + ":(" + val + ")");
2311     }
2312 
2313     /**
2314      * Coerce to a string.
2315      * <p>Double.NaN coerce to the empty string.</p>
2316      *
2317      * @param strict true if the calling operator or casting is strict, false otherwise
2318      * @param val value to coerce.
2319      * @return The String coerced value.
2320      * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2321      * @since 3.3
2322      */
2323     protected String toString(final boolean strict, final Object val) {
2324         return isNullOperand(val)? controlNullOperand(strict, "") : toString(val);
2325     }
2326 
2327     /**
2328      * Coerce to a string.
2329      * <p>Double.NaN coerce to the empty string.</p>
2330      *
2331      * @param val value to coerce.
2332      * @return The String coerced value.
2333      * @throws ArithmeticException if val is null and mode is strict or if coercion is not possible
2334      */
2335     public String toString(final Object val) {
2336         if (val instanceof Double) {
2337             final Double dval = (Double) val;
2338             if (Double.isNaN(dval)) {
2339                 return "";
2340             }
2341             return dval.toString();
2342         }
2343         return val == null ? controlNullOperand(strict, "") : val.toString();
2344     }
2345 
2346     /**
2347      * Performs a bitwise xor.
2348      *
2349      * @param left  the left operand
2350      * @param right the right operator
2351      * @return left ^ right
2352      */
2353     public Object xor(final Object left, final Object right) {
2354         final long l = toLong(left);
2355         final long r = toLong(right);
2356         return l ^ r;
2357     }
2358 }