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