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