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 java.io.IOException;
20  import java.util.ArrayList;
21  import java.util.Arrays;
22  import java.util.Collection;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Collections;
26  import java.util.concurrent.atomic.AtomicInteger;
27  
28  import org.apache.commons.jexl3.jexl342.OptionalArithmetic;
29  import org.apache.commons.jexl3.junit.Asserter;
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.junit.Assert;
33  import org.junit.Before;
34  import org.junit.Test;
35  
36  
37  /**
38   * Tests for array access operator []
39   *
40   * @since 2.0
41   */
42  @SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"})
43  public class SideEffectTest extends JexlTestCase {
44  
45      private Asserter asserter;
46  
47      public SideEffectTest() {
48          super("SideEffectTest");
49      }
50  
51      @Override
52      @Before
53      public void setUp() {
54          asserter = new Asserter(JEXL);
55      }
56  
57      @Test
58      public void testSideEffectVar() throws Exception {
59          final Map<String,Object> context = asserter.getVariables();
60          final Integer i41 = Integer.valueOf(4141);
61          final Object foo = i41;
62  
63          context.put("foo", foo);
64          asserter.assertExpression("foo += 2", i41 + 2);
65          Assert.assertEquals(context.get("foo"), i41 + 2);
66  
67          context.put("foo", foo);
68          asserter.assertExpression("foo -= 2", i41 - 2);
69          Assert.assertEquals(context.get("foo"), i41 - 2);
70  
71          context.put("foo", foo);
72          asserter.assertExpression("foo *= 2", i41 * 2);
73          Assert.assertEquals(context.get("foo"), i41 * 2);
74  
75          context.put("foo", foo);
76          asserter.assertExpression("foo /= 2", i41 / 2);
77          Assert.assertEquals(context.get("foo"), i41 / 2);
78  
79          context.put("foo", foo);
80          asserter.assertExpression("foo %= 2", i41 % 2);
81          Assert.assertEquals(context.get("foo"), i41 % 2);
82  
83          context.put("foo", foo);
84          asserter.assertExpression("foo &= 3", (long) (i41 & 3));
85          Assert.assertEquals(context.get("foo"), (long)(i41 & 3));
86  
87          context.put("foo", foo);
88          asserter.assertExpression("foo |= 2", (long)(i41 | 2));
89          Assert.assertEquals(context.get("foo"), (long)(i41 | 2));
90  
91          context.put("foo", foo);
92          asserter.assertExpression("foo ^= 2", (long)(i41 ^ 2));
93          Assert.assertEquals(context.get("foo"), (long)(i41 ^ 2));
94  
95          context.put("foo", foo);
96          asserter.assertExpression("foo <<= 2", (long)(i41 << 2));
97          Assert.assertEquals(context.get("foo"), (long)(i41 << 2));
98  
99          context.put("foo", foo);
100         asserter.assertExpression("foo >>= 2", (long)(i41 >> 2));
101         Assert.assertEquals(context.get("foo"), (long)(i41 >> 2));
102 
103         context.put("foo", foo);
104         asserter.assertExpression("foo >>>= 2", (long)(i41 >>> 2));
105         Assert.assertEquals(context.get("foo"), (long)(i41 >>> 2));
106     }
107 
108     @Test
109     public void testSideEffectVarDots() throws Exception {
110         final Map<String,Object> context = asserter.getVariables();
111         final Integer i41 = Integer.valueOf(4141);
112         final Object foo = i41;
113 
114         context.put("foo.bar.quux", foo);
115         asserter.assertExpression("foo.bar.quux += 2", i41 + 2);
116         Assert.assertEquals(context.get("foo.bar.quux"), i41 + 2);
117 
118         context.put("foo.bar.quux", foo);
119         asserter.assertExpression("foo.bar.quux -= 2", i41 - 2);
120         Assert.assertEquals(context.get("foo.bar.quux"), i41 - 2);
121 
122         context.put("foo.bar.quux", foo);
123         asserter.assertExpression("foo.bar.quux *= 2", i41 * 2);
124         Assert.assertEquals(context.get("foo.bar.quux"), i41 * 2);
125 
126         context.put("foo.bar.quux", foo);
127         asserter.assertExpression("foo.bar.quux /= 2", i41 / 2);
128         Assert.assertEquals(context.get("foo.bar.quux"), i41 / 2);
129 
130         context.put("foo.bar.quux", foo);
131         asserter.assertExpression("foo.bar.quux %= 2", i41 % 2);
132         Assert.assertEquals(context.get("foo.bar.quux"), i41 % 2);
133 
134         context.put("foo.bar.quux", foo);
135         asserter.assertExpression("foo.bar.quux &= 3", (long) (i41 & 3));
136         Assert.assertEquals(context.get("foo.bar.quux"), (long)(i41 & 3));
137 
138         context.put("foo.bar.quux", foo);
139         asserter.assertExpression("foo.bar.quux |= 2", (long)(i41 | 2));
140         Assert.assertEquals(context.get("foo.bar.quux"), (long)(i41 | 2));
141 
142         context.put("foo.bar.quux", foo);
143         asserter.assertExpression("foo.bar.quux ^= 2", (long)(i41 ^ 2));
144         Assert.assertEquals(context.get("foo.bar.quux"), (long)(i41 ^ 2));
145     }
146 
147     @Test
148     public void testSideEffectArray() throws Exception {
149         final Integer i41 = Integer.valueOf(4141);
150         final Integer i42 = Integer.valueOf(42);
151         final Integer i43 = Integer.valueOf(43);
152         final String s42 = "fourty-two";
153         final String s43 = "fourty-three";
154         final Object[] foo = new Object[3];
155         foo[1] = i42;
156         foo[2] = i43;
157         asserter.setVariable("foo", foo);
158         foo[0] = i41;
159         asserter.assertExpression("foo[0] += 2", i41 + 2);
160         Assert.assertEquals(foo[0], i41 + 2);
161         foo[0] = i41;
162         asserter.assertExpression("foo[0] -= 2", i41 - 2);
163         Assert.assertEquals(foo[0], i41 - 2);
164         foo[0] = i41;
165         asserter.assertExpression("foo[0] *= 2", i41 * 2);
166         Assert.assertEquals(foo[0], i41 * 2);
167         foo[0] = i41;
168         asserter.assertExpression("foo[0] /= 2", i41 / 2);
169         Assert.assertEquals(foo[0], i41 / 2);
170         foo[0] = i41;
171         asserter.assertExpression("foo[0] %= 2", i41 % 2);
172         Assert.assertEquals(foo[0], i41 % 2);
173         foo[0] = i41;
174         asserter.assertExpression("foo[0] &= 3", (long) (i41 & 3));
175         Assert.assertEquals(foo[0], (long)(i41 & 3));
176         foo[0] = i41;
177         asserter.assertExpression("foo[0] |= 2", (long)(i41 | 2));
178         Assert.assertEquals(foo[0], (long)(i41 | 2));
179         foo[0] = i41;
180         asserter.assertExpression("foo[0] ^= 2", (long)(i41 ^ 2));
181         Assert.assertEquals(foo[0], (long)(i41 ^ 2));
182     }
183 
184     @Test
185     public void testSideEffectDotArray() throws Exception {
186         final Integer i41 = Integer.valueOf(4141);
187         final Integer i42 = Integer.valueOf(42);
188         final Integer i43 = Integer.valueOf(43);
189         final String s42 = "fourty-two";
190         final String s43 = "fourty-three";
191         final Object[] foo = new Object[3];
192         foo[1] = i42;
193         foo[2] = i43;
194         asserter.setVariable("foo", foo);
195         foo[0] = i41;
196         asserter.assertExpression("foo.0 += 2", i41 + 2);
197         Assert.assertEquals(foo[0], i41 + 2);
198         foo[0] = i41;
199         asserter.assertExpression("foo.0 -= 2", i41 - 2);
200         Assert.assertEquals(foo[0], i41 - 2);
201         foo[0] = i41;
202         asserter.assertExpression("foo.0 *= 2", i41 * 2);
203         Assert.assertEquals(foo[0], i41 * 2);
204         foo[0] = i41;
205         asserter.assertExpression("foo.0 /= 2", i41 / 2);
206         Assert.assertEquals(foo[0], i41 / 2);
207         foo[0] = i41;
208         asserter.assertExpression("foo.0 %= 2", i41 % 2);
209         Assert.assertEquals(foo[0], i41 % 2);
210         foo[0] = i41;
211         asserter.assertExpression("foo.0 &= 3", (long) (i41 & 3));
212         Assert.assertEquals(foo[0], (long)(i41 & 3));
213         foo[0] = i41;
214         asserter.assertExpression("foo.0 |= 2", (long)(i41 | 2));
215         Assert.assertEquals(foo[0], (long)(i41 | 2));
216         foo[0] = i41;
217         asserter.assertExpression("foo.0 ^= 2", (long)(i41 ^ 2));
218         Assert.assertEquals(foo[0], (long)(i41 ^ 2));
219     }
220 
221     @Test
222     public void testSideEffectAntishArray() throws Exception {
223         final Integer i41 = Integer.valueOf(4141);
224         final Integer i42 = Integer.valueOf(42);
225         final Integer i43 = Integer.valueOf(43);
226         final Object[] foo = new Object[3];
227         foo[1] = i42;
228         foo[2] = i43;
229         asserter.setVariable("foo.bar", foo);
230         foo[0] = i41;
231         asserter.assertExpression("foo.bar[0] += 2", i41 + 2);
232         Assert.assertEquals(foo[0], i41 + 2);
233         foo[0] = i41;
234         asserter.assertExpression("foo.bar[0] -= 2", i41 - 2);
235         Assert.assertEquals(foo[0], i41 - 2);
236         foo[0] = i41;
237         asserter.assertExpression("foo.bar[0] *= 2", i41 * 2);
238         Assert.assertEquals(foo[0], i41 * 2);
239         foo[0] = i41;
240         asserter.assertExpression("foo.bar[0] /= 2", i41 / 2);
241         Assert.assertEquals(foo[0], i41 / 2);
242         foo[0] = i41;
243         asserter.assertExpression("foo.bar[0] %= 2", i41 % 2);
244         Assert.assertEquals(foo[0], i41 % 2);
245         foo[0] = i41;
246         asserter.assertExpression("foo.bar[0] &= 3", (long) (i41 & 3));
247         Assert.assertEquals(foo[0], (long)(i41 & 3));
248         foo[0] = i41;
249         asserter.assertExpression("foo.bar[0] |= 2", (long)(i41 | 2));
250         Assert.assertEquals(foo[0], (long)(i41 | 2));
251         foo[0] = i41;
252         asserter.assertExpression("foo.bar[0] ^= 2", (long)(i41 ^ 2));
253         Assert.assertEquals(foo[0], (long)(i41 ^ 2));
254     }
255 
256     public static class Foo {
257         int value;
258         Foo(final int v) {
259             value = v;
260         }
261 
262         @Override
263         public String toString() {
264             return Integer.toString(value);
265         }
266 
267         public void setValue(final long v) {
268             value = (int) v;
269         }
270         public int getValue() {
271             return value;
272         }
273 
274         public void setBar(final int x, final long v) {
275             value = (int) v + x;
276         }
277 
278         public int getBar(final int x) {
279             return value + x;
280         }
281     }
282 
283     @Test
284     public void testSideEffectBean() throws Exception {
285         final Integer i41 = Integer.valueOf(4141);
286         final Foo foo = new Foo(0);
287         asserter.setVariable("foo", foo);
288         foo.value = i41;
289         asserter.assertExpression("foo.value += 2", i41 + 2);
290         Assert.assertEquals(foo.value, i41 + 2);
291         foo.value = i41;
292         asserter.assertExpression("foo.value -= 2", i41 - 2);
293         Assert.assertEquals(foo.value, i41 - 2);
294         foo.value = i41;
295         asserter.assertExpression("foo.value *= 2", i41 * 2);
296         Assert.assertEquals(foo.value, i41 * 2);
297         foo.value = i41;
298         asserter.assertExpression("foo.value /= 2", i41 / 2);
299         Assert.assertEquals(foo.value, i41 / 2);
300         foo.value = i41;
301         asserter.assertExpression("foo.value %= 2", i41 % 2);
302         Assert.assertEquals(foo.value, i41 % 2);
303         foo.value = i41;
304         asserter.assertExpression("foo.value &= 3", (long) (i41 & 3));
305         Assert.assertEquals(foo.value, i41 & 3);
306         foo.value = i41;
307         asserter.assertExpression("foo.value |= 2", (long)(i41 | 2));
308         Assert.assertEquals(foo.value, i41 | 2);
309         foo.value = i41;
310         asserter.assertExpression("foo.value ^= 2", (long)(i41 ^ 2));
311         Assert.assertEquals(foo.value, i41 ^ 2);
312     }
313 
314     @Test
315     public void testSideEffectBeanContainer() throws Exception {
316         final Integer i41 = Integer.valueOf(4141);
317         final Foo foo = new Foo(0);
318         asserter.setVariable("foo", foo);
319         foo.value = i41;
320         asserter.assertExpression("foo.bar[0] += 2", i41 + 2);
321         Assert.assertEquals(foo.value, i41 + 2);
322         foo.value = i41;
323         asserter.assertExpression("foo.bar[1] += 2", i41 + 3);
324         Assert.assertEquals(foo.value, i41 + 4);
325         foo.value = i41;
326         asserter.assertExpression("foo.bar[0] -= 2", i41 - 2);
327         Assert.assertEquals(foo.value, i41 - 2);
328         foo.value = i41;
329         asserter.assertExpression("foo.bar[0] *= 2", i41 * 2);
330         Assert.assertEquals(foo.value, i41 * 2);
331         foo.value = i41;
332         asserter.assertExpression("foo.bar[0] /= 2", i41 / 2);
333         Assert.assertEquals(foo.value, i41 / 2);
334         foo.value = i41;
335         asserter.assertExpression("foo.bar[0] %= 2", i41 % 2);
336         Assert.assertEquals(foo.value, i41 % 2);
337         foo.value = i41;
338         asserter.assertExpression("foo.bar[0] &= 3", (long) (i41 & 3));
339         Assert.assertEquals(foo.value, i41 & 3);
340         foo.value = i41;
341         asserter.assertExpression("foo.bar[0] |= 2", (long)(i41 | 2));
342         Assert.assertEquals(foo.value, i41 | 2);
343         foo.value = i41;
344         asserter.assertExpression("foo.bar[0] ^= 2", (long)(i41 ^ 2));
345         Assert.assertEquals(foo.value, i41 ^ 2);
346     }
347 
348     @Test
349     public void testArithmeticSelf() throws Exception {
350         final JexlEngine jexl = new JexlBuilder().cache(64).arithmetic(new SelfArithmetic(false)).create();
351         final JexlContext jc = null;
352         runSelfOverload(jexl, jc);
353         runSelfOverload(jexl, jc);
354     }
355 
356     @Test
357     public void testArithmeticSelfNoCache() throws Exception {
358         final JexlEngine jexl = new JexlBuilder().cache(0).arithmetic(new SelfArithmetic(false)).create();
359         final JexlContext jc = null;
360         runSelfOverload(jexl, jc);
361     }
362 
363     protected void runSelfOverload(final JexlEngine jexl, final JexlContext jc) {
364         JexlScript script;
365         Object result;
366         script = jexl.createScript("(x, y)->{ x += y }");
367         result = script.execute(jc, 3115, 15);
368         Assert.assertEquals(3115 + 15, result);
369         final Var v0 = new Var(3115);
370         result = script.execute(jc, v0, new Var(15));
371         Assert.assertEquals(result, v0);
372         Assert.assertEquals(3115 + 15, v0.value);
373 
374         script = jexl.createScript("(x, y)->{ x -= y}");
375         result = script.execute(jc, 3115, 15);
376         Assert.assertEquals(3115 - 15, result);
377         final Var v1 = new Var(3115);
378         result = script.execute(jc, v1, new Var(15));
379         Assert.assertNotEquals(result, v1); // not a real side effect
380         Assert.assertEquals(3115 - 15, ((Var) result).value);
381 
382         script = jexl.createScript("(x, y)->{ x *= y }");
383         result = script.execute(jc, 3115, 15);
384         Assert.assertEquals(3115 * 15, result);
385         final Var v2 = new Var(3115);
386         result = script.execute(jc, v2, new Var(15));
387         Assert.assertEquals(result, v2);
388         Assert.assertEquals(3115 * 15, v2.value);
389 
390         script = jexl.createScript("(x, y)->{ x /= y }");
391         result = script.execute(jc, 3115, 15);
392         Assert.assertEquals(3115 / 15, result);
393         final Var v3 = new Var(3115);
394         result = script.execute(jc, v3, new Var(15));
395         Assert.assertEquals(result, v3);
396         Assert.assertEquals(3115 / 15, v3.value);
397 
398         script = jexl.createScript("(x, y)->{ x %= y }");
399         result = script.execute(jc, 3115, 15);
400         Assert.assertEquals(3115 % 15, result);
401         final Var v4 = new Var(3115);
402         result = script.execute(jc, v4, new Var(15));
403         Assert.assertEquals(result, v4);
404         Assert.assertEquals(3115 % 15, v4.value);
405 
406         script = jexl.createScript("(x, y)->{ x &= y }");
407         result = script.execute(jc, 3115, 15);
408         Assert.assertEquals(3115L & 15, result);
409         final Var v5 = new Var(3115);
410         result = script.execute(jc, v5, new Var(15));
411         Assert.assertEquals(result, v5);
412         Assert.assertEquals(3115 & 15, v5.value);
413 
414         script = jexl.createScript("(x, y)->{ x |= y }");
415         result = script.execute(jc, 3115, 15);
416         Assert.assertEquals(3115L | 15, result);
417         final Var v6 = new Var(3115);
418         result = script.execute(jc, v6, new Var(15));
419         Assert.assertEquals(result, v6);
420         Assert.assertEquals(3115L | 15, v6.value);
421 
422         script = jexl.createScript("(x, y)->{ x ^= y }");
423         result = script.execute(jc, 3115, 15);
424         Assert.assertEquals(3115L ^ 15, result);
425         final Var v7 = new Var(3115);
426         result = script.execute(jc, v7, new Var(15));
427         Assert.assertEquals(result, v7);
428         Assert.assertEquals(3115L ^ 15, v7.value);
429 
430         script = jexl.createScript("(x, y)->{ x >>>= y }");
431         result = script.execute(jc, 234453115, 5);
432         Assert.assertEquals(234453115L >>> 5, result);
433         final Var v8 = new Var(234453115);
434         result = script.execute(jc, v8, 5);
435         Assert.assertEquals(result, v8);
436         Assert.assertEquals(234453115L >>> 5, v8.value);
437 
438         script = jexl.createScript("(x, y)->{ x >>= y }");
439         result = script.execute(jc, 435566788L, 7);
440         Assert.assertEquals(435566788L >> 7, result);
441         final Var v9 = new Var(435566788);
442         result = script.execute(jc, v9, 7);
443         Assert.assertEquals(result, v9);
444         Assert.assertEquals(435566788L >> 7, v9.value);
445 
446         script = jexl.createScript("(x, y)->{ x <<= y }");
447         result = script.execute(jc, 3115, 2);
448         Assert.assertEquals(3115L << 2, result);
449         final Var v10 = new Var(3115);
450         result = script.execute(jc, v10, 2);
451         Assert.assertEquals(result, v10);
452         Assert.assertEquals(3115L << 2, v10.value);
453     }
454 
455 
456     @Test
457     public void testIncrementSelf() throws Exception {
458         final JexlEngine jexl = new JexlBuilder().cache(64).arithmetic(new SelfArithmetic(false)).create();
459         final JexlContext jc = null;
460         runSelfIncrement(jexl, jc);
461         runSelfIncrement(jexl, jc);
462     }
463 
464     @Test
465     public void testIncrementSelfNoCache() throws Exception {
466         final JexlEngine jexl = new JexlBuilder().cache(0).arithmetic(new SelfArithmetic(false)).create();
467         final JexlContext jc = null;
468         runSelfIncrement(jexl, jc);
469     }
470 
471     protected void runSelfIncrement(final JexlEngine jexl, final JexlContext jc) {
472         JexlScript script = jexl.createScript("x -> [+x, +(x++), +x]");
473         final Var v11 = new Var(3115);
474         final AtomicInteger i11 = new AtomicInteger(3115);
475         for(Object v : Arrays.asList(v11, i11)) {
476             Object result = script.execute(jc, v);
477             Assert.assertTrue(result instanceof int[]);
478             int[] r = (int[]) result;
479             Assert.assertEquals(3115, r[0]);
480             Assert.assertEquals(3115, r[1]);
481             Assert.assertEquals(3116, r[2]);
482         }
483 
484         script = jexl.createScript("x -> [+x, +(++x), +x]");
485         final Var v12 = new Var(3189);
486         final AtomicInteger i12 = new AtomicInteger(3189);
487         for(Object v : Arrays.asList(v12, i12)) {
488             Object result = script.execute(jc, v);
489             Assert.assertTrue(result instanceof int[]);
490             int[] r = (int[]) result;
491             Assert.assertEquals(3189, r[0]);
492             Assert.assertEquals(3190, r[1]);
493             Assert.assertEquals(3190, r[2]);
494         }
495 
496         script = jexl.createScript("x -> [+x, +(x--), +x]");
497         final Var v13 = new Var(3115);
498         for(Object v : Arrays.asList(v13)) {
499             Object result = script.execute(jc, v13);
500             Assert.assertTrue(result instanceof int[]);
501             int[] r = (int[]) result;
502             Assert.assertEquals(3115, r[0]);
503             Assert.assertEquals(3115, r[1]);
504             Assert.assertEquals(3114, r[2]);
505         }
506 
507         script = jexl.createScript("x -> [+x, +(--x), +x]");
508         final Var v14 = new Var(3189);
509         for(Object v : Arrays.asList(v14)) {
510             Object result = script.execute(jc, v);
511             Assert.assertTrue(result instanceof int[]);
512             int[] r = (int[]) result;
513             Assert.assertEquals(3189, r[0]);
514             Assert.assertEquals(3188, r[1]);
515             Assert.assertEquals(3188, r[2]);
516         }
517     }
518 
519     @Test
520     public void testOverrideGetSet() throws Exception {
521         final JexlEngine jexl = new JexlBuilder().cache(64).arithmetic(new SelfArithmetic(false)).create();
522         final JexlContext jc = null;
523 
524         JexlScript script;
525         Object result;
526         final Var v0 = new Var(3115);
527         script = jexl.createScript("(x)->{ x.value}");
528         result = script.execute(jc, v0);
529         Assert.assertEquals(3115, result);
530         script = jexl.createScript("(x)->{ x['VALUE']}");
531         result = script.execute(jc, v0);
532         Assert.assertEquals(3115, result);
533         script = jexl.createScript("(x,y)->{ x.value = y}");
534         result = script.execute(jc, v0, 42);
535         Assert.assertEquals(42, result);
536         script = jexl.createScript("(x,y)->{ x['VALUE'] = y}");
537         result = script.execute(jc, v0, 169);
538         Assert.assertEquals(169, result);
539     }
540 
541     public static class Var {
542         int value;
543 
544         Var(final int v) {
545             value = v;
546         }
547 
548         @Override public String toString() {
549             return Integer.toString(value);
550         }
551     }
552 
553     // an arithmetic that performs side effects
554     public static class SelfArithmetic extends OptionalArithmetic {
555         public SelfArithmetic(final boolean strict) {
556             super(strict);
557         }
558 
559         public Object propertyGet(final Var var, final String property) {
560             return "value".equals(property)? var.value : JexlEngine.TRY_FAILED;
561         }
562 
563         public Object propertySet(final Var var, final String property, final int v) {
564             return "value".equals(property)? var.value = v : JexlEngine.TRY_FAILED;
565         }
566 
567         public Object arrayGet(final Var var, final String property) {
568             return "VALUE".equals(property)? var.value : JexlEngine.TRY_FAILED;
569         }
570 
571         public Object arraySet(final Var var, final String property, final int v) {
572             return "VALUE".equals(property)? var.value = v : JexlEngine.TRY_FAILED;
573         }
574 
575         public Var selfAdd(final Var lhs, final Var rhs) {
576             lhs.value += rhs.value;
577             return lhs;
578         }
579 
580         // for kicks, this one does not side effect but overloads nevertheless
581         public Var selfSubtract(final Var lhs, final Var rhs) {
582             return new Var(lhs.value - rhs.value);
583         }
584 
585         public Var selfDivide(final Var lhs, final Var rhs) {
586             lhs.value /= rhs.value;
587             return lhs;
588         }
589 
590         public Var selfMultiply(final Var lhs, final Var rhs) {
591             lhs.value *= rhs.value;
592             return lhs;
593         }
594 
595         public Var selfMod(final Var lhs, final Var rhs) {
596             lhs.value %= rhs.value;
597             return lhs;
598         }
599 
600         public Var and(final Var lhs, final Var rhs) {
601             return new Var(lhs.value & rhs.value);
602         }
603 
604         public Var selfAnd(final Var lhs, final Var rhs) {
605             lhs.value &= rhs.value;
606             return lhs;
607         }
608 
609         public Var or(final Var lhs, final Var rhs) {
610             return new Var(lhs.value | rhs.value);
611         }
612 
613         public Var selfOr(final Var lhs, final Var rhs) {
614             lhs.value |= rhs.value;
615             return lhs;
616         }
617 
618         public Var xor(final Var lhs, final Var rhs) {
619             return new Var(lhs.value ^ rhs.value);
620         }
621 
622         public Var selfXor(final Var lhs, final Var rhs) {
623             lhs.value ^= rhs.value;
624             return lhs;
625         }
626 
627         public Var shiftLeft(final Var lhs, final int rhs) {
628             return new Var(lhs.value << rhs);
629         }
630 
631         public Var selfShiftLeft(final Var lhs, final int rhs) {
632             lhs.value <<= rhs;
633             return lhs;
634         }
635 
636         public Var shiftRight(final Var lhs, final int rhs) {
637             return new Var(lhs.value >> rhs);
638         }
639 
640         public Var selfShiftRight(final Var lhs, final int rhs) {
641             lhs.value >>= rhs;
642             return lhs;
643         }
644 
645         public Var shiftRightUnsigned(final Var lhs, final int rhs) {
646             return new Var(lhs.value >>> rhs);
647         }
648 
649         public Var selfShiftRightUnsigned(final Var lhs, final int rhs) {
650             lhs.value >>>= rhs;
651             return lhs;
652         }
653 
654         public int increment(final Var lhs) {
655             return lhs.value + 1;
656         }
657 
658         public int decrement(final Var lhs) {
659             return lhs.value - 1;
660         }
661 
662         public int incrementAndGet(AtomicInteger i) {
663             return i.incrementAndGet();
664         }
665 
666         public int getAndIncrement(AtomicInteger i) {
667             return i.getAndIncrement();
668         }
669 
670         public int positivize(Var n) {
671             return n.value;
672         }
673 
674         public int positivize(Number n) {
675             return n.intValue();
676         }
677     }
678 
679     /**
680      * An arithmetic that implements 2 selfAdd methods.
681      */
682     public static class Arithmetic246 extends JexlArithmetic {
683         public Arithmetic246(final boolean astrict) {
684             super(astrict);
685         }
686 
687         public Object selfAdd(final Collection<String> c, final String item) throws IOException {
688             c.add(item);
689             return c;
690         }
691 
692         public Object selfAdd(final Appendable c, final String item) throws IOException {
693             c.append(item);
694             return c;
695         }
696 
697         @Override
698         public Object add(final Object right, final Object left) {
699             return super.add(left, right);
700         }
701     }
702 
703    public static class Arithmetic246b extends Arithmetic246 {
704         public Arithmetic246b(final boolean astrict) {
705             super(astrict);
706         }
707 
708         public Object selfAdd(final Object c, final String item) throws IOException {
709             if (c == null) {
710                 return new ArrayList<>(Collections.singletonList(item));
711             }
712             if (c instanceof Appendable) {
713                 ((Appendable) c).append(item);
714                 return c;
715             }
716             return JexlEngine.TRY_FAILED;
717         }
718     }
719 
720     @Test
721     public void test246() throws Exception {
722         run246(new Arithmetic246(true));
723     }
724 
725     @Test
726     public void test246b() throws Exception {
727         run246(new Arithmetic246b(true));
728     }
729 
730     private void run246(final JexlArithmetic j246) throws Exception {
731         final Log log246 = LogFactory.getLog(SideEffectTest.class);
732         // quiesce the logger
733         final java.util.logging.Logger ll246 = java.util.logging.LogManager.getLogManager().getLogger(SideEffectTest.class.getName());
734        // ll246.setLevel(Level.WARNING);
735         final JexlEngine jexl = new JexlBuilder().arithmetic(j246).cache(32).debug(true).logger(log246).create();
736         final JexlScript script = jexl.createScript("z += x", "x");
737         final MapContext ctx = new MapContext();
738         List<String> z = new ArrayList<>(1);
739 
740         // no ambiguous, std case
741         ctx.set("z", z);
742         Object zz = script.execute(ctx, "42");
743         Assert.assertSame(zz, z);
744         Assert.assertEquals(1, z.size());
745         z.clear();
746 
747         boolean t246 = false;
748         // call with null
749         try {
750             script.execute(ctx, "42");
751             zz = ctx.get("z");
752             Assert.assertTrue(zz instanceof List<?>);
753             z = (List<String>) zz;
754             Assert.assertEquals(1, z.size());
755         } catch(JexlException | ArithmeticException xjexl) {
756             t246 = true;
757             Assert.assertEquals(j246.getClass(), Arithmetic246.class);
758         }
759         ctx.clear();
760 
761         // a non ambiguous call still succeeds
762         ctx.set("z", z);
763         zz = script.execute(ctx, "-42");
764         Assert.assertSame(zz, z);
765         Assert.assertEquals(t246? 1 : 2, z.size());
766     }
767 
768     // an arithmetic that performs side effects
769     public static class Arithmetic248 extends JexlArithmetic {
770         public Arithmetic248(final boolean strict) {
771             super(strict);
772         }
773 
774         public Object arrayGet(final List<?> list, final Collection<Integer> range) {
775             final List<Object> rl = new ArrayList<>(range.size());
776             for(final int i : range) {
777                 rl.add(list.get(i));
778             }
779             return rl;
780         }
781 
782         public Object arraySet(final List<Object> list, final Collection<Integer> range, final Object value) {
783             for(final int i : range) {
784                 list.set(i, value);
785             }
786             return list;
787         }
788     }
789 
790     @Test
791     public void test248() throws Exception {
792         final MapContext ctx = new MapContext();
793         final List<Object> foo = new ArrayList<>(Arrays.asList(10, 20, 30, 40));
794         ctx.set("foo", foo);
795 
796         final JexlEngine engine = new JexlBuilder().arithmetic(new Arithmetic248(true)).create();
797         final JexlScript foo12 = engine.createScript("foo[1..2]");
798         try {
799             final Object r = foo12.execute(ctx);
800             Assert.assertEquals(Arrays.asList(20, 30), r);
801         } catch (final JexlException xp) {
802             Assert.assertTrue(xp instanceof JexlException.Property);
803         }
804 
805         final JexlScript foo12assign = engine.createScript("foo[1..2] = x", "x");
806         try {
807             final Object r = foo12assign.execute(ctx, 25);
808             Assert.assertEquals(25, r);
809             Assert.assertEquals(Arrays.asList(10, 25, 25, 40), foo);
810         } catch (final JexlException xp) {
811             Assert.assertTrue(xp instanceof JexlException.Property);
812         }
813     }
814 
815 }