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