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