View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jexl3;
18  
19  import org.apache.commons.jexl3.junit.Asserter;
20  import org.junit.Assert;
21  import org.junit.Before;
22  import org.junit.Test;
23  import org.w3c.dom.Attr;
24  import org.w3c.dom.Document;
25  import org.w3c.dom.NamedNodeMap;
26  import org.w3c.dom.Node;
27  
28  import javax.xml.parsers.DocumentBuilder;
29  import javax.xml.parsers.DocumentBuilderFactory;
30  import java.io.ByteArrayInputStream;
31  import java.io.InputStream;
32  import java.math.BigDecimal;
33  import java.math.BigInteger;
34  import java.math.MathContext;
35  import java.nio.charset.StandardCharsets;
36  import java.util.Arrays;
37  import java.util.Date;
38  import java.util.HashMap;
39  import java.util.List;
40  import java.util.Map;
41  import java.util.concurrent.atomic.AtomicBoolean;
42  
43  @SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"})
44  public class ArithmeticTest extends JexlTestCase {
45      /** A small delta to compare doubles. */
46      private static final double EPSILON = 1.e-6;
47      private final Asserter asserter;
48      private final JexlArithmetic jexla;
49      private final JexlArithmetic jexlb;
50      private final Date date = new Date();
51  
52      public ArithmeticTest() {
53          super("ArithmeticTest");
54          asserter = new Asserter(JEXL);
55          jexla = JEXL.getArithmetic();
56          JexlOptions options = new JexlOptions();
57          options.setStrictArithmetic(false);
58          jexlb = jexla.options(options);
59      }
60  
61      @Before
62      @Override
63      public void setUp() {
64      }
65  
66      static private void assertNullOperand(java.util.function.Supplier<Object> fun) {
67          try {
68              Assert.assertNull(fun.get());
69          } catch(JexlArithmetic.NullOperand xany) {
70              Assert.assertNotNull(xany);
71          }
72      }
73  
74      static private void assertArithmeticException(java.util.function.Supplier<Object> fun) {
75          try {
76              Assert.assertNull(fun.get());
77          } catch(ArithmeticException xany) {
78              Assert.assertNotNull(xany);
79          }
80      }
81  
82      @Test
83      public void testModEdge() {
84          assertNullOperand(() -> jexla.mod(null, null));
85          Assert.assertEquals(0, jexlb.mod(null, null));
86          assertArithmeticException(() -> jexla.mod(1, 0));
87          assertArithmeticException(() -> jexla.mod(1L, 0L));
88          assertArithmeticException(() -> jexla.mod(1f, 0f));
89          assertArithmeticException(() -> jexla.mod(1d, 0d));
90          assertArithmeticException(() -> jexla.mod(BigInteger.ONE, BigInteger.ZERO));
91          assertArithmeticException(() -> jexla.mod(BigInteger.ONE, BigDecimal.ZERO));
92          assertNullOperand(()-> jexla.divide(null, null));
93      }
94  
95      @Test
96      public void testUnaryopsEdges() {
97          assertArithmeticException(()-> jexla.positivize(date) );
98          assertNullOperand(()-> jexla.positivize(null));
99          Assert.assertNull(jexlb.positivize(null));
100         Assert.assertEquals(42, jexla.positivize((char) 42));
101         Assert.assertEquals(Boolean.TRUE, jexla.positivize(Boolean.TRUE));
102         Assert.assertEquals(Boolean.FALSE, jexla.positivize(Boolean.FALSE));
103         Assert.assertEquals(Boolean.TRUE, jexla.positivize(new AtomicBoolean(true)));
104         Assert.assertEquals(Boolean.FALSE, jexla.positivize(new AtomicBoolean(false)));
105 
106         assertNullOperand(()-> jexla.negate(null));
107         Assert.assertNull(jexlb.negate(null));
108         assertArithmeticException(()-> jexla.negate(date));
109         Assert.assertEquals(Boolean.FALSE, jexla.negate(Boolean.TRUE));
110         Assert.assertEquals(Boolean.TRUE, jexla.negate(Boolean.FALSE));
111     }
112 
113     @Test
114     public void testDivideEdges() {
115         assertNullOperand(()-> jexla.divide(null, null));
116         Assert.assertEquals(0, jexlb.divide(null, null));
117         assertNullOperand(() -> jexla.divide(null, null));
118         Assert.assertEquals(0, jexlb.mod(null, null));
119         assertArithmeticException(() -> jexla.divide(1, 0));
120         assertArithmeticException(() -> jexla.divide(1L, 0L));
121         assertArithmeticException(() -> jexla.divide(1f, 0f));
122         assertArithmeticException(() -> jexla.divide(1d, 0d));
123         assertArithmeticException(() -> jexla.divide(BigInteger.ONE, BigInteger.ZERO));
124         assertArithmeticException(() -> jexla.divide(BigInteger.ONE, BigDecimal.ZERO));
125     }
126 
127 
128     @Test
129     public void testOperatorsEdges() {
130         assertNullOperand(()-> jexla.multiply(null, null));
131         Assert.assertEquals(0, jexlb.multiply(null, null));
132         assertNullOperand(()-> jexla.add(null, null));
133         Assert.assertEquals(0, jexlb.add(null, null));
134         assertNullOperand(()-> jexla.subtract(null, null));
135         Assert.assertEquals(0, jexlb.subtract(null, null));
136 
137         Assert.assertTrue(jexla.contains(null, null));
138         Assert.assertFalse(jexla.contains(true, null));
139         Assert.assertFalse(jexla.contains(null, true));
140         Assert.assertTrue(jexla.endsWith(null, null));
141         Assert.assertFalse(jexla.endsWith(true, null));
142         Assert.assertFalse(jexla.endsWith(null, true));
143         Assert.assertTrue(jexla.startsWith(null, null));
144         Assert.assertFalse(jexla.startsWith(true, null));
145         Assert.assertFalse(jexla.startsWith(null, true));
146         Assert.assertTrue(jexla.isEmpty(null));
147     }
148 
149     @Test
150     public void testIntegerCoercionEdges() {
151         assertNullOperand(()-> jexla.toBoolean(null));
152         Assert.assertTrue(jexla.toBoolean(date));
153         // int coercions
154         assertNullOperand(()-> jexla.toInteger(null));
155         Assert.assertEquals(0, jexlb.toInteger(null));
156         assertArithmeticException(()-> jexla.toInteger(date));
157         Assert.assertEquals(0, jexla.toInteger(Double.NaN));
158         Assert.assertEquals(0, jexla.toInteger(""));
159         Assert.assertEquals((int) 'b', jexla.toInteger('b'));
160         Assert.assertEquals(1, jexla.toInteger(new AtomicBoolean(true)));
161         Assert.assertEquals(0, jexla.toInteger(new AtomicBoolean(false)));
162 
163         // long coercions
164         assertNullOperand(()-> jexla.toLong(null));
165         Assert.assertEquals(0L, jexlb.toLong(null));
166         assertArithmeticException(()-> jexla.toLong(date));
167         Assert.assertEquals(0L, jexla.toLong(Double.NaN));
168         Assert.assertEquals(0L, jexla.toLong(""));
169         Assert.assertEquals('b', jexla.toLong('b'));
170         Assert.assertEquals(1L, jexla.toLong(new AtomicBoolean(true)));
171         Assert.assertEquals(0L, jexla.toLong(new AtomicBoolean(false)));
172     }
173 
174     @Test
175     public void testRealCoercionEdges() {
176         assertNullOperand(()-> jexla.toDouble(null));
177         Assert.assertEquals(0.0d, jexlb.toDouble(null), EPSILON);
178         Assert.assertEquals(32.0d, jexlb.toDouble((char) 32), EPSILON);
179         assertArithmeticException(()-> jexla.toDouble(date));
180         Assert.assertTrue(Double.isNaN(jexla.toDouble("")));
181         Assert.assertEquals("", jexla.toString(Double.NaN));
182 
183         assertNullOperand(()-> jexla.toBigInteger(null));
184         assertArithmeticException(()-> jexla.toBigInteger(date));
185         Assert.assertEquals(BigInteger.ZERO, jexla.toBigInteger(Double.NaN));
186         Assert.assertEquals(BigInteger.ZERO, jexla.toBigInteger(""));
187         Assert.assertEquals(BigInteger.ZERO, jexla.toBigInteger((char) 0));
188 
189         assertNullOperand(()-> jexla.toBigDecimal(null));
190         assertArithmeticException(()-> jexla.toBigDecimal(date));
191 
192         Assert.assertEquals(BigDecimal.ZERO, jexla.toBigDecimal(Double.NaN));
193         Assert.assertEquals(BigDecimal.ZERO, jexla.toBigDecimal(""));
194         Assert.assertEquals(BigDecimal.ZERO, jexla.toBigDecimal((char) 0));
195     }
196 
197     @Test
198     public void testUndefinedVar() throws Exception {
199         asserter.failExpression("objects[1].status", ".*variable 'objects' is undefined.*");
200     }
201 
202     @Test
203     public void testLeftNullOperand() throws Exception {
204         asserter.setVariable("left", null);
205         asserter.setVariable("right", Integer.valueOf(8));
206         asserter.setStrict(true);
207         asserter.failExpression("left + right", ".*null.*");
208         asserter.failExpression("left - right", ".*null.*");
209         asserter.failExpression("left * right", ".*null.*");
210         asserter.failExpression("left / right", ".*null.*");
211         asserter.failExpression("left % right", ".*null.*");
212         asserter.failExpression("left & right", ".*null.*");
213         asserter.failExpression("left | right", ".*null.*");
214         asserter.failExpression("left ^ right", ".*null.*");
215         asserter.failExpression("left < right", ".*null.*");
216         asserter.failExpression("left <= right", ".*null.*");
217         asserter.failExpression("left > right", ".*null.*");
218         asserter.failExpression("left >= right", ".*null.*");
219     }
220 
221     @Test
222     public void testLeftNullOperand2() throws Exception {
223         asserter.setVariable("x.left", null);
224         asserter.setVariable("right", Integer.valueOf(8));
225         asserter.setStrict(true);
226         asserter.failExpression("x.left + right", ".*null.*");
227         asserter.failExpression("x.left - right", ".*null.*");
228         asserter.failExpression("x.left * right", ".*null.*");
229         asserter.failExpression("x.left / right", ".*null.*");
230         asserter.failExpression("x.left % right", ".*null.*");
231         asserter.failExpression("x.left & right", ".*null.*");
232         asserter.failExpression("x.left | right", ".*null.*");
233         asserter.failExpression("x.left ^ right", ".*null.*");
234         asserter.failExpression("x.left < right", ".*null.*");
235         asserter.failExpression("x.left <= right", ".*null.*");
236         asserter.failExpression("x.left > right", ".*null.*");
237         asserter.failExpression("x.left >= right", ".*null.*");
238     }
239 
240     @Test
241     public void testRightNullOperand() throws Exception {
242         asserter.setVariable("left", Integer.valueOf(9));
243         asserter.setVariable("right", null);
244         asserter.failExpression("left + right", ".*null.*");
245         asserter.failExpression("left - right", ".*null.*");
246         asserter.failExpression("left * right", ".*null.*");
247         asserter.failExpression("left / right", ".*null.*");
248         asserter.failExpression("left % right", ".*null.*");
249         asserter.failExpression("left & right", ".*null.*");
250         asserter.failExpression("left | right", ".*null.*");
251         asserter.failExpression("left ^ right", ".*null.*");
252         asserter.failExpression("left < right", ".*null.*");
253         asserter.failExpression("left <= right", ".*null.*");
254         asserter.failExpression("left > right", ".*null.*");
255         asserter.failExpression("left >= right", ".*null.*");
256     }
257 
258     @Test
259     public void testRightNullOperand2() throws Exception {
260         asserter.setVariable("left", Integer.valueOf(9));
261         asserter.setVariable("y.right", null);
262         asserter.failExpression("left + y.right", ".*null.*");
263         asserter.failExpression("left - y.right", ".*null.*");
264         asserter.failExpression("left * y.right", ".*null.*");
265         asserter.failExpression("left / y.right", ".*null.*");
266         asserter.failExpression("left % y.right", ".*null.*");
267         asserter.failExpression("left & y.right", ".*null.*");
268         asserter.failExpression("left | y.right", ".*null.*");
269         asserter.failExpression("left ^ y.right", ".*null.*");
270         asserter.failExpression("left < y.right", ".*null.*");
271         asserter.failExpression("left <= y.right", ".*null.*");
272         asserter.failExpression("left > y.right", ".*null.*");
273         asserter.failExpression("left >= y.right", ".*null.*");
274     }
275 
276     @Test
277     public void testNullOperands() throws Exception {
278         asserter.setVariable("left", null);
279         asserter.setVariable("right", null);
280         asserter.failExpression("left + right", ".*null.*");
281         asserter.failExpression("left - right", ".*null.*");
282         asserter.failExpression("left * right", ".*null.*");
283         asserter.failExpression("left / right", ".*null.*");
284         asserter.failExpression("left % right", ".*null.*");
285         asserter.failExpression("left & right", ".*null.*");
286         asserter.failExpression("left | right", ".*null.*");
287         asserter.failExpression("left ^ right", ".*null.*");
288     }
289 
290     @Test
291     public void testNullOperand() throws Exception {
292         asserter.setVariable("right", null);
293         asserter.failExpression("~right", ".*null.*");
294     }
295 
296     @Test
297     public void testBigDecimal() throws Exception {
298         asserter.setVariable("left", new BigDecimal(2));
299         asserter.setVariable("right", new BigDecimal(6));
300         asserter.assertExpression("left + right", new BigDecimal(8));
301         asserter.assertExpression("right - left", new BigDecimal(4));
302         asserter.assertExpression("right * left", new BigDecimal(12));
303         asserter.assertExpression("right / left", new BigDecimal(3));
304         asserter.assertExpression("right % left", new BigDecimal(0));
305     }
306 
307     @Test
308     public void testBigInteger() throws Exception {
309         asserter.setVariable("left", new BigInteger("2"));
310         asserter.setVariable("right", new BigInteger("6"));
311         asserter.assertExpression("left + right", new BigInteger("8"));
312         asserter.assertExpression("right - left", new BigInteger("4"));
313         asserter.assertExpression("right * left", new BigInteger("12"));
314         asserter.assertExpression("right / left", new BigInteger("3"));
315         asserter.assertExpression("right % left", new BigInteger("0"));
316     }
317 
318     @Test
319     public void testOverflows() throws Exception {
320         asserter.assertExpression("1 + 2147483647", Long.valueOf("2147483648"));
321         asserter.assertExpression("3 + " + (Long.MAX_VALUE - 2),  BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE));
322         asserter.assertExpression("-2147483648 - 1", Long.valueOf("-2147483649"));
323         asserter.assertExpression("-3 + " + (Long.MIN_VALUE + 2),  BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.ONE));
324         asserter.assertExpression("1 + 9223372036854775807", new BigInteger("9223372036854775808"));
325         asserter.assertExpression("-1 + (-9223372036854775808)", new BigInteger("-9223372036854775809"));
326         asserter.assertExpression("-9223372036854775808 - 1", new BigInteger("-9223372036854775809"));
327         final BigInteger maxl = BigInteger.valueOf(Long.MAX_VALUE);
328         asserter.assertExpression(maxl + " * " + maxl , maxl.multiply(maxl));
329     }
330 
331     /**
332      * test some simple mathematical calculations
333      */
334     @Test
335     public void testUnaryMinus() throws Exception {
336         asserter.setVariable("aByte", new Byte((byte) 1));
337         asserter.setVariable("aShort", new Short((short) 2));
338         asserter.setVariable("anInteger", new Integer(3));
339         asserter.setVariable("aLong", new Long(4));
340         asserter.setVariable("aFloat", new Float(5.5));
341         asserter.setVariable("aDouble", new Double(6.6));
342         asserter.setVariable("aBigInteger", new BigInteger("7"));
343         asserter.setVariable("aBigDecimal", new BigDecimal("8.8"));
344 
345         // loop to allow checking caching of constant numerals (debug)
346         for(int i = 0 ; i < 2; ++i) {
347             asserter.assertExpression("-3", new Integer("-3"));
348             asserter.assertExpression("-3.0", new Double("-3.0"));
349             asserter.assertExpression("-aByte", new Byte((byte) -1));
350             asserter.assertExpression("-aShort", new Short((short) -2));
351             asserter.assertExpression("-anInteger", new Integer(-3));
352             asserter.assertExpression("-aLong", new Long(-4));
353             asserter.assertExpression("-aFloat", new Float(-5.5));
354             asserter.assertExpression("-aDouble", new Double(-6.6));
355             asserter.assertExpression("-aBigInteger", new BigInteger("-7"));
356             asserter.assertExpression("-aBigDecimal", new BigDecimal("-8.8"));
357         }
358     }
359 
360     /**
361      * test some simple mathematical calculations
362      */
363     @Test
364     public void testUnaryPlus() throws Exception {
365         asserter.setVariable("aByte", new Byte((byte) 1));
366         asserter.setVariable("aShort", new Short((short) 2));
367         asserter.setVariable("anInteger", new Integer(3));
368         asserter.setVariable("aLong", new Long(4));
369         asserter.setVariable("aFloat", new Float(5.5));
370         asserter.setVariable("aDouble", new Double(6.6));
371         asserter.setVariable("aBigInteger", new BigInteger("7"));
372         asserter.setVariable("aBigDecimal", new BigDecimal("8.8"));
373 
374         // loop to allow checking caching of constant numerals (debug)
375         for(int i = 0 ; i < 2; ++i) {
376             asserter.assertExpression("+3", new Integer("3"));
377             asserter.assertExpression("+3.0", new Double("3.0"));
378             asserter.assertExpression("+aByte", new Integer(1));
379             asserter.assertExpression("+aShort", new Integer(2));
380             asserter.assertExpression("+anInteger", new Integer(3));
381             asserter.assertExpression("+aLong", new Long(4));
382             asserter.assertExpression("+aFloat", new Float(5.5));
383             asserter.assertExpression("+aDouble", new Double(6.6));
384             asserter.assertExpression("+aBigInteger", new BigInteger("7"));
385             asserter.assertExpression("+aBigDecimal", new BigDecimal("8.8"));
386         }
387     }
388 
389     @Test
390     public void testMinusMinusPrefix() throws Exception {
391         asserter.setVariable("aByte", new Byte((byte) 2));
392         asserter.setVariable("aShort", new Short((short) 3));
393         asserter.setVariable("anInteger", new Integer(4));
394         asserter.setVariable("aLong", new Long(5));
395         asserter.setVariable("aFloat", new Float(6.6));
396         asserter.setVariable("aDouble", new Double(7.7));
397         asserter.setVariable("aBigInteger", new BigInteger("8"));
398         asserter.setVariable("aBigDecimal", new BigDecimal("9.9"));
399         asserter.setVariable("aString", "forty-two");
400 
401         asserter.assertExpression("--aByte", new Byte((byte) 1));
402         asserter.assertExpression("--aShort", new Short((short) 2));
403         asserter.assertExpression("--anInteger", new Integer(3));
404         asserter.assertExpression("--aLong", new Long(4));
405         asserter.assertExpression("--aFloat", new Float(5.6));
406         asserter.assertExpression("--aDouble", new Double(6.7));
407         asserter.assertExpression("--aBigInteger", new BigInteger("7"));
408         asserter.assertExpression("--aBigDecimal", new BigDecimal("8.9"));
409 
410         asserter.failExpression("aString--", "--", String::contains);
411     }
412 
413     @Test
414     public void testMinusMinusPostfix() throws Exception {
415         asserter.setVariable("aByte", new Byte((byte) 2));
416         asserter.setVariable("aShort", new Short((short) 3));
417         asserter.setVariable("anInteger", new Integer(4));
418         asserter.setVariable("aLong", new Long(5));
419         asserter.setVariable("aFloat", new Float(6.6));
420         asserter.setVariable("aDouble", new Double(7.7));
421         asserter.setVariable("aBigInteger", new BigInteger("8"));
422         asserter.setVariable("aBigDecimal", new BigDecimal("9.9"));
423         asserter.setVariable("aString", "forty-two");
424 
425         asserter.assertExpression("aByte--",new Byte((byte) 2));
426         asserter.assertExpression("aShort--", new Short((short) 3));
427         asserter.assertExpression("anInteger--", new Integer(4));
428         asserter.assertExpression("aLong--", new Long(5));
429         asserter.assertExpression("aFloat--", new Float(6.6));
430         asserter.assertExpression("aDouble--", new Double(7.7));
431         asserter.assertExpression("aBigInteger--", new BigInteger("8"));
432         asserter.assertExpression("aBigDecimal--", new BigDecimal("9.9"));
433 
434         asserter.assertExpression("aByte", new Byte((byte) 1));
435         asserter.assertExpression("aShort", new Short((short) 2));
436         asserter.assertExpression("anInteger", new Integer(3));
437         asserter.assertExpression("aLong", new Long(4));
438         asserter.assertExpression("aFloat", new Float(5.6));
439         asserter.assertExpression("aDouble", new Double(6.7));
440         asserter.assertExpression("aBigInteger", new BigInteger("7"));
441         asserter.assertExpression("aBigDecimal", new BigDecimal("8.9"));
442 
443         asserter.failExpression("aString--", "--", String::contains);
444     }
445 
446     @Test
447     public void testPlusPlusPrefix() throws Exception {
448         asserter.setVariable("aByte", new Byte((byte) 0));
449         asserter.setVariable("aShort", new Short((short) 1));
450         asserter.setVariable("anInteger", new Integer(2));
451         asserter.setVariable("aLong", new Long(3));
452         asserter.setVariable("aFloat", new Float(4.4));
453         asserter.setVariable("aDouble", new Double(5.5));
454         asserter.setVariable("aBigInteger", new BigInteger("6"));
455         asserter.setVariable("aBigDecimal", new BigDecimal("7.7"));
456         asserter.setVariable("aString", "forty-two");
457 
458         asserter.assertExpression("++aByte", new Byte((byte) 1));
459         asserter.assertExpression("++aShort", new Short((short) 2));
460         asserter.assertExpression("++anInteger", new Integer(3));
461         asserter.assertExpression("++aLong", new Long(4));
462         asserter.assertExpression("++aFloat", new Float(5.4));
463         asserter.assertExpression("++aDouble", new Double(6.5));
464         asserter.assertExpression("++aBigInteger", new BigInteger("7"));
465         asserter.assertExpression("++aBigDecimal", new BigDecimal("8.7"));
466 
467         asserter.failExpression("++aString", "++", String::contains);
468     }
469 
470     @Test
471     public void testPlusPlusPostfix() throws Exception {
472         asserter.setVariable("aByte", new Byte((byte) 0));
473         asserter.setVariable("aShort", new Short((short) 1));
474         asserter.setVariable("anInteger", new Integer(2));
475         asserter.setVariable("aLong", new Long(3));
476         asserter.setVariable("aFloat", new Float(4.4));
477         asserter.setVariable("aDouble", new Double(5.5));
478         asserter.setVariable("aBigInteger", new BigInteger("6"));
479         asserter.setVariable("aBigDecimal", new BigDecimal("7.7"));
480         asserter.setVariable("aString", "forty-two");
481 
482         asserter.assertExpression("aByte++", new Byte((byte) 0));
483         asserter.assertExpression("aShort++", new Short((short) 1));
484         asserter.assertExpression("anInteger++", new Integer(2));
485         asserter.assertExpression("aLong++", new Long(3));
486         asserter.assertExpression("aFloat++", new Float(4.4));
487         asserter.assertExpression("aDouble++", new Double(5.5));
488         asserter.assertExpression("aBigInteger++", new BigInteger("6"));
489         asserter.assertExpression("aBigDecimal++", new BigDecimal("7.7"));
490 
491         asserter.assertExpression("aByte", new Byte((byte) 1));
492         asserter.assertExpression("aShort", new Short((short) 2));
493         asserter.assertExpression("anInteger", new Integer(3));
494         asserter.assertExpression("aLong", new Long(4));
495         asserter.assertExpression("aFloat", new Float(5.4));
496         asserter.assertExpression("aDouble", new Double(6.5));
497         asserter.assertExpression("aBigInteger", new BigInteger("7"));
498         asserter.assertExpression("aBigDecimal", new BigDecimal("8.7"));
499 
500         asserter.failExpression("aString++", "++", String::contains);
501     }
502 
503     @Test
504     public void testNarrowBig() throws Exception {
505         List<String> ls = Arrays.asList("zero", "one", "two");
506         asserter.setVariable("list",ls);
507         asserter.setVariable("aBigDecimal", new BigDecimal("1"));
508         asserter.setVariable("aBigInteger", new BigDecimal("1"));
509         asserter.assertExpression("list.get(aBigDecimal)", "one");
510         asserter.assertExpression("list.get(aBigInteger)", "one");
511     }
512 
513     @Test
514     public void testNarrowBigDecimal() throws Exception {
515         asserter.setVariable("bi420", BigInteger.valueOf(420));
516         asserter.setVariable("bi10", BigInteger.valueOf(10));
517         asserter.setVariable("bd420", new BigDecimal("420"));
518         asserter.setVariable("bd10", new BigDecimal("10"));
519         asserter.assertExpression("420 / bi10", 42);
520         asserter.assertExpression("420l / bi10", 42L);
521         asserter.assertExpression("bi420 / 420", 1);
522         asserter.assertExpression("bi420 / 420l", 1L);
523         asserter.assertExpression("bd420 / 10", new BigDecimal("42"));
524     }
525 
526     /**
527      * test some simple mathematical calculations
528      */
529     @Test
530     public void testCalculations() throws Exception {
531         asserter.setStrict(true, false);
532         /*
533          * test new null coersion
534          */
535         asserter.setVariable("imanull", null);
536         asserter.assertExpression("imanull + 2", new Integer(2));
537         asserter.assertExpression("imanull + imanull", new Integer(0));
538         asserter.setVariable("foo", new Integer(2));
539 
540         asserter.assertExpression("foo + 2", new Integer(4));
541         asserter.assertExpression("3 + 3", new Integer(6));
542         asserter.assertExpression("3 + 3 + foo", new Integer(8));
543         asserter.assertExpression("3 * 3", new Integer(9));
544         asserter.assertExpression("3 * 3 + foo", new Integer(11));
545         asserter.assertExpression("3 * 3 - foo", new Integer(7));
546 
547         /*
548          * test parenthesized exprs
549          */
550         asserter.assertExpression("(4 + 3) * 6", new Integer(42));
551         asserter.assertExpression("(8 - 2) * 7", new Integer(42));
552 
553         /*
554          * test some floaty stuff
555          */
556         asserter.assertExpression("3 * \"3.0\"", new Double(9));
557         asserter.assertExpression("3 * 3.0", new Double(9));
558 
559         /*
560          * test / and %
561          */
562         asserter.setStrict(false, false);
563         asserter.assertExpression("6 / 3", new Integer(6 / 3));
564         asserter.assertExpression("6.4 / 3", new Double(6.4 / 3));
565         asserter.assertExpression("0 / 3", new Integer(0 / 3));
566         asserter.assertExpression("3 / 0", new Double(0));
567         asserter.assertExpression("4 % 3", new Integer(1));
568         asserter.assertExpression("4.8 % 3", new Double(4.8 % 3));
569 
570     }
571 
572     @Test
573     public void testCoercions() throws Exception {
574         asserter.assertExpression("1", new Integer(1)); // numerics default to Integer
575         asserter.assertExpression("5L", new Long(5));
576 
577         asserter.setVariable("I2", new Integer(2));
578         asserter.setVariable("L2", new Long(2));
579         asserter.setVariable("L3", new Long(3));
580         asserter.setVariable("B10", BigInteger.TEN);
581 
582         // Integer & Integer => Integer
583         asserter.assertExpression("I2 + 2", new Integer(4));
584         asserter.assertExpression("I2 * 2", new Integer(4));
585         asserter.assertExpression("I2 - 2", new Integer(0));
586         asserter.assertExpression("I2 / 2", new Integer(1));
587 
588         // Integer & Long => Long
589         asserter.assertExpression("I2 * L2", new Long(4));
590         asserter.assertExpression("I2 / L2", new Long(1));
591 
592         // Long & Long => Long
593         asserter.assertExpression("L2 + 3", new Long(5));
594         asserter.assertExpression("L2 + L3", new Long(5));
595         asserter.assertExpression("L2 / L2", new Long(1));
596         asserter.assertExpression("L2 / 2", new Long(1));
597 
598         // BigInteger
599         asserter.assertExpression("B10 / 10", BigInteger.ONE);
600         asserter.assertExpression("B10 / I2", new BigInteger("5"));
601         asserter.assertExpression("B10 / L2", new BigInteger("5"));
602     }
603 
604     // JEXL-24: long integers (and doubles)
605     @Test
606     public void testLongLiterals() {
607         final JexlEvalContext ctxt = new JexlEvalContext();
608         final JexlOptions options = ctxt.getEngineOptions();
609         options.setStrictArithmetic(true);
610         final String stmt = "{a = 10L; b = 10l; c = 42.0D; d = 42.0d; e=56.3F; f=56.3f; g=63.5; h=0x10; i=010; j=0x10L; k=010l}";
611         final JexlScript expr = JEXL.createScript(stmt);
612         /* Object value = */ expr.execute(ctxt);
613         Assert.assertEquals(10L, ctxt.get("a"));
614         Assert.assertEquals(10L, ctxt.get("b"));
615         Assert.assertEquals(42.0D, ctxt.get("c"));
616         Assert.assertEquals(42.0d, ctxt.get("d"));
617         Assert.assertEquals(56.3f, ctxt.get("e"));
618         Assert.assertEquals(56.3f, ctxt.get("f"));
619         Assert.assertEquals(63.5d, ctxt.get("g"));
620         Assert.assertEquals(0x10, ctxt.get("h"));
621         Assert.assertEquals(010, ctxt.get("i")); // octal 010
622         Assert.assertEquals(0x10L, ctxt.get("j")); // octal 010L
623         Assert.assertEquals(010L, ctxt.get("k"));
624     }
625 
626     @Test
627     public void testBigLiteralValue() {
628         final JexlEvalContext ctxt = new JexlEvalContext();
629         final JexlOptions options = ctxt.getEngineOptions();
630         options.setStrictArithmetic(true);
631         final JexlExpression e = JEXL.createExpression("9223372036854775806.5B");
632         final String res = String.valueOf(e.evaluate(ctxt));
633         Assert.assertEquals("9223372036854775806.5", res);
634     }
635 
636     @Test
637     public void testBigdOp() {
638         final BigDecimal sevendot475 = new BigDecimal("7.475");
639         final BigDecimal SO = new BigDecimal("325");
640         final JexlContext jc = new MapContext();
641         jc.set("SO", SO);
642 
643         final String expr = "2.3*SO/100";
644 
645         final Object evaluate = JEXL.createExpression(expr).evaluate(jc);
646         Assert.assertEquals(sevendot475, evaluate);
647     }
648 
649     // JEXL-24: big integers and big decimals
650     @Test
651     public void testBigLiterals() {
652         final JexlEvalContext ctxt = new JexlEvalContext();
653         final JexlOptions options = ctxt.getEngineOptions();
654         options.setStrictArithmetic(true);
655         final String stmt = "{a = 10H; b = 10h; c = 42.0B; d = 42.0b;}";
656         final JexlScript expr = JEXL.createScript(stmt);
657         /* Object value = */ expr.execute(ctxt);
658         Assert.assertEquals(new BigInteger("10"), ctxt.get("a"));
659         Assert.assertEquals(new BigInteger("10"), ctxt.get("b"));
660         Assert.assertEquals(new BigDecimal("42.0"), ctxt.get("c"));
661         Assert.assertEquals(new BigDecimal("42.0"), ctxt.get("d"));
662     }
663 
664     // JEXL-24: big decimals with exponent
665     @Test
666     public void testBigExponentLiterals() {
667         final JexlEvalContext ctxt = new JexlEvalContext();
668         final JexlOptions options = ctxt.getEngineOptions();
669         options.setStrictArithmetic(true);
670         final String stmt = "{a = 42.0e1B; b = 42.0E+2B; c = 42.0e-1B; d = 42.0E-2b; e=4242.4242e1b}";
671         final JexlScript expr = JEXL.createScript(stmt);
672         /* Object value = */ expr.execute(ctxt);
673         Assert.assertEquals(new BigDecimal("42.0e+1"), ctxt.get("a"));
674         Assert.assertEquals(new BigDecimal("42.0e+2"), ctxt.get("b"));
675         Assert.assertEquals(new BigDecimal("42.0e-1"), ctxt.get("c"));
676         Assert.assertEquals(new BigDecimal("42.0e-2"), ctxt.get("d"));
677         Assert.assertEquals(new BigDecimal("4242.4242e1"), ctxt.get("e"));
678     }
679 
680     // JEXL-24: doubles with exponent
681     @Test
682     public void test2DoubleLiterals() {
683         final JexlEvalContext ctxt = new JexlEvalContext();
684         final JexlOptions options = ctxt.getEngineOptions();
685         options.setStrictArithmetic(true);
686         final String stmt = "{a = 42.0e1D; b = 42.0E+2D; c = 42.0e-1d; d = 42.0E-2d; e=10e10; f= +1.e1; g=1e1; }";
687         final JexlScript expr = JEXL.createScript(stmt);
688         /* Object value = */ expr.execute(ctxt);
689         Assert.assertEquals(Double.valueOf("42.0e+1"), ctxt.get("a"));
690         Assert.assertEquals(Double.valueOf("42.0e+2"), ctxt.get("b"));
691         Assert.assertEquals(Double.valueOf("42.0e-1"), ctxt.get("c"));
692         Assert.assertEquals(Double.valueOf("42.0e-2"), ctxt.get("d"));
693         Assert.assertEquals(Double.valueOf("10e10"), ctxt.get("e"));
694         Assert.assertEquals(Double.valueOf("10"), ctxt.get("f"));
695         Assert.assertEquals(Double.valueOf("10"), ctxt.get("g"));
696     }
697 
698     /**
699      *
700      * if silent, all arith exception return 0.0
701      * if not silent, all arith exception throw
702      */
703     @Test
704     public void testDivideByZero() throws Exception {
705         final Map<String, Object> vars = new HashMap<>();
706         final JexlEvalContext context = new JexlEvalContext(vars);
707         final JexlOptions options = context.getEngineOptions();
708         options.setStrictArithmetic(true);
709         vars.put("aByte", new Byte((byte) 1));
710         vars.put("aShort", new Short((short) 2));
711         vars.put("aInteger", new Integer(3));
712         vars.put("aLong", new Long(4));
713         vars.put("aFloat", new Float(5.5));
714         vars.put("aDouble", new Double(6.6));
715         vars.put("aBigInteger", new BigInteger("7"));
716         vars.put("aBigDecimal", new BigDecimal("8.8"));
717 
718         vars.put("zByte", new Byte((byte) 0));
719         vars.put("zShort", new Short((short) 0));
720         vars.put("zInteger", new Integer(0));
721         vars.put("zLong", new Long(0));
722         vars.put("zFloat", new Float(0));
723         vars.put("zDouble", new Double(0));
724         vars.put("zBigInteger", new BigInteger("0"));
725         vars.put("zBigDecimal", new BigDecimal("0"));
726 
727         final String[] tnames = {
728             "Byte", "Short", "Integer", "Long",
729             "Float", "Double",
730             "BigInteger", "BigDecimal"
731         };
732         // number of permutations this will generate
733         final int PERMS = tnames.length * tnames.length;
734 
735         final JexlEngine jexl = JEXL;
736         // for non-silent, silent...
737         for (int s = 0; s < 2; ++s) {
738             final boolean strict = s != 0;
739             options.setStrict(true);
740             options.setStrictArithmetic(strict);
741             int zthrow = 0;
742             int zeval = 0;
743             // for vars of all types...
744             for (final String vname : tnames) {
745                 // for zeros of all types...
746                 for (final String zname : tnames) {
747                     // divide var by zero
748                     final String expr = "a" + vname + " / " + "z" + zname;
749                     try {
750                         final JexlExpression zexpr = jexl.createExpression(expr);
751                         final Object nan = zexpr.evaluate(context);
752                         // check we have a zero & incremement zero count
753                         if (nan instanceof Number) {
754                             final double zero = ((Number) nan).doubleValue();
755                             if (zero == 0.0) {
756                                 zeval += 1;
757                             }
758                         }
759                     } catch (final Exception any) {
760                         // increment the exception count
761                         zthrow += 1;
762                     }
763                 }
764             }
765             if (strict) {
766                 Assert.assertEquals("All expressions should have thrown " + zthrow + "/" + PERMS, zthrow, PERMS);
767             } else {
768                 Assert.assertEquals("All expressions should have zeroed " + zeval + "/" + PERMS, zeval, PERMS);
769             }
770         }
771         debuggerCheck(jexl);
772     }
773 
774     @Test
775     public void testNaN() {
776         final Map<String, Object> ns = new HashMap<>();
777         ns.put("double", Double.class);
778         final JexlEngine jexl = new JexlBuilder().namespaces(ns).create();
779         JexlScript script;
780         Object result;
781         script = jexl.createScript("#NaN");
782         result = script.execute(null);
783         Assert.assertTrue(Double.isNaN((Double) result));
784         script = jexl.createScript("NaN");
785         result = script.execute(null);
786         Assert.assertTrue(Double.isNaN((Double) result));
787         script = jexl.createScript("double:isNaN(#NaN)");
788         result = script.execute(null);
789         Assert.assertTrue((Boolean) result);
790         script = jexl.createScript("double:isNaN(NaN)");
791         result = script.execute(null);
792         Assert.assertTrue((Boolean) result);
793     }
794 
795     /**
796      * JEXL-156.
797      */
798     @Test
799     public void testMultClass(){
800         final JexlEngine jexl = new JexlBuilder().create();
801         final JexlContext jc = new MapContext();
802         final Object ra = jexl.createExpression("463.0d * 0.1").evaluate(jc);
803         Assert.assertEquals(Double.class, ra.getClass());
804         final Object r0 = jexl.createExpression("463.0B * 0.1").evaluate(jc);
805         Assert.assertEquals(java.math.BigDecimal.class, r0.getClass());
806         final Object r1 = jexl.createExpression("463.0B * 0.1B").evaluate(jc);
807         Assert.assertEquals(java.math.BigDecimal.class, r1.getClass());
808     }
809 
810     @Test
811     public void testDivClass() {
812         final JexlEngine jexl = new JexlBuilder().create();
813         final JexlContext jc = new MapContext();
814         final Object ra = jexl.createExpression("463.0d / 0.1").evaluate(jc);
815         Assert.assertEquals(Double.class, ra.getClass());
816         final Object r0 = jexl.createExpression("463.0B / 0.1").evaluate(jc);
817         Assert.assertEquals(java.math.BigDecimal.class, r0.getClass());
818         final Object r1 = jexl.createExpression("463.0B / 0.1B").evaluate(jc);
819         Assert.assertEquals(java.math.BigDecimal.class, r1.getClass());
820     }
821 
822     @Test
823     public void testPlusClass() {
824         final JexlEngine jexl = new JexlBuilder().create();
825         final JexlContext jc = new MapContext();
826         final Object ra = jexl.createExpression("463.0d + 0.1").evaluate(jc);
827         Assert.assertEquals(Double.class, ra.getClass());
828         final Object r0 = jexl.createExpression("463.0B + 0.1").evaluate(jc);
829         Assert.assertEquals(java.math.BigDecimal.class, r0.getClass());
830         final Object r1 = jexl.createExpression("463.0B + 0.1B").evaluate(jc);
831         Assert.assertEquals(java.math.BigDecimal.class, r1.getClass());
832     }
833 
834     @Test
835     public void testMinusClass() {
836         final JexlEngine jexl = new JexlBuilder().create();
837         final JexlContext jc = new MapContext();
838         final Object ra = jexl.createExpression("463.0d - 0.1").evaluate(jc);
839         Assert.assertEquals(Double.class, ra.getClass());
840         final Object r0 = jexl.createExpression("463.0B - 0.1").evaluate(jc);
841         Assert.assertEquals(java.math.BigDecimal.class, r0.getClass());
842         final Object r1 = jexl.createExpression("463.0B - 0.1B").evaluate(jc);
843         Assert.assertEquals(java.math.BigDecimal.class, r1.getClass());
844     }
845 
846     @Test
847     public void testAddWithStringsLenient() {
848         final JexlEngine jexl = new JexlBuilder().arithmetic(new JexlArithmetic(false)).create();
849         JexlScript script;
850         Object result;
851         script = jexl.createScript("'a' + 0");
852         result = script.execute(null);
853         Assert.assertEquals("a0", result);
854 
855         script = jexl.createScript("0 + 'a' ");
856         result = script.execute(null);
857         Assert.assertEquals("0a", result);
858 
859         script = jexl.createScript("0 + '1.2' ");
860         result = script.execute(null);
861         Assert.assertEquals(1.2d, (Double) result, EPSILON);
862 
863         script = jexl.createScript("'1.2' + 1.2 ");
864         result = script.execute(null);
865         Assert.assertEquals(2.4d, (Double) result, EPSILON);
866 
867         script = jexl.createScript("1.2 + 1.2 ");
868         result = script.execute(null);
869         Assert.assertEquals(2.4d, (Double) result, EPSILON);
870 
871         script = jexl.createScript("1.2 + '1.2' ");
872         result = script.execute(null);
873         Assert.assertEquals(2.4d, (Double) result, EPSILON);
874 
875         script = jexl.createScript("'1.2' + 0 ");
876         result = script.execute(null);
877         Assert.assertEquals(1.2d, (Double) result, EPSILON);
878 
879         script = jexl.createScript("'1.2' + '1.2' ");
880         result = script.execute(null);
881         Assert.assertEquals("1.21.2", result);
882     }
883 
884     @Test
885     public void testAddWithStringsStrict() {
886         final JexlEngine jexl = new JexlBuilder().arithmetic(new JexlArithmetic(true)).create();
887         JexlScript script;
888         Object result;
889         script = jexl.createScript("'a' + 0");
890         result = script.execute(null);
891         Assert.assertEquals("a0", result);
892 
893         script = jexl.createScript("0 + 'a' ");
894         result = script.execute(null);
895         Assert.assertEquals("0a", result);
896 
897         script = jexl.createScript("0 + '1.2' ");
898         result = script.execute(null);
899         Assert.assertEquals("01.2", result);
900 
901         script = jexl.createScript("'1.2' + 1.2 ");
902         result = script.execute(null);
903         Assert.assertEquals("1.21.2", result);
904 
905         script = jexl.createScript("1.2 + 1.2 ");
906         result = script.execute(null);
907         Assert.assertEquals(2.4d, (Double) result, EPSILON);
908 
909         script = jexl.createScript("1.2 + '1.2' ");
910         result = script.execute(null);
911         Assert.assertEquals("1.21.2", result);
912 
913         script = jexl.createScript("'1.2' + 0 ");
914         result = script.execute(null);
915         Assert.assertEquals("1.20", result);
916 
917         script = jexl.createScript("'1.2' + '1.2' ");
918         result = script.execute(null);
919         Assert.assertEquals("1.21.2", result);
920 
921     }
922 
923     @Test
924     public void testNullArgs() {
925         JexlEngine jexl =  new JexlBuilder().arithmetic(new JexlArithmetic(true) {
926             @Override public boolean isStrict(JexlOperator op) {
927                 return JexlOperator.ADD == op? false: super.isStrict(op);
928             }
929         }).create();
930         JexlScript script = jexl.createScript("'1.2' + x ", "x");
931         Object result = script.execute(null);
932         Assert.assertEquals("1.2", result);
933     }
934 
935     @Test
936     public void testOption() {
937         final Map<String, Object> vars = new HashMap<>();
938         final JexlEvalContext context = new JexlEvalContext(vars);
939         final JexlOptions options = context.getEngineOptions();
940         options.setStrictArithmetic(true);
941         final JexlScript script = JEXL.createScript("0 + '1.2' ");
942         Object result;
943 
944         options.setStrictArithmetic(true);
945         result = script.execute(context);
946         Assert.assertEquals("01.2", result);
947 
948         options.setStrictArithmetic(false);
949         result = script.execute(context);
950         Assert.assertEquals(1.2d, (Double) result, EPSILON);
951     }
952 
953     @Test
954     public void testIsFloatingPointPattern() {
955         final JexlArithmetic ja = new JexlArithmetic(true);
956 
957         Assert.assertFalse(ja.isFloatingPointNumber("floating point"));
958         Assert.assertFalse(ja.isFloatingPointNumber("a1."));
959         Assert.assertFalse(ja.isFloatingPointNumber("b1.2"));
960         Assert.assertFalse(ja.isFloatingPointNumber("-10.2a-34"));
961         Assert.assertFalse(ja.isFloatingPointNumber("+10.2a+34"));
962         Assert.assertFalse(ja.isFloatingPointNumber("0"));
963         Assert.assertFalse(ja.isFloatingPointNumber("1"));
964         Assert.assertFalse(ja.isFloatingPointNumber("12A"));
965         Assert.assertFalse(ja.isFloatingPointNumber("2F3"));
966         Assert.assertFalse(ja.isFloatingPointNumber("23"));
967         Assert.assertFalse(ja.isFloatingPointNumber("+3"));
968         Assert.assertFalse(ja.isFloatingPointNumber("+34"));
969         Assert.assertFalse(ja.isFloatingPointNumber("+3-4"));
970         Assert.assertFalse(ja.isFloatingPointNumber("+3.-4"));
971         Assert.assertFalse(ja.isFloatingPointNumber("3ee4"));
972 
973         Assert.assertTrue(ja.isFloatingPointNumber("0."));
974         Assert.assertTrue(ja.isFloatingPointNumber("1."));
975         Assert.assertTrue(ja.isFloatingPointNumber("1.2"));
976         Assert.assertTrue(ja.isFloatingPointNumber("1.2e3"));
977         Assert.assertTrue(ja.isFloatingPointNumber("2e3"));
978         Assert.assertTrue(ja.isFloatingPointNumber("+2e-3"));
979         Assert.assertTrue(ja.isFloatingPointNumber("+23E-34"));
980         Assert.assertTrue(ja.isFloatingPointNumber("+23.E-34"));
981         Assert.assertTrue(ja.isFloatingPointNumber("-23.4E+45"));
982         Assert.assertTrue(ja.isFloatingPointNumber("1.2e34"));
983         Assert.assertTrue(ja.isFloatingPointNumber("10.2e34"));
984         Assert.assertTrue(ja.isFloatingPointNumber("+10.2e34"));
985         Assert.assertTrue(ja.isFloatingPointNumber("-10.2e34"));
986         Assert.assertTrue(ja.isFloatingPointNumber("10.2e-34"));
987         Assert.assertTrue(ja.isFloatingPointNumber("10.2e+34"));
988         Assert.assertTrue(ja.isFloatingPointNumber("-10.2e-34"));
989         Assert.assertTrue(ja.isFloatingPointNumber("+10.2e+34"));
990         Assert.assertTrue(ja.isFloatingPointNumber("-10.2E-34"));
991         Assert.assertTrue(ja.isFloatingPointNumber("+10.2E+34"));
992     }
993 
994     public static class EmptyTestContext extends MapContext implements JexlContext.NamespaceResolver {
995         public static int log(final Object fmt, final Object... arr) {
996             //System.out.println(String.format(fmt.toString(), arr));
997             return arr == null ? 0 : arr.length;
998         }
999 
1000         public static int log(final Object fmt, final int... arr) {
1001             //System.out.println(String.format(fmt.toString(), arr));
1002             return arr == null ? 0 : arr.length;
1003         }
1004 
1005         @Override
1006         public Object resolveNamespace(final String name) {
1007             return this;
1008         }
1009     }
1010 
1011     @Test
1012     public void testEmpty() throws Exception {
1013         final Object[] SCRIPTS = {
1014             "var x = null; log('x = %s', x);", 0,
1015             "var x = 'abc'; log('x = %s', x);", 1,
1016             "var x = 333; log('x = %s', x);", 1,
1017             "var x = [1, 2]; log('x = %s', x);", 2,
1018             "var x = ['a', 'b']; log('x = %s', x);", 2,
1019             "var x = {1:'A', 2:'B'}; log('x = %s', x);", 1,
1020             "var x = null; return empty(x);", true,
1021             "var x = ''; return empty(x);", true,
1022             "var x = 'abc'; return empty(x);", false,
1023             "var x = 0; return empty(x);", true,
1024             "var x = 333; return empty(x);", false,
1025             "var x = []; return empty(x);", true,
1026             "var x = [1, 2]; return empty(x);", false,
1027             "var x = ['a', 'b']; return empty(x);", false,
1028             "var x = [...]; return empty(x);", true,
1029             "var x = [1, 2,...]; return empty(x);", false,
1030             "var x = {:}; return empty(x);", true,
1031             "var x = {1:'A', 2:'B'}; return empty(x);", false,
1032             "var x = {}; return empty(x);", true,
1033             "var x = {'A','B'}; return empty(x);", false
1034         };
1035         final JexlEngine jexl = new JexlBuilder().create();
1036         final JexlContext jc = new EmptyTestContext();
1037         JexlScript script;
1038 
1039         for (int e = 0; e < SCRIPTS.length; e += 2) {
1040             final String stext = (String) SCRIPTS[e];
1041             final Object expected = SCRIPTS[e + 1];
1042             script = jexl.createScript(stext);
1043             final Object result = script.execute(jc);
1044             Assert.assertEquals("failed on " + stext, expected, result);
1045         }
1046     }
1047 
1048     public static class Var {
1049         int value;
1050 
1051         Var(final int v) {
1052             value = v;
1053         }
1054 
1055         @Override
1056         public String toString() {
1057             return Integer.toString(value);
1058         }
1059     }
1060 
1061     // an arithmetic that knows how to deal with vars
1062     public static class ArithmeticPlus extends JexlArithmetic {
1063         public ArithmeticPlus(final boolean strict) {
1064             super(strict);
1065         }
1066 
1067         public boolean equals(final Var lhs, final Var rhs) {
1068             return lhs.value == rhs.value;
1069         }
1070 
1071         public boolean lessThan(final Var lhs, final Var rhs) {
1072             return lhs.value < rhs.value;
1073         }
1074 
1075         public boolean lessThanOrEqual(final Var lhs, final Var rhs) {
1076             return lhs.value <= rhs.value;
1077         }
1078 
1079         public boolean greaterThan(final Var lhs, final Var rhs) {
1080             return lhs.value > rhs.value;
1081         }
1082 
1083         public boolean greaterThanOrEqual(final Var lhs, final Var rhs) {
1084             return lhs.value >= rhs.value;
1085         }
1086 
1087         public Var add(final Var lhs, final Var rhs) {
1088             return new Var(lhs.value + rhs.value);
1089         }
1090 
1091         public Var subtract(final Var lhs, final Var rhs) {
1092             return new Var(lhs.value - rhs.value);
1093         }
1094 
1095         public Var divide(final Var lhs, final Var rhs) {
1096             return new Var(lhs.value / rhs.value);
1097         }
1098 
1099         public Var multiply(final Var lhs, final Var rhs) {
1100             return new Var(lhs.value * rhs.value);
1101         }
1102 
1103         public Var mod(final Var lhs, final Var rhs) {
1104             return new Var(lhs.value / rhs.value);
1105         }
1106 
1107         public Var negate(final Var arg) {
1108             return new Var(-arg.value);
1109         }
1110 
1111         public Var and(final Var lhs, final Var rhs) {
1112             return new Var(lhs.value & rhs.value);
1113         }
1114 
1115         public Var or(final Var lhs, final Var rhs) {
1116             return new Var(lhs.value | rhs.value);
1117         }
1118 
1119         public Var xor(final Var lhs, final Var rhs) {
1120             return new Var(lhs.value ^ rhs.value);
1121         }
1122 
1123         public Var shiftRight(final Var lhs, final Var rhs) {
1124             return new Var(lhs.value >> rhs.value);
1125         }
1126 
1127         public Var shiftRightUnsigned(final Var lhs, final Var rhs) {
1128             return new Var(lhs.value >>> rhs.value);
1129         }
1130 
1131         public Var shiftLeft(final Var lhs, final Var rhs) {
1132             return new Var(lhs.value << rhs.value);
1133         }
1134 
1135         public Boolean contains(final Var lhs, final Var rhs) {
1136             return lhs.toString().contains(rhs.toString());
1137         }
1138 
1139         public Boolean startsWith(final Var lhs, final Var rhs) {
1140             return lhs.toString().startsWith(rhs.toString());
1141         }
1142 
1143         public Boolean endsWith(final Var lhs, final Var rhs) {
1144             return lhs.toString().endsWith(rhs.toString());
1145         }
1146 
1147         public Var complement(final Var arg) {
1148             return new Var(~arg.value);
1149         }
1150 
1151         public Object subtract(final String x, final String y) {
1152             final int ix = x.indexOf(y);
1153             if (ix < 0) {
1154                 return x;
1155             }
1156             final StringBuilder strb = new StringBuilder(x.substring(0, ix));
1157             strb.append(x.substring(ix + y.length()));
1158             return strb.toString();
1159         }
1160 
1161         public Object negate(final String str) {
1162             final int length = str.length();
1163             final StringBuilder strb = new StringBuilder(str.length());
1164             for (int c = length - 1; c >= 0; --c) {
1165                 strb.append(str.charAt(c));
1166             }
1167             return strb.toString();
1168         }
1169 
1170         public Object not(final Var x) {
1171             throw new NullPointerException("make it fail");
1172         }
1173     }
1174 
1175     @Test
1176     public void testArithmeticPlus() throws Exception {
1177         final JexlEngine jexl = new JexlBuilder().cache(64).arithmetic(new ArithmeticPlus(false)).create();
1178         final JexlContext jc = new EmptyTestContext();
1179         runOverload(jexl, jc);
1180         runOverload(jexl, jc);
1181     }
1182 
1183     @Test
1184     public void testArithmeticPlusNoCache() {
1185         final JexlEngine jexl = new JexlBuilder().cache(0).arithmetic(new ArithmeticPlus(false)).create();
1186         final JexlContext jc = new EmptyTestContext();
1187         runOverload(jexl, jc);
1188     }
1189 
1190     protected void runOverload(final JexlEngine jexl, final JexlContext jc) {
1191         JexlScript script;
1192         Object result;
1193 
1194         script = jexl.createScript("(x, y)->{ x < y }");
1195         result = script.execute(jc, 42, 43);
1196         Assert.assertEquals(true, result);
1197         result = script.execute(jc, new Var(42), new Var(43));
1198         Assert.assertEquals(true, result);
1199         result = script.execute(jc, new Var(42), new Var(43));
1200         Assert.assertEquals(true, result);
1201         result = script.execute(jc, 43, 42);
1202         Assert.assertEquals(false, result);
1203         result = script.execute(jc, new Var(43), new Var(42));
1204         Assert.assertEquals(false, result);
1205 
1206         script = jexl.createScript("(x, y)->{ x <= y }");
1207         result = script.execute(jc, 42, 43);
1208         Assert.assertEquals(true, result);
1209         result = script.execute(jc, new Var(42), new Var(43));
1210         Assert.assertEquals(true, result);
1211         result = script.execute(jc, new Var(41), new Var(44));
1212         Assert.assertEquals(true, result);
1213         result = script.execute(jc, 43, 42);
1214         Assert.assertEquals(false, result);
1215         result = script.execute(jc, new Var(45), new Var(40));
1216         Assert.assertEquals(false, result);
1217 
1218         script = jexl.createScript("(x, y)->{ x > y }");
1219         result = script.execute(jc, 42, 43);
1220         Assert.assertEquals(false, result);
1221         result = script.execute(jc, new Var(42), new Var(43));
1222         Assert.assertEquals(false, result);
1223         result = script.execute(jc, new Var(42), new Var(43));
1224         Assert.assertEquals(false, result);
1225         result = script.execute(jc, 43, 42);
1226         Assert.assertEquals(true, result);
1227         result = script.execute(jc, new Var(43), new Var(42));
1228         Assert.assertEquals(true, result);
1229 
1230         script = jexl.createScript("(x, y)->{ x >= y }");
1231         result = script.execute(jc, 42, 43);
1232         Assert.assertEquals(false, result);
1233         result = script.execute(jc, new Var(42), new Var(43));
1234         Assert.assertEquals(false, result);
1235         result = script.execute(jc, new Var(41), new Var(44));
1236         Assert.assertEquals(false, result);
1237         result = script.execute(jc, 43, 42);
1238         Assert.assertEquals(true, result);
1239         result = script.execute(jc, new Var(45), new Var(40));
1240         Assert.assertEquals(true, result);
1241 
1242         script = jexl.createScript("(x, y)->{ x == y }");
1243         result = script.execute(jc, 42, 43);
1244         Assert.assertEquals(false, result);
1245         result = script.execute(jc, new Var(42), new Var(43));
1246         Assert.assertEquals(false, result);
1247         result = script.execute(jc, new Var(41), new Var(44));
1248         Assert.assertEquals(false, result);
1249         result = script.execute(jc, 43, 42);
1250         Assert.assertEquals(false, result);
1251         result = script.execute(jc, new Var(45), new Var(40));
1252         Assert.assertEquals(false, result);
1253 
1254         script = jexl.createScript("(x, y)->{ x != y }");
1255         result = script.execute(jc, 42, 43);
1256         Assert.assertEquals(true, result);
1257         result = script.execute(jc, new Var(42), new Var(43));
1258         Assert.assertEquals(true, result);
1259         result = script.execute(jc, new Var(44), new Var(44));
1260         Assert.assertEquals(false, result);
1261         result = script.execute(jc, 44, 44);
1262         Assert.assertEquals(false, result);
1263         result = script.execute(jc, new Var(45), new Var(40));
1264         Assert.assertEquals(true, result);
1265 
1266         script = jexl.createScript("(x, y)->{ x % y }");
1267         result = script.execute(jc, 4242, 100);
1268         Assert.assertEquals(42, result);
1269         result = script.execute(jc, new Var(4242), new Var(100));
1270         Assert.assertEquals(42, ((Var) result).value);
1271 
1272         script = jexl.createScript("(x, y)->{ x * y }");
1273         result = script.execute(jc, 6, 7);
1274         Assert.assertEquals(42, result);
1275         result = script.execute(jc, new Var(6), new Var(7));
1276         Assert.assertEquals(42, ((Var) result).value);
1277 
1278         script = jexl.createScript("(x, y)->{ x + y }");
1279         result = script.execute(jc, 35, 7);
1280         Assert.assertEquals(42, result);
1281         result = script.execute(jc, new Var(35), new Var(7));
1282         Assert.assertEquals(42, ((Var) result).value);
1283 
1284         script = jexl.createScript("(x, y)->{ x - y }");
1285         result = script.execute(jc, 49, 7);
1286         Assert.assertEquals(42, result);
1287         result = script.execute(jc, "foobarquux", "bar");
1288         Assert.assertEquals("fooquux", result);
1289         result = script.execute(jc, 50, 8);
1290         Assert.assertEquals(42, result);
1291         result = script.execute(jc, new Var(50), new Var(8));
1292         Assert.assertEquals(42, ((Var) result).value);
1293 
1294         script = jexl.createScript("(x)->{ -x }");
1295         result = script.execute(jc, -42);
1296         Assert.assertEquals(42, result);
1297         result = script.execute(jc, new Var(-42));
1298         Assert.assertEquals(42, ((Var) result).value);
1299         result = script.execute(jc, "pizza");
1300         Assert.assertEquals("azzip", result);
1301         result = script.execute(jc, -142);
1302         Assert.assertEquals(142, result);
1303 
1304         script = jexl.createScript("(x)->{ ~x }");
1305         result = script.execute(jc, -1);
1306         Assert.assertEquals(0L, result);
1307         result = script.execute(jc, new Var(-1));
1308         Assert.assertEquals(0L, ((Var) result).value);
1309         result = script.execute(jc, new Var(-42));
1310         Assert.assertEquals(41, ((Var) result).value);
1311 
1312         script = jexl.createScript("(x, y)->{ x ^ y }");
1313         result = script.execute(jc, 35, 7);
1314         Assert.assertEquals(36L, result);
1315         result = script.execute(jc, new Var(35), new Var(7));
1316         Assert.assertEquals(36L, ((Var) result).value);
1317         // legacy
1318         script = jexl.createScript("(x, y)->bitwiseXor(x,y)");
1319         result = script.execute(jc, 35, 7);
1320         Assert.assertEquals(36L, result);
1321 
1322         script = jexl.createScript("(x, y)->{ x | y }");
1323         result = script.execute(jc, 35, 7);
1324         Assert.assertEquals(39L, result);
1325         result = script.execute(jc, new Var(35), new Var(7));
1326         Assert.assertEquals(39L, ((Var) result).value);
1327         // legacy
1328         script = jexl.createScript("(x, y)->bitwiseOr(x,y)");
1329         result = script.execute(jc, 35, 7);
1330         Assert.assertEquals(39L, result);
1331 
1332         script = jexl.createScript("(x, y)->{ x << y }");
1333         result = script.execute(jc, 35, 1);
1334         Assert.assertEquals(70L, result);
1335         result = script.execute(jc, new Var(35), new Var(1));
1336         Assert.assertEquals(70L, ((Var) result).value);
1337 
1338         script = jexl.createScript("(x, y)->{ x >> y }");
1339         result = script.execute(jc, 42, 1);
1340         Assert.assertEquals(21L, result);
1341         result = script.execute(jc, new Var(42), new Var(1));
1342         Assert.assertEquals(21, ((Var) result).value);
1343 
1344         script = jexl.createScript("(x, y)->{ x >>> y }");
1345         result = script.execute(jc, 84, 2);
1346         Assert.assertEquals(21L, result);
1347         result = script.execute(jc, new Var(84), new Var(2));
1348         Assert.assertEquals(21, ((Var) result).value);
1349 
1350         script = jexl.createScript("(x, y)->{ x & y }");
1351         result = script.execute(jc, 35, 7);
1352         Assert.assertEquals(3L, result);
1353         result = script.execute(jc, new Var(35), new Var(7));
1354         Assert.assertEquals(3L, ((Var) result).value);
1355         // legacy
1356         script = jexl.createScript("(x, y)->bitwiseAnd(x,y)");
1357         result = script.execute(jc, 35, 7);
1358         Assert.assertEquals(3L, result);
1359 
1360         script = jexl.createScript("(x, y)->{ x =^ y }");
1361         result = script.execute(jc, 3115, 31);
1362         Assert.assertFalse((Boolean) result);
1363         result = script.execute(jc, new Var(3115), new Var(31));
1364         Assert.assertTrue((Boolean) result);
1365 
1366         script = jexl.createScript("(x, y)->{ x !^ y }");
1367         result = script.execute(jc, 3115, 31);
1368         Assert.assertTrue((Boolean) result);
1369         result = script.execute(jc, new Var(3115), new Var(31));
1370         Assert.assertFalse((Boolean) result);
1371 
1372         script = jexl.createScript("(x, y)->{ x =$ y }");
1373         result = script.execute(jc, 3115, 15);
1374         Assert.assertFalse((Boolean) result);
1375         result = script.execute(jc, new Var(3115), new Var(15));
1376         Assert.assertTrue((Boolean) result);
1377 
1378         script = jexl.createScript("(x, y)->{ x !$ y }");
1379         result = script.execute(jc, 3115, 15);
1380         Assert.assertTrue((Boolean) result);
1381         result = script.execute(jc, new Var(3115), new Var(15));
1382         Assert.assertFalse((Boolean) result);
1383 
1384         script = jexl.createScript("(x, y)->{ x =~ y }");
1385         result = script.execute(jc, 3155, 15);
1386         Assert.assertFalse((Boolean) result);
1387         result = script.execute(jc, new Var(3155), new Var(15));
1388         Assert.assertFalse((Boolean) result);
1389         result = script.execute(jc, new Var(15), new Var(3155));
1390         Assert.assertTrue((Boolean) result);
1391 
1392         script = jexl.createScript("(x, y)->{ x !~ y }");
1393         result = script.execute(jc, 3115, 15);
1394         Assert.assertTrue((Boolean) result);
1395         result = script.execute(jc, new Var(3155), new Var(15));
1396         Assert.assertTrue((Boolean) result);
1397         result = script.execute(jc, new Var(15), new Var(3155));
1398         Assert.assertFalse((Boolean) result);
1399 
1400         script = jexl.createScript("(x)->{ !x }");
1401         try {
1402             result = script.execute(jc, new Var(-42));
1403             Assert.fail("should fail");
1404         } catch (final JexlException xany) {
1405             Assert.assertTrue(xany instanceof JexlException.Operator);
1406         }
1407     }
1408 
1409     public static class Callable173 {
1410         public Object call(final String... arg) {
1411             return 42;
1412         }
1413         public Object call(final Integer... arg) {
1414             return arg[0] * arg[1];
1415         }
1416     }
1417 
1418     @Test
1419     public void testJexl173() {
1420         final JexlEngine jexl = new JexlBuilder().create();
1421         final JexlContext jc = new MapContext();
1422         final Callable173 c173 = new Callable173();
1423         JexlScript e = jexl.createScript( "c173(9, 6)", "c173" );
1424         Object result = e.execute(jc, c173);
1425         Assert.assertEquals(54, result);
1426         e = jexl.createScript( "c173('fourty', 'two')", "c173" );
1427         result = e.execute(jc, c173);
1428         Assert.assertEquals(42, result);
1429 
1430     }
1431 
1432     public static class Arithmetic132 extends JexlArithmetic {
1433         public Arithmetic132() {
1434             super(false);
1435         }
1436 
1437         protected double divideZero(final BigDecimal x) {
1438             final int ls = x.signum();
1439             if (ls < 0) {
1440                 return Double.NEGATIVE_INFINITY;
1441             }
1442             if (ls > 0) {
1443                 return Double.POSITIVE_INFINITY;
1444             }
1445             return Double.NaN;
1446         }
1447 
1448         protected double divideZero(final BigInteger x) {
1449             final int ls = x.signum();
1450             if (ls < 0) {
1451                 return Double.NEGATIVE_INFINITY;
1452             }
1453             if (ls > 0) {
1454                 return Double.POSITIVE_INFINITY;
1455             }
1456             return Double.NaN;
1457         }
1458 
1459         @Override
1460         public Object divide(final Object left, final Object right) {
1461             if (left == null && right == null) {
1462                 return controlNullNullOperands(JexlOperator.DIVIDE);
1463             }
1464             // if either are bigdecimal use that type
1465             if (left instanceof BigDecimal || right instanceof BigDecimal) {
1466                 final BigDecimal l = toBigDecimal(left);
1467                 final BigDecimal r = toBigDecimal(right);
1468                 if (BigDecimal.ZERO.equals(r)) {
1469                     return divideZero(l);
1470                 }
1471                 final BigDecimal result = l.divide(r, getMathContext());
1472                 return narrowBigDecimal(left, right, result);
1473             }
1474             // if either are floating point (double or float) use double
1475             if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
1476                 final double l = toDouble(left);
1477                 final double r = toDouble(right);
1478                 return new Double(l / r);
1479             }
1480             // otherwise treat as integers
1481             final BigInteger l = toBigInteger(left);
1482             final BigInteger r = toBigInteger(right);
1483             if (BigInteger.ZERO.equals(r)) {
1484                 return divideZero(l);
1485             }
1486             final BigInteger result = l.divide(r);
1487             return narrowBigInteger(left, right, result);
1488         }
1489 
1490         @Override
1491         public Object mod(final Object left, final Object right) {
1492             if (left == null && right == null) {
1493                 return controlNullNullOperands(JexlOperator.MOD);
1494             }
1495             // if either are bigdecimal use that type
1496             if (left instanceof BigDecimal || right instanceof BigDecimal) {
1497                 final BigDecimal l = toBigDecimal(left);
1498                 final BigDecimal r = toBigDecimal(right);
1499                 if (BigDecimal.ZERO.equals(r)) {
1500                     return divideZero(l);
1501                 }
1502                 final BigDecimal remainder = l.remainder(r, getMathContext());
1503                 return narrowBigDecimal(left, right, remainder);
1504             }
1505             // if either are floating point (double or float) use double
1506             if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
1507                 final double l = toDouble(left);
1508                 final double r = toDouble(right);
1509                 return new Double(l % r);
1510             }
1511             // otherwise treat as integers
1512             final BigInteger l = toBigInteger(left);
1513             final BigInteger r = toBigInteger(right);
1514             final BigInteger result = l.mod(r);
1515             if (BigInteger.ZERO.equals(r)) {
1516                 return divideZero(l);
1517             }
1518             return narrowBigInteger(left, right, result);
1519         }
1520     }
1521 
1522     @Test
1523     public void testInfiniteArithmetic() throws Exception {
1524         final Map<String, Object> ns = new HashMap<>();
1525         ns.put("math", Math.class);
1526         final JexlEngine jexl = new JexlBuilder().arithmetic(new Arithmetic132()).namespaces(ns).create();
1527 
1528         Object evaluate = jexl.createExpression("1/0").evaluate(null);
1529         Assert.assertTrue(Double.isInfinite((Double) evaluate));
1530 
1531         evaluate = jexl.createExpression("-1/0").evaluate(null);
1532         Assert.assertTrue(Double.isInfinite((Double) evaluate));
1533 
1534         evaluate = jexl.createExpression("1.0/0.0").evaluate(null);
1535         Assert.assertTrue(Double.isInfinite((Double) evaluate));
1536 
1537         evaluate = jexl.createExpression("-1.0/0.0").evaluate(null);
1538         Assert.assertTrue(Double.isInfinite((Double) evaluate));
1539 
1540         evaluate = jexl.createExpression("math:abs(-42)").evaluate(null);
1541         Assert.assertEquals(42, evaluate);
1542     }
1543 
1544     private static Document getDocument(final String xml) throws Exception {
1545         final DocumentBuilder xmlBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
1546         final InputStream stringInputStream = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
1547         return xmlBuilder.parse(stringInputStream);
1548     }
1549 
1550     public static class XmlArithmetic extends JexlArithmetic {
1551         public XmlArithmetic(final boolean astrict) {
1552             super(astrict);
1553         }
1554 
1555         public XmlArithmetic(final boolean astrict, final MathContext bigdContext, final int bigdScale) {
1556             super(astrict, bigdContext, bigdScale);
1557         }
1558 
1559         public boolean empty(final org.w3c.dom.Element elt) {
1560             return !elt.hasAttributes() && !elt.hasChildNodes();
1561         }
1562 
1563         public int size(final org.w3c.dom.Element elt) {
1564             return elt.getChildNodes().getLength();
1565         }
1566     }
1567 
1568     /**
1569      * Inspired by JEXL-16{1,2}.
1570      */
1571     @Test
1572     public void testXmlArithmetic() throws Exception {
1573         Document xml;
1574         Node x;
1575         Boolean empty;
1576         int size;
1577         final JexlEvalContext ctxt = new JexlEvalContext();
1578         final JexlEngine jexl = new JexlBuilder().strict(true).safe(false).arithmetic(new XmlArithmetic(false)).create();
1579         final JexlScript e0 = jexl.createScript("x.empty()", "x");
1580         final JexlScript e1 = jexl.createScript("empty(x)", "x");
1581         final JexlScript s0 = jexl.createScript("x.size()", "x");
1582         final JexlScript s1 = jexl.createScript("size(x)", "x");
1583 
1584         empty = (Boolean) e1.execute(null, (Object) null);
1585         Assert.assertTrue(empty);
1586         size = (Integer) s1.execute(null, (Object) null);
1587         Assert.assertEquals(0, size);
1588 
1589         try {
1590             final Object xx = e0.execute(null, (Object) null);
1591             Assert.assertNull(xx);
1592         } catch (final JexlException.Variable xvar) {
1593             Assert.assertNotNull(xvar);
1594         }
1595         try {
1596             final Object xx = s0.execute(null, (Object) null);
1597             Assert.assertNull(xx);
1598         } catch (final JexlException.Variable xvar) {
1599             Assert.assertNotNull(xvar);
1600         }
1601         final JexlOptions options = ctxt.getEngineOptions();
1602         options.setSafe(true);
1603         final Object x0 = e0.execute(ctxt, (Object) null);
1604         Assert.assertNull(x0);
1605         final Object x1 = s0.execute(ctxt, (Object) null);
1606         Assert.assertNull(x1);
1607 
1608         xml = getDocument("<node info='123'/>");
1609         x = xml.getLastChild();
1610         empty = (Boolean) e0.execute(null, x);
1611         Assert.assertFalse(empty);
1612         empty = (Boolean) e1.execute(null, x);
1613         Assert.assertFalse(empty);
1614         size = (Integer) s0.execute(null, x);
1615         Assert.assertEquals(0, size);
1616         size = (Integer) s1.execute(null, x);
1617         Assert.assertEquals(0, size);
1618         xml = getDocument("<node><a/><b/></node>");
1619         x = xml.getLastChild();
1620         empty = (Boolean) e0.execute(null, x);
1621         Assert.assertFalse(empty);
1622         empty = (Boolean) e1.execute(null, x);
1623         Assert.assertFalse(empty);
1624         size = (Integer) s0.execute(null, x);
1625         Assert.assertEquals(2, size);
1626         size = (Integer) s1.execute(null, x);
1627         Assert.assertEquals(2, size);
1628         xml = getDocument("<node/>");
1629         x = xml.getLastChild();
1630         empty = (Boolean) e0.execute(null, x);
1631         Assert.assertTrue(empty);
1632         empty = (Boolean) e1.execute(null, x);
1633         Assert.assertTrue(empty);
1634         size = (Integer) s0.execute(null, x);
1635         Assert.assertEquals(0, size);
1636         size = (Integer) s1.execute(null, x);
1637         Assert.assertEquals(0, size);
1638         xml = getDocument("<node info='123'/>");
1639         NamedNodeMap nnm = xml.getLastChild().getAttributes();
1640         Attr info = (Attr) nnm.getNamedItem("info");
1641         Assert.assertEquals("123", info.getValue());
1642 
1643         // JEXL-161
1644         final JexlContext jc = new MapContext();
1645         jc.set("x", xml.getLastChild());
1646         final String y = "456";
1647         jc.set("y", y);
1648         final JexlScript s = jexl.createScript("x.attribute.info = y");
1649         Object r;
1650         try {
1651             r = s.execute(jc);
1652             nnm = xml.getLastChild().getAttributes();
1653             info = (Attr) nnm.getNamedItem("info");
1654             Assert.assertEquals(y, r);
1655             Assert.assertEquals(y, info.getValue());
1656         } catch(JexlException.Property xprop) {
1657             // test fails in java > 11 because modules, etc; need investigation
1658             Assert.assertTrue(xprop.getMessage().contains("info"));
1659             Assert.assertTrue(getJavaVersion() > 11);
1660         }
1661     }
1662 
1663     /**
1664      * Returns the Java version as an int value.
1665      * @return the Java version as an int value (8, 9, etc.)
1666      */
1667     private static int getJavaVersion() {
1668         String version = System.getProperty("java.version");
1669         if (version.startsWith("1.")) {
1670             version = version.substring(2);
1671         }
1672         int sep = version.indexOf(".");
1673         if (sep < 0) {
1674             sep = version.indexOf("-");
1675         }
1676         if (sep > 0) {
1677             version = version.substring(0, sep);
1678         }
1679         return Integer.parseInt(version);
1680     }
1681 
1682     @Test
1683     public void testEmptyLong()  {
1684         Object x;
1685         x = JEXL.createScript("new('java.lang.Long', 4294967296)").execute(null);
1686         Assert.assertEquals(4294967296L, ((Long) x).longValue());
1687         x = JEXL.createScript("new Long(4294967296)").execute(null);
1688         Assert.assertEquals(4294967296L, ((Long) x).longValue());
1689         x = JEXL.createScript("new('java.lang.Long', '4294967296')").execute(null);
1690         Assert.assertEquals(4294967296L, ((Long) x).longValue());
1691         x = JEXL.createScript("4294967296l").execute(null);
1692         Assert.assertEquals(4294967296L, ((Long) x).longValue());
1693         x = JEXL.createScript("4294967296L").execute(null);
1694         Assert.assertEquals(4294967296L, ((Long) x).longValue());
1695         checkEmpty(x, false);
1696         x = JEXL.createScript("0L").execute(null);
1697         Assert.assertEquals(0, ((Long) x).longValue());
1698         checkEmpty(x, true);
1699     }
1700 
1701     @Test
1702     public void testEmptyFloat()  {
1703         Object x;
1704         x = JEXL.createScript("4294967296.f").execute(null);
1705         Assert.assertEquals(4294967296.0f, (Float) x, EPSILON);
1706         checkEmpty(x, false);
1707         x = JEXL.createScript("4294967296.0f").execute(null);
1708         Assert.assertEquals(4294967296.0f, (Float) x, EPSILON);
1709         checkEmpty(x, false);
1710         x = JEXL.createScript("0.0f").execute(null);
1711         Assert.assertEquals(0.0f, (Float) x, EPSILON);
1712         checkEmpty(x, true);
1713         x = Float.NaN;
1714         checkEmpty(x, true);
1715     }
1716 
1717     @Test
1718     public void testEmptyDouble()  {
1719         Object x;
1720         x = JEXL.createScript("4294967296.d").execute(null);
1721         Assert.assertEquals(4294967296.0d, (Double) x, EPSILON);
1722         checkEmpty(x, false);
1723         x = JEXL.createScript("4294967296.0d").execute(null);
1724         Assert.assertEquals(4294967296.0d, (Double) x, EPSILON);
1725         checkEmpty(x, false);
1726         x = JEXL.createScript("0.0d").execute(null);
1727         Assert.assertEquals(0.0d, (Double) x, EPSILON);
1728         checkEmpty(x, true);
1729         x = Double.NaN;
1730         checkEmpty(x, true);
1731 
1732     }
1733 
1734     void checkEmpty(final Object x, final boolean expect) {
1735         final JexlScript s0 = JEXL.createScript("empty(x)", "x");
1736         boolean empty = (Boolean) s0.execute(null, x);
1737         Assert.assertEquals(expect, empty);
1738         final JexlScript s1 = JEXL.createScript("empty x", "x");
1739         empty = (Boolean) s1.execute(null, x);
1740         Assert.assertEquals(expect, empty);
1741         final JexlScript s2 = JEXL.createScript("x.empty()", "x");
1742         empty = (Boolean) s2.execute(null, x);
1743         Assert.assertEquals(expect, empty);
1744     }
1745 
1746     @Test
1747     public void testCoerceInteger() {
1748         final JexlArithmetic ja = JEXL.getArithmetic();
1749         final JexlEvalContext ctxt = new JexlEvalContext();
1750         final JexlOptions options = ctxt.getEngineOptions();
1751         options.setStrictArithmetic(true);
1752         final String stmt = "a = 34L; b = 45.0D; c=56.0F; d=67B; e=78H;";
1753         final JexlScript expr = JEXL.createScript(stmt);
1754         /* Object value = */ expr.execute(ctxt);
1755         Assert.assertEquals(34, ja.toInteger(ctxt.get("a")));
1756         Assert.assertEquals(45, ja.toInteger(ctxt.get("b")));
1757         Assert.assertEquals(56, ja.toInteger(ctxt.get("c")));
1758         Assert.assertEquals(67, ja.toInteger(ctxt.get("d")));
1759         Assert.assertEquals(78, ja.toInteger(ctxt.get("e")));
1760         Assert.assertEquals(10, ja.toInteger("10"));
1761         Assert.assertEquals(1, ja.toInteger(true));
1762         Assert.assertEquals(0, ja.toInteger(false));
1763     }
1764 
1765     @Test
1766     public void testCoerceLong() {
1767         final JexlArithmetic ja = JEXL.getArithmetic();
1768         final JexlEvalContext ctxt = new JexlEvalContext();
1769         final JexlOptions options = ctxt.getEngineOptions();
1770         options.setStrictArithmetic(true);
1771         final String stmt = "a = 34L; b = 45.0D; c=56.0F; d=67B; e=78H;";
1772         final JexlScript expr = JEXL.createScript(stmt);
1773         /* Object value = */ expr.execute(ctxt);
1774         Assert.assertEquals(34L, ja.toLong(ctxt.get("a")));
1775         Assert.assertEquals(45L, ja.toLong(ctxt.get("b")));
1776         Assert.assertEquals(56L, ja.toLong(ctxt.get("c")));
1777         Assert.assertEquals(67L, ja.toLong(ctxt.get("d")));
1778         Assert.assertEquals(78L, ja.toLong(ctxt.get("e")));
1779         Assert.assertEquals(10L, ja.toLong("10"));
1780         Assert.assertEquals(1L, ja.toLong(true));
1781         Assert.assertEquals(0L, ja.toLong(false));
1782     }
1783 
1784     @Test
1785     public void testCoerceDouble() {
1786         final JexlArithmetic ja = JEXL.getArithmetic();
1787         final JexlEvalContext ctxt = new JexlEvalContext();
1788         final JexlOptions options = ctxt.getEngineOptions();
1789         options.setStrictArithmetic(true);
1790         final String stmt = "{a = 34L; b = 45.0D; c=56.0F; d=67B; e=78H; }";
1791         final JexlScript expr = JEXL.createScript(stmt);
1792         /* Object value = */ expr.execute(ctxt);
1793         Assert.assertEquals(34, ja.toDouble(ctxt.get("a")), EPSILON);
1794         Assert.assertEquals(45, ja.toDouble(ctxt.get("b")), EPSILON);
1795         Assert.assertEquals(56, ja.toDouble(ctxt.get("c")), EPSILON);
1796         Assert.assertEquals(67, ja.toDouble(ctxt.get("d")), EPSILON);
1797         Assert.assertEquals(78, ja.toDouble(ctxt.get("e")), EPSILON);
1798         Assert.assertEquals(10d, ja.toDouble("10"), EPSILON);
1799         Assert.assertEquals(1.D, ja.toDouble(true), EPSILON);
1800         Assert.assertEquals(0.D, ja.toDouble(false), EPSILON);
1801     }
1802 
1803     @Test
1804     public void testCoerceBigInteger() {
1805         final JexlArithmetic ja = JEXL.getArithmetic();
1806         final JexlEvalContext ctxt = new JexlEvalContext();
1807         final JexlOptions options = ctxt.getEngineOptions();
1808         options.setStrictArithmetic(true);
1809         final String stmt = "{a = 34L; b = 45.0D; c=56.0F; d=67B; e=78H; }";
1810         final JexlScript expr = JEXL.createScript(stmt);
1811         /* Object value = */ expr.execute(ctxt);
1812         Assert.assertEquals(BigInteger.valueOf(34), ja.toBigInteger(ctxt.get("a")));
1813         Assert.assertEquals(BigInteger.valueOf(45), ja.toBigInteger(ctxt.get("b")));
1814         Assert.assertEquals(BigInteger.valueOf(56), ja.toBigInteger(ctxt.get("c")));
1815         Assert.assertEquals(BigInteger.valueOf(67), ja.toBigInteger(ctxt.get("d")));
1816         Assert.assertEquals(BigInteger.valueOf(78), ja.toBigInteger(ctxt.get("e")));
1817         Assert.assertEquals(BigInteger.valueOf(10), ja.toBigInteger("10"));
1818         Assert.assertEquals(BigInteger.valueOf(1), ja.toBigInteger(true));
1819         Assert.assertEquals(BigInteger.valueOf(0), ja.toBigInteger(false));
1820     }
1821 
1822     @Test
1823     public void testCoerceBigDecimal() {
1824         final JexlArithmetic ja = JEXL.getArithmetic();
1825         final JexlEvalContext ctxt = new JexlEvalContext();
1826         final JexlOptions options = ctxt.getEngineOptions();
1827         options.setStrictArithmetic(true);
1828         final String stmt = "{a = 34L; b = 45.0D; c=56.0F; d=67B; e=78H; }";
1829         final JexlScript expr = JEXL.createScript(stmt);
1830         /* Object value = */ expr.execute(ctxt);
1831         Assert.assertEquals(BigDecimal.valueOf(34), ja.toBigDecimal(ctxt.get("a")));
1832         Assert.assertEquals(BigDecimal.valueOf(45.), ja.toBigDecimal(ctxt.get("b")));
1833         Assert.assertEquals(BigDecimal.valueOf(56.), ja.toBigDecimal(ctxt.get("c")));
1834         Assert.assertEquals(BigDecimal.valueOf(67), ja.toBigDecimal(ctxt.get("d")));
1835         Assert.assertEquals(BigDecimal.valueOf(78), ja.toBigDecimal(ctxt.get("e")));
1836         Assert.assertEquals(BigDecimal.valueOf(10), ja.toBigDecimal("10"));
1837         Assert.assertEquals(BigDecimal.valueOf(1.), ja.toBigDecimal(true));
1838         Assert.assertEquals(BigDecimal.valueOf(0.), ja.toBigDecimal(false));
1839     }
1840 
1841     @Test
1842     public void testAtomicBoolean() {
1843         // in a condition
1844         JexlScript e = JEXL.createScript("if (x) 1 else 2;", "x");
1845         final JexlContext jc = new MapContext();
1846         final AtomicBoolean ab = new AtomicBoolean(false);
1847         Object o;
1848         o = e.execute(jc, ab);
1849         Assert.assertEquals("Result is not 2", new Integer(2), o);
1850         ab.set(true);
1851         o = e.execute(jc, ab);
1852         Assert.assertEquals("Result is not 1", new Integer(1), o);
1853         // in a binary logical op
1854         e = JEXL.createScript("x && y", "x", "y");
1855         ab.set(true);
1856         o = e.execute(jc, ab, Boolean.FALSE);
1857         Assert.assertFalse((Boolean) o);
1858         ab.set(true);
1859         o = e.execute(jc, ab, Boolean.TRUE);
1860         Assert.assertTrue((Boolean) o);
1861         ab.set(false);
1862         o = e.execute(jc, ab, Boolean.FALSE);
1863         Assert.assertFalse((Boolean) o);
1864         ab.set(false);
1865         o = e.execute(jc, ab, Boolean.FALSE);
1866         Assert.assertFalse((Boolean) o);
1867         // in arithmetic op
1868         e = JEXL.createScript("x + y", "x", "y");
1869         ab.set(true);
1870         o = e.execute(jc, ab, 10);
1871         Assert.assertEquals(11, o);
1872         o = e.execute(jc, 10, ab);
1873         Assert.assertEquals(11, o);
1874         o = e.execute(jc, ab, 10.d);
1875         Assert.assertEquals(11.d, (Double) o, EPSILON);
1876         o = e.execute(jc, 10.d, ab);
1877         Assert.assertEquals(11.d, (Double) o, EPSILON);
1878 
1879         final BigInteger bi10 = BigInteger.TEN;
1880         ab.set(false);
1881         o = e.execute(jc, ab, bi10);
1882         Assert.assertEquals(bi10, o);
1883         o = e.execute(jc, bi10, ab);
1884         Assert.assertEquals(bi10, o);
1885 
1886         final BigDecimal bd10 = BigDecimal.TEN;
1887         ab.set(false);
1888         o = e.execute(jc, ab, bd10);
1889         Assert.assertEquals(bd10, o);
1890         o = e.execute(jc, bd10, ab);
1891         Assert.assertEquals(bd10, o);
1892 
1893         // in a (the) monadic op
1894         e = JEXL.createScript("!x", "x");
1895         ab.set(true);
1896         o = e.execute(jc, ab);
1897         Assert.assertFalse((Boolean) o);
1898         ab.set(false);
1899         o = e.execute(jc, ab);
1900         Assert.assertTrue((Boolean) o);
1901 
1902         // in a (the) monadic op
1903         e = JEXL.createScript("-x", "x");
1904         ab.set(true);
1905         o = e.execute(jc, ab);
1906         Assert.assertFalse((Boolean) o);
1907         ab.set(false);
1908         o = e.execute(jc, ab);
1909         Assert.assertTrue((Boolean) o);
1910     }
1911 
1912     @Test
1913     public void testCompare() {
1914         // JEXL doesn't support more than one operator in the same expression, for example: 1 == 1 == 1
1915         final Object[] EXPRESSIONS = {
1916                 // Basic compare
1917                 "1 == 1", true,
1918                 "1 != 1", false,
1919                 "1 != 2", true,
1920                 "1 > 2", false,
1921                 "1 >= 2", false,
1922                 "1 < 2", true,
1923                 "1 <= 2", true,
1924                 // Int <-> Float Coercion
1925                 "1.0 == 1", true,
1926                 "1 == 1.0", true,
1927                 "1.1 != 1", true,
1928                 "1.1 < 2", true,
1929                 // Big Decimal <-> Big Integer Coercion
1930                 "1.0b == 1h", true,
1931                 "1h == 1.0b", true,
1932                 "1.1b != 1h", true,
1933                 "1.1b < 2h", true,
1934                 // Mix all type of numbers
1935                 "1l == 1.0", true, // long and int
1936                 "1.0d == 1.0f", true, // double and float
1937                 "1l == 1.0b", true,
1938                 "1l == 1h", true,
1939                 "1.0d == 1.0b", true,
1940                 "1.0f == 1.0b", true,
1941                 "1.0d == 1h", true,
1942                 "1.0f == 1h", true,
1943                 // numbers and strings
1944                 "'1' == 1", true,
1945                 "'1' == 1l", true,
1946                 "'1' == 1h", true,
1947                 "'' == 0", true, // empty string is coerced to zero (ECMA compliance)
1948                 "'1.0' == 1", true,
1949                 "'1.0' == 1.0f", true,
1950                 "'1.0' == 1.0d", true,
1951                 "'1.0' == 1.0b", true,
1952                 "'1.01' == 1.01", true,
1953                 "'1.01' == 1", false,
1954                 "'1.01' == 1b", false,
1955                 "'1.01' == 1h", false,
1956                 "'1.00001' == 1b", false,
1957                 "'1.00001' == 1h", false,
1958                 "'1.00000001' == 1", false,
1959                 "'1.00000001' == 1b", false,
1960                 "'1.00000001' == 1h", false,
1961                 "1.0 >= '1'", true,
1962                 "1.0 > '1'", false,
1963         };
1964         final JexlEngine jexl = new JexlBuilder().create();
1965         final JexlContext jc = new EmptyTestContext();
1966         JexlExpression expression;
1967 
1968         for (int e = 0; e < EXPRESSIONS.length; e += 2) {
1969             final String stext = (String) EXPRESSIONS[e];
1970             final Object expected = EXPRESSIONS[e + 1];
1971             expression = jexl.createExpression(stext);
1972             final Object result = expression.evaluate(jc);
1973             Assert.assertEquals("failed on " + stext, expected, result);
1974         }
1975     }
1976 }