1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jexl3;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertNotNull;
21 import static org.junit.jupiter.api.Assertions.assertNull;
22 import static org.junit.jupiter.api.Assertions.assertThrows;
23 import static org.junit.jupiter.api.Assertions.assertTrue;
24
25 import java.util.HashMap;
26 import java.util.Map;
27
28 import org.apache.commons.jexl3.internal.Debugger;
29 import org.apache.commons.jexl3.internal.introspection.IndexedType;
30 import org.apache.commons.jexl3.internal.introspection.Uberspect;
31 import org.apache.commons.jexl3.introspection.JexlPermissions;
32 import org.apache.commons.jexl3.junit.Asserter;
33 import org.junit.jupiter.api.BeforeEach;
34 import org.junit.jupiter.api.Test;
35
36
37
38
39 @SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"})
40 class PropertyAccessTest extends JexlTestCase {
41
42 public static class Container extends PropertyContainer {
43 public Container(final String name, final int number) {
44 super(name, number);
45 }
46
47 public Object getProperty(final int ref) {
48 switch (ref) {
49 case 0:
50 return value0;
51 case 1:
52 return value1;
53 default:
54 return null;
55 }
56 }
57
58 public void setProperty(final int ref, final int value) {
59 if (1 == ref) {
60 this.value1 = value;
61 }
62 }
63
64 public void setProperty(final int ref, final String value) {
65 if (0 == ref) {
66 this.value0 = value;
67 }
68 }
69
70 public void setProperty(final String name, final int value) {
71 if ("number".equals(name)) {
72 this.value1 = value;
73 }
74 }
75
76 @Override
77 public void setProperty(final String name, final String value) {
78 if ("name".equals(name)) {
79 this.value0 = value;
80 }
81 }
82 }
83
84 public static class Prompt {
85 private final Map<String, PromptValue> values = new HashMap<>();
86
87 public Object get(final String name) {
88 final PromptValue v = values.get(name);
89 return v != null ? v.getValue() : null;
90 }
91
92 public void set(final String name, final Object value) {
93 values.put(name, new PromptValue(value));
94 }
95 }
96
97
98
99
100 public static class PromptValue {
101
102
103 private Object value;
104
105 public PromptValue(final Object v) {
106 value = v;
107 }
108
109 public Object getValue() {
110 return value;
111 }
112
113 public void setValue(final Object value) {
114 this.value = value;
115 }
116 }
117
118
119
120
121 public static class PropertyArithmetic extends JexlArithmetic {
122 int ncalls;
123
124 public PropertyArithmetic(final boolean astrict) {
125 super(astrict);
126 }
127
128 public int getCalls() {
129 return ncalls;
130 }
131
132 public Object propertySet(final IndexedType.IndexedContainer map, final String key, final Integer value) {
133 if (map.getContainerClass().equals(PropertyContainer.class)
134 && map.getContainerName().equals("property")) {
135 try {
136 map.set(key, value.toString());
137 ncalls += 1;
138 } catch (final Exception xany) {
139 throw new JexlException.Operator(null, key + "." + value.toString(), xany);
140 }
141 return null;
142 }
143 return JexlEngine.TRY_FAILED;
144 }
145 }
146
147
148
149
150 public static class PropertyContainer {
151 String value0;
152 int value1;
153
154 public PropertyContainer(final String name, final int number) {
155 value0 = name;
156 value1 = number;
157 }
158
159 public Object getProperty(final String name) {
160 if ("name".equals(name)) {
161 return value0;
162 }
163 if ("number".equals(name)) {
164 return value1;
165 }
166 return null;
167 }
168
169 public void setProperty(final String name, final String value) {
170 if ("name".equals(name)) {
171 this.value0 = value.toUpperCase();
172 }
173 if ("number".equals(name)) {
174 this.value1 = Integer.parseInt(value) + 1000;
175 }
176 }
177 }
178
179 private Asserter asserter;
180
181 public PropertyAccessTest() {
182 super("PropertyAccessTest");
183 }
184
185 @BeforeEach
186 @Override
187 public void setUp() {
188 asserter = new Asserter(JEXL);
189 }
190
191 @Test
192 void test250() {
193 final MapContext ctx = new MapContext();
194 final HashMap<Object, Object> x = new HashMap<>();
195 x.put(2, "123456789");
196 ctx.set("x", x);
197
198 final JexlEngine engine = new JexlBuilder()
199 .uberspect(new Uberspect(null, null, JexlPermissions.UNRESTRICTED))
200 .strict(true).silent(false).create();
201
202 String stmt = "x.2.class.name";
203 JexlScript script = engine.createScript(stmt);
204 Object result = script.execute(ctx);
205 assertEquals("java.lang.String", result);
206
207 stmt = "x.3?.class.name";
208 script = engine.createScript(stmt);
209 result = script.execute(ctx);
210 assertNull(result);
211
212 stmt = "x?.3.class.name";
213 final JexlScript script1 = engine.createScript(stmt);
214 assertThrows(JexlException.class, () -> script1.execute(ctx));
215
216 stmt = "x?.3?.class.name";
217 script = engine.createScript(stmt);
218 result = script.execute(ctx);
219 assertNull(result);
220
221 stmt = "y?.3.class.name";
222 script = engine.createScript(stmt);
223 result = script.execute(ctx);
224 assertNull(result);
225
226 stmt = "x?.y?.z";
227 script = engine.createScript(stmt);
228 result = script.execute(ctx);
229 assertNull(result);
230
231 stmt = "x? (x.y? (x.y.z ?: null) :null) : null";
232 script = engine.createScript(stmt);
233 result = script.execute(ctx);
234 assertNull(result);
235 }
236
237 @Test
238 void test275a() {
239 final JexlEngine jexl = new JexlBuilder().strict(true).safe(false).create();
240 final JexlContext ctxt = new MapContext();
241 Object result = null;
242 final Prompt p0 = new Prompt();
243 p0.set("stuff", 42);
244 ctxt.set("$in", p0);
245
246
247 final JexlScript script0 = jexl.createScript("$in[p].intValue()", "p");
248 assertThrows(JexlException.Property.class, () -> script0.execute(ctxt, "fail"));
249
250 assertNull(result);
251 result = script0.execute(ctxt, "stuff");
252 assertEquals(42, result);
253
254
255 JexlScript script = jexl.createScript("$in[p]?.intValue()", "p");
256 result = script.execute(ctxt, "fail");
257 assertNull(result);
258 result = script.execute(ctxt, "stuff");
259 assertEquals(42, result);
260
261
262 final JexlScript script1 = jexl.createScript("$in.`${p}`.intValue()", "p");
263 assertThrows(JexlException.Property.class, () -> script1.execute(ctxt, "fail"));
264
265 result = script.execute(ctxt, "stuff");
266 assertEquals(42, result);
267
268
269 script = jexl.createScript("$in.`${p}`?.intValue()", "p");
270 result = script.execute(ctxt, "fail");
271 assertNull(result);
272 result = script.execute(ctxt, "stuff");
273 assertEquals(42, result);
274
275 }
276
277 @Test
278 void test275b() {
279 final JexlEngine jexl = new JexlBuilder().strict(true).safe(true).create();
280 final JexlContext ctxt = new MapContext();
281 JexlScript script;
282 final Prompt p0 = new Prompt();
283 p0.set("stuff", 42);
284 ctxt.set("$in", p0);
285
286
287 script = jexl.createScript("$in[p].intValue()", "p");
288 Object result = script.execute(ctxt, "fail");
289 assertNull(result);
290
291 result = script.execute(ctxt, "stuff");
292 assertEquals(42, result);
293
294
295 script = jexl.createScript("$in.`${p}`.intValue()", "p");
296 result = script.execute(ctxt, "fail");
297 assertNull(result);
298 result = script.execute(ctxt, "stuff");
299 assertEquals(42, result);
300
301
302 script = jexl.createScript("$in.`${p}`?.intValue()", "p");
303 result = script.execute(ctxt, "fail");
304 assertNull(result);
305 result = script.execute(ctxt, "stuff");
306 assertEquals(42, result);
307 }
308
309 @Test
310 void testErroneousIdentifier() {
311 final MapContext ctx = new MapContext();
312 final JexlEngine engine = new JexlBuilder().strict(true).silent(false).create();
313
314
315 String stmt = "(x)->{ x?.class ?? 'oops' }";
316 JexlScript script = engine.createScript(stmt);
317 Object result = script.execute(ctx, "querty");
318 assertEquals("querty".getClass(), result);
319
320
321 stmt = "(x)->{ x.class1 ?? 'oops' }";
322 script = engine.createScript(stmt);
323 result = script.execute(ctx, "querty");
324 assertEquals("oops", result);
325
326
327 ctx.set("al", "la");
328 stmt = "(x)->{ x.`c${al}ss` ?? 'oops' }";
329 script = engine.createScript(stmt);
330 result = script.execute(ctx, "querty");
331 assertEquals("querty".getClass(), result);
332
333
334 stmt = "(x)->{ x?.`c${al}ss` ?? 'oops' }";
335 script = engine.createScript(stmt);
336 result = script.execute(ctx, "querty");
337 assertEquals("querty".getClass(), result);
338
339
340 stmt = "(x)->{ x?.`c${la}ss` ?? 'oops' }";
341 script = engine.createScript(stmt);
342 result = script.execute(ctx, "querty");
343 assertEquals("oops", result);
344
345
346 stmt = "(x)->{ x.`c${la}ss` ?? 'oops' }";
347 script = engine.createScript(stmt);
348 result = script.execute(ctx, "querty");
349 assertEquals("oops", result);
350
351
352 stmt = "(x)->{ x?.`c${la--ss` ?? 'oops' }";
353 try {
354 script = engine.createScript(stmt);
355 result = script.execute(ctx, "querty");
356 } catch (final JexlException xany) {
357 assertNotNull(xany.getMessage());
358 assertTrue(xany.getMessage().contains("c${la--ss"));
359 }
360
361
362 stmt = "(x)->{ x.`c${la--ss` ?? 'oops' }";
363 try {
364 script = engine.createScript(stmt);
365 result = script.execute(ctx, "querty");
366 } catch (final JexlException xany) {
367 assertNotNull(xany.getMessage());
368 assertTrue(xany.getMessage().contains("c${la--ss"));
369 }
370 }
371
372 @Test
373 void testInnerProperty() {
374 final PropertyArithmetic pa = new PropertyArithmetic(true);
375 final JexlEngine jexl = new JexlBuilder().arithmetic(pa).strict(true).cache(32).create();
376 final Container quux = new Container("quux", 42);
377 final JexlScript get;
378 Object result;
379
380 final int calls = pa.getCalls();
381 final JexlScript getName = JEXL.createScript("foo.property.name", "foo");
382 result = getName.execute(null, quux);
383 assertEquals("quux", result);
384
385 final JexlScript get0 = JEXL.createScript("foo.property.0", "foo");
386 result = get0.execute(null, quux);
387 assertEquals("quux", result);
388
389 final JexlScript getNumber = JEXL.createScript("foo.property.number", "foo");
390 result = getNumber.execute(null, quux);
391 assertEquals(42, result);
392
393 final JexlScript get1 = JEXL.createScript("foo.property.1", "foo");
394 result = get1.execute(null, quux);
395 assertEquals(42, result);
396
397 final JexlScript setName = JEXL.createScript("foo.property.name = $0", "foo", "$0");
398 setName.execute(null, quux, "QUUX");
399 result = getName.execute(null, quux);
400 assertEquals("QUUX", result);
401 result = get0.execute(null, quux);
402 assertEquals("QUUX", result);
403
404 final JexlScript set0 = JEXL.createScript("foo.property.0 = $0", "foo", "$0");
405 set0.execute(null, quux, "BAR");
406 result = getName.execute(null, quux);
407 assertEquals("BAR", result);
408 result = get0.execute(null, quux);
409 assertEquals("BAR", result);
410
411 final JexlScript setNumber = JEXL.createScript("foo.property.number = $0", "foo", "$0");
412 setNumber.execute(null, quux, -42);
413 result = getNumber.execute(null, quux);
414 assertEquals(-42, result);
415 result = get1.execute(null, quux);
416 assertEquals(-42, result);
417
418 final JexlScript set1 = JEXL.createScript("foo.property.1 = $0", "foo", "$0");
419 set1.execute(null, quux, 24);
420 result = getNumber.execute(null, quux);
421 assertEquals(24, result);
422 result = get1.execute(null, quux);
423 assertEquals(24, result);
424
425 assertEquals(calls, pa.getCalls());
426 }
427
428 @Test
429 void testInnerViaArithmetic() {
430 final PropertyArithmetic pa = new PropertyArithmetic(true);
431 final JexlEngine jexl = new JexlBuilder().arithmetic(pa).strict(true).cache(32).create();
432 final PropertyContainer quux = new PropertyContainer("bar", 169);
433 Object result;
434
435 final JexlScript getName = jexl.createScript("foo.property.name", "foo");
436 result = getName.execute(null, quux);
437 assertEquals("bar", result);
438 final int calls = pa.getCalls();
439 final JexlScript setName = jexl.createScript("foo.property.name = $0", "foo", "$0");
440 setName.execute(null, quux, 123);
441 result = getName.execute(null, quux);
442 assertEquals("123", result);
443 setName.execute(null, quux, 456);
444 result = getName.execute(null, quux);
445 assertEquals("456", result);
446 assertEquals(calls + 2, pa.getCalls());
447 setName.execute(null, quux, "quux");
448 result = getName.execute(null, quux);
449 assertEquals("QUUX", result);
450 assertEquals(calls + 2, pa.getCalls());
451
452 final JexlScript getNumber = jexl.createScript("foo.property.number", "foo");
453 result = getNumber.execute(null, quux);
454 assertEquals(169, result);
455 final JexlScript setNumber = jexl.createScript("foo.property.number = $0", "foo", "$0");
456 setNumber.execute(null, quux, 42);
457 result = getNumber.execute(null, quux);
458 assertEquals(1042, result);
459 setNumber.execute(null, quux, 24);
460 result = getNumber.execute(null, quux);
461 assertEquals(1024, result);
462 assertEquals(calls + 4, pa.getCalls());
463 setNumber.execute(null, quux, "42");
464 result = getNumber.execute(null, quux);
465 assertEquals(1042, result);
466 assertEquals(calls + 4, pa.getCalls());
467 }
468
469 @Test
470 void testPropertyProperty() throws Exception {
471 final Integer i42 = Integer.valueOf(42);
472 final Integer i43 = Integer.valueOf(43);
473 final String s42 = "fourty-two";
474 final Object[] foo = new Object[3];
475 foo[0] = foo;
476 foo[1] = i42;
477 foo[2] = s42;
478 asserter.setVariable("foo", foo);
479 asserter.setVariable("zero", Integer.valueOf(0));
480 asserter.setVariable("one", Integer.valueOf(1));
481 asserter.setVariable("two", Integer.valueOf(2));
482 for (int l = 0; l < 2; ++l) {
483 asserter.assertExpression("foo.0", foo);
484 asserter.assertExpression("foo.0.'0'", foo);
485 asserter.assertExpression("foo.'1'", foo[1]);
486 asserter.assertExpression("foo.0.'1'", foo[1]);
487 asserter.assertExpression("foo.0.'1' = 43", i43);
488 asserter.assertExpression("foo.0.'1'", i43);
489 asserter.assertExpression("foo.0.'1' = 42", i42);
490
491 asserter.assertExpression("foo?.0.'1'", i42);
492 asserter.assertExpression("foo?.0", foo);
493 asserter.assertExpression("foo?.0.'0'", foo);
494 asserter.assertExpression("foo?.'1'", foo[1]);
495 asserter.assertExpression("foo.0?.'1'", foo[1]);
496 asserter.assertExpression("foo?.0.'1' = 43", i43);
497 asserter.assertExpression("foo?.0?.'1'", i43);
498 asserter.assertExpression("foo?.0.'1' = 42", i42);
499 asserter.assertExpression("foo?.0.'1'", i42);
500
501 asserter.assertExpression("foo?.0.`1`", i42);
502 asserter.assertExpression("foo?.0", foo);
503 asserter.assertExpression("foo?.0.'0'", foo);
504 asserter.assertExpression("foo?.`1`", foo[1]);
505 asserter.assertExpression("foo?.0.`1`", foo[1]);
506 asserter.assertExpression("foo?.0.`${one}` = 43", i43);
507 asserter.assertExpression("foo.0?.`${one}`", i43);
508 asserter.assertExpression("foo.0.`${one}` = 42", i42);
509 asserter.assertExpression("foo?.0?.`${one}`", i42);
510
511 asserter.assertExpression("foo?[0].'1'", i42);
512 asserter.assertExpression("foo?[0]", foo);
513 asserter.assertExpression("foo?[0].'0'", foo);
514 asserter.assertExpression("foo?[1]", foo[1]);
515 asserter.assertExpression("foo[0]?.'1'", foo[1]);
516 asserter.assertExpression("foo?[0].'1' = 43", i43);
517 asserter.assertExpression("foo?[0]?.'1'", i43);
518 asserter.assertExpression("foo?[0].'1' = 42", i42);
519 asserter.assertExpression("foo?[0].'1'", i42);
520 }
521 }
522 @Test
523 void testStringIdentifier() {
524 final Map<String, String> foo = new HashMap<>();
525
526 final JexlContext jc = new MapContext();
527 jc.set("foo", foo);
528 foo.put("q u u x", "456");
529 JexlExpression e = JEXL.createExpression("foo.\"q u u x\"");
530 Object result = e.evaluate(jc);
531 assertEquals("456", result);
532 e = JEXL.createExpression("foo.'q u u x'");
533 result = e.evaluate(jc);
534 assertEquals("456", result);
535 JexlScript s = JEXL.createScript("foo.\"q u u x\"");
536 result = s.execute(jc);
537 assertEquals("456", result);
538 s = JEXL.createScript("foo.'q u u x'");
539 result = s.execute(jc);
540 assertEquals("456", result);
541
542 final Debugger dbg = new Debugger();
543 dbg.debug(e);
544 final String dbgdata = dbg.toString();
545 assertEquals("foo.'q u u x'", dbgdata);
546 }
547
548 }