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