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