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