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