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.assertDoesNotThrow;
20 import static org.junit.jupiter.api.Assertions.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertFalse;
22 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
23 import static org.junit.jupiter.api.Assertions.assertNotNull;
24 import static org.junit.jupiter.api.Assertions.assertNull;
25 import static org.junit.jupiter.api.Assertions.assertSame;
26 import static org.junit.jupiter.api.Assertions.assertThrows;
27 import static org.junit.jupiter.api.Assertions.assertTrue;
28
29 import java.io.PrintWriter;
30 import java.io.StringReader;
31 import java.io.StringWriter;
32 import java.io.Writer;
33 import java.lang.reflect.Method;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.Set;
41
42 import org.apache.commons.jexl3.internal.Debugger;
43 import org.apache.commons.jexl3.internal.TemplateDebugger;
44 import org.apache.commons.jexl3.internal.TemplateInterpreter;
45 import org.apache.commons.jexl3.internal.introspection.Permissions;
46 import org.apache.commons.jexl3.internal.introspection.Uberspect;
47 import org.apache.commons.jexl3.introspection.JexlPermissions;
48 import org.apache.commons.logging.Log;
49 import org.apache.commons.logging.LogFactory;
50 import org.junit.jupiter.api.AfterEach;
51 import org.junit.jupiter.api.BeforeEach;
52 import org.junit.jupiter.api.Test;
53 import org.junit.jupiter.params.ParameterizedTest;
54 import org.junit.jupiter.params.provider.MethodSource;
55
56
57
58
59 @SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"})
60 class JXLTTest extends JexlTestCase {
61
62 public static class Context311 extends MapContext
63 implements JexlContext.OptionsHandle, JexlContext.ThreadLocal {
64 private JexlOptions options;
65
66 public Executor311 exec(final String name) {
67 return new Executor311(name);
68 }
69
70 @Override
71 public JexlOptions getEngineOptions() {
72 return options;
73 }
74
75 JexlOptions newOptions() {
76 options = new JexlOptions();
77 return options;
78 }
79
80 public void setOptions(final JexlOptions o) {
81 options = o;
82 }
83 }
84 public static class Executor311 {
85 private final String name;
86
87 public Executor311(final String name) {
88 this.name = name;
89 }
90
91 public Object execute(final JexlScript script, final Object ...args) {
92 Object[] actuals;
93 if (args != null && args.length > 0) {
94 actuals = new Object[args.length + 1] ;
95 System.arraycopy(args, 0, actuals, 1, args.length);
96 actuals[0] = name;
97 } else {
98 actuals = new Object[]{name};
99 }
100 return script.execute(JexlEngine.getThreadContext(), actuals);
101 }
102 }
103 public static class Froboz {
104 int value;
105
106 public Froboz(final int v) {
107 value = v;
108 }
109
110 public int getValue() {
111 return value;
112 }
113
114 public int plus10() {
115 final int i = value;
116 value += 10;
117 return i;
118 }
119
120 public void setValue(final int v) {
121 value = v;
122 }
123 }
124 public static class FrobozWriter extends PrintWriter {
125 public FrobozWriter(final Writer w) {
126 super(w);
127 }
128
129 public void print(final Froboz froboz) {
130 super.print("froboz{");
131 super.print(froboz.value);
132 super.print("}");
133 }
134
135 @Override
136 public String toString() {
137 return out.toString();
138 }
139 }
140 private static final Log LOGGER = LogFactory.getLog(JxltEngine.class);
141 private static final Permissions NOJEXL3 = new Permissions() {
142 @Override public boolean allow(final Class<?> clazz) {
143 final String cname = clazz.getName();
144 return !cname.contains("jexl3") || cname.contains("311");
145 }
146 };
147
148 public static List<JexlBuilder> engines() {
149 final JexlFeatures f = new JexlFeatures();
150 f.lexical(true).lexicalShade(true);
151
152
153
154 final JexlPermissions permissions = JexlPermissions.RESTRICTED.compose(
155 "org.apache.commons.jexl3 +{ -JXLTTest {"
156 + " +Context311 {} +Executor311 {} +Froboz {} +FrobozWriter {} +Arithmetic425 {}"
157 + " } }");
158 return Arrays.asList(
159 new JexlBuilder().permissions(permissions).silent(false).lexical(true).lexicalShade(true).cache(128).strict(true),
160 new JexlBuilder().permissions(permissions).features(f).silent(false).cache(128).strict(true),
161 new JexlBuilder().permissions(permissions).silent(false).cache(128).strict(true));
162 }
163
164 private static String refactor(final TemplateDebugger td, final JxltEngine.Template ts) {
165 final boolean dbg = td.debug(ts);
166 if (dbg) {
167 return td.toString();
168 }
169 return "";
170 }
171
172 private final MapContext vars = new MapContext();
173
174 private JexlEvalContext context;
175
176 private JexlBuilder BUILDER;
177
178 private JexlEngine ENGINE;
179
180 private JxltEngine JXLT;
181
182 public JXLTTest() {
183 super("JXLTTest");
184 }
185
186 boolean contains(final Set<List<String>> set, final List<String> list) {
187 for (final List<String> sl : set) {
188 if (sl.equals(list)) {
189 return true;
190 }
191 }
192 return false;
193 }
194
195
196 private String getSource(final String tostring) {
197 final int len = tostring.length();
198 int sc = tostring.lastIndexOf(" /*= ");
199 if (sc >= 0) {
200 sc += " /*= ".length();
201 }
202 final int ec = tostring.lastIndexOf(" */");
203 if (sc >= 0 && ec >= 0 && ec > sc && ec < len) {
204 return tostring.substring(sc, ec);
205 }
206 return tostring;
207
208 }
209
210 private void init(final JexlBuilder builder) {
211 BUILDER = builder;
212 ENGINE = BUILDER.create();
213 JXLT = ENGINE.createJxltEngine();
214 }
215
216 private boolean isLexicalShade() {
217 JexlOptions options = context.getEngineOptions();
218 if (options.isLexicalShade()) {
219 return true;
220 }
221 options = new JexlOptions().set(ENGINE);
222 return options.isLexicalShade();
223 }
224
225 @BeforeEach
226 @Override
227 public void setUp() {
228
229 java.util.logging.Logger.getLogger(org.apache.commons.jexl3.JexlEngine.class.getName()).setLevel(java.util.logging.Level.SEVERE);
230 context = new JexlEvalContext(vars);
231 }
232
233 @AfterEach
234 @Override
235 public void tearDown() throws Exception {
236 debuggerCheck(ENGINE);
237 super.tearDown();
238 }
239
240 @ParameterizedTest
241 @MethodSource("engines")
242 void test311a(final JexlBuilder builder) {
243 init(builder);
244 final JexlContext ctx = null;
245
246 final String rpt
247 = "$$((a)->{\n"
248 + "<p>Universe ${a}</p>\n"
249 + "$$})(42)";
250
251 final JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader(rpt));
252 final StringWriter strw = new StringWriter();
253 t.evaluate(ctx, strw);
254 final String output = strw.toString();
255 assertEquals("<p>Universe 42</p>\n", output);
256 }
257
258 @ParameterizedTest
259 @MethodSource("engines")
260 void test311b(final JexlBuilder builder) {
261 init(builder);
262 final JexlContext ctx311 = new Context311();
263
264 final String rpt
265 = "$$ exec('42').execute(()->{\n"
266 + "<p>Universe 42</p>\n"
267 + "$$})";
268
269 final JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader(rpt));
270 final StringWriter strw = new StringWriter();
271 t.evaluate(ctx311, strw, 42);
272 final String output = strw.toString();
273 assertEquals("<p>Universe 42</p>\n", output);
274 }
275
276 @ParameterizedTest
277 @MethodSource("engines")
278 void test311c(final JexlBuilder builder) {
279 init(builder);
280 final Context311 ctx311 = new Context311();
281 ctx311.newOptions().setLexical(true);
282
283 final String rpt
284 = "$$ exec('42').execute((a)->{"
285 + "\n<p>Universe ${a}</p>"
286 + "\n$$})";
287
288 final JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader(rpt));
289 final StringWriter strw = new StringWriter();
290 t.evaluate(ctx311, strw, 42);
291 final String output = strw.toString();
292 assertEquals("<p>Universe 42</p>\n", output);
293 }
294
295 @ParameterizedTest
296 @MethodSource("engines")
297 void test311d(final JexlBuilder builder) {
298 init(builder);
299 final Context311 ctx311 = new Context311();
300 ctx311.newOptions().setLexical(true);
301
302 final String rpt
303 = "$$ exec('4').execute((a, b)->{"
304 + "\n<p>Universe ${a}${b}</p>"
305 + "\n$$}, '2')";
306
307 final JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader(rpt));
308 final StringWriter strw = new StringWriter();
309 t.evaluate(ctx311, strw, 42);
310 final String output = strw.toString();
311 assertEquals("<p>Universe 42</p>\n", output);
312 }
313
314 @ParameterizedTest
315 @MethodSource("engines")
316 void test311e(final JexlBuilder builder) {
317 init(builder);
318 final Context311 ctx311 = new Context311();
319 ctx311.newOptions().setLexical(true);
320
321 final String rpt
322 = "exec('4').execute((a, b)->{"
323 + " '<p>Universe ' + a + b + '</p>'"
324 + "}, '2')";
325
326 final JexlScript script = JEXL.createScript(rpt);
327 final String output = script.execute(ctx311, 42).toString();
328 assertEquals("<p>Universe 42</p>", output);
329 }
330
331 @ParameterizedTest
332 @MethodSource("engines")
333 void test311f(final JexlBuilder builder) {
334 init(builder);
335 final Context311 ctx311 = new Context311();
336 ctx311.newOptions().setLexical(true);
337
338 final String rpt
339 = "exec('4').execute((a, b)->{"
340 + " `<p>Universe ${a}${b}</p>`"
341 + "}, '2')";
342
343 final JexlScript script = JEXL.createScript(rpt);
344 final String output = script.execute(ctx311, 42).toString();
345 assertEquals("<p>Universe 42</p>", output);
346 }
347
348 @ParameterizedTest
349 @MethodSource("engines")
350 void test311g(final JexlBuilder builder) {
351 init(builder);
352 final Context311 ctx311 = new Context311();
353 ctx311.newOptions().setLexical(true);
354
355 final String rpt
356 = "(a, b)->{"
357 + " `<p>Universe ${a}${b}</p>`"
358 + "}";
359
360 final JexlScript script = JEXL.createScript(rpt);
361 final String output = script.execute(ctx311, "4", "2").toString();
362 assertEquals("<p>Universe 42</p>", output);
363 }
364
365 @ParameterizedTest
366 @MethodSource("engines")
367 void test311h(final JexlBuilder builder) {
368 init(builder);
369 final Context311 ctx311 = new Context311();
370 ctx311.newOptions().setLexical(true);
371 final String rpt= " `<p>Universe ${a}${b}</p>`";
372 final JexlScript script = JEXL.createScript(rpt, "a", "b");
373 final String output = script.execute(ctx311, "4", "2").toString();
374 assertEquals("<p>Universe 42</p>", output);
375 }
376
377 @ParameterizedTest
378 @MethodSource("engines")
379 void test311i(final JexlBuilder builder) {
380 init(builder);
381 final JexlContext ctx311 = new Context311();
382
383 final String rpt
384 = "$$var u = 'Universe'; exec('4').execute((a, b)->{"
385 + "\n<p>${u} ${a}${b}</p>"
386 + "\n$$}, '2')";
387
388 final JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader(rpt));
389 final StringWriter strw = new StringWriter();
390 t.evaluate(ctx311, strw, 42);
391 final String output = strw.toString();
392 assertEquals("<p>Universe 42</p>\n", output);
393 }
394
395 @ParameterizedTest
396 @MethodSource("engines")
397 void test315(final JexlBuilder builder) {
398 init(builder);
399 String s315;
400 StringWriter strw;
401 JxltEngine.Template t315;
402 String output;
403
404 s315 = "<report/>$";
405 t315 = JXLT.createTemplate("$$", new StringReader(s315));
406 strw = new StringWriter();
407 t315.evaluate(context, strw);
408 output = strw.toString();
409 assertEquals(s315, output);
410
411 s315 = "<foo/>#";
412 t315 = JXLT.createTemplate("$$", new StringReader(s315));
413 strw = new StringWriter();
414 t315.evaluate(context, strw);
415 output = strw.toString();
416 assertEquals(s315, output);
417
418 s315 = "<bar/>\\";
419 t315 = JXLT.createTemplate("$$", new StringReader(s315));
420 strw = new StringWriter();
421 t315.evaluate(context, strw);
422 output = strw.toString();
423 assertEquals(s315, output);
424 }
425
426 @ParameterizedTest
427 @MethodSource("engines")
428 void test42(final JexlBuilder builder) {
429 init(builder);
430
431 final String test42
432 = "$$ for (var x : list) {\n"
433 + "$$ if (x == 42) {\n"
434 + "Life, the universe, and everything\n"
435 + "$$ } else if (x > 42) {\n"
436 + "The value ${x} is over fourty-two\n"
437 + "$$ } else {\n"
438 + "The value ${x} is under fourty-two\n"
439 + "$$ }\n"
440 + "$$ }\n";
441
442 final JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader(test42), "list");
443 final StringWriter strw = new StringWriter();
444 final int[] list = {1, 3, 5, 42, 169};
445 t.evaluate(context, strw, list);
446 final String output = strw.toString();
447
448 final String out42
449 = "The value 1 is under fourty-two\n"
450 + "The value 3 is under fourty-two\n"
451 + "The value 5 is under fourty-two\n"
452 + "Life, the universe, and everything\n"
453 + "The value 169 is over fourty-two\n";
454
455 assertEquals(out42, output);
456
457 final String dstr = t.asString();
458 assertNotNull(dstr);
459
460 final TemplateDebugger td = new TemplateDebugger();
461 final String refactored = refactor(td, t);
462 assertNotNull(refactored);
463 assertEquals(test42, refactored);
464 }
465
466 @Test
467 void testParseIdentifier() {
468 assertNull(JexlArithmetic.parseIdentifier(null));
469 assertNull(JexlArithmetic.parseIdentifier(""));
470 assertNull(JexlArithmetic.parseIdentifier("za"));
471 assertNull(JexlArithmetic.parseIdentifier("a"));
472 assertNull(JexlArithmetic.parseIdentifier("00"));
473 assertNull(JexlArithmetic.parseIdentifier("01"));
474 assertNull(JexlArithmetic.parseIdentifier("001"));
475 assertNull(JexlArithmetic.parseIdentifier("12345678901"));
476
477 assertEquals(0, JexlArithmetic.parseIdentifier("0"));
478 assertEquals(10, JexlArithmetic.parseIdentifier("10"));
479 assertEquals(100, JexlArithmetic.parseIdentifier("100"));
480 assertEquals(42, JexlArithmetic.parseIdentifier("42"));
481 assertEquals(42000, JexlArithmetic.parseIdentifier("42000"));
482
483 assertEquals(42, JexlArithmetic.parseIdentifier(42));
484 }
485
486
487
488
489
490 public static class Arithmetic425 extends JexlArithmetic {
491 public Arithmetic425(final boolean astrict) {
492 super(astrict);
493 }
494
495 public Object propertyGet(final List list, final String property) {
496 final Integer id = JexlArithmetic.parseIdentifier(property);
497 return id == null? JexlEngine.TRY_FAILED : list.get(id);
498 }
499
500 public Object propertySet(final List list, final String property, final Object value) {
501 final Integer id = JexlArithmetic.parseIdentifier(property);
502 if (id != null) {
503 return list.set(id, value);
504 }
505 return JexlEngine.TRY_FAILED;
506 }
507
508 public Object propertyGet(final Map list, final String property) {
509
510 if (list.keySet().stream().findFirst().orElse(null) instanceof Number) {
511 final Integer id = JexlArithmetic.parseIdentifier(property);
512 if (id != null) {
513 return list.get(id);
514 }
515 }
516 return JexlEngine.TRY_FAILED;
517 }
518
519 public Object propertySet(final Map list, final String property, final Object value) {
520
521 if (list.keySet().stream().findFirst().orElse(null) instanceof Number) {
522 final Integer id = JexlArithmetic.parseIdentifier(property);
523 if (id != null) {
524 list.put(id, value);
525 return value;
526 }
527 }
528 return JexlEngine.TRY_FAILED;
529 }
530 }
531
532 @Test void test425a() {
533 final String S42 = "fourty-two";
534 final JexlBuilder builder = new JexlBuilder().strictInterpolation(true);
535 final JexlEngine jexl = builder.create();
536 JexlScript script;
537 Object result;
538
539 script = jexl.createScript("let x = 42; let y = `${x}`; y");
540 result = script.execute(null);
541 assertInstanceOf(String.class, result);
542 assertEquals("42", result);
543
544 final Map<Object,Object> map = Collections.singletonMap("42", S42);
545 script = jexl.createScript("let x = 42; map.`${x}`", "map");
546 result = script.execute(null, map);
547 assertEquals(S42, result);
548
549 final List<String> list = Collections.singletonList(S42);
550 final JexlScript finalScript = script;
551 assertThrows(JexlException.Property.class, () -> finalScript.execute(null, list));
552 script = jexl.createScript("let x = 0; list[x]", "list");
553 assertEquals(S42, result);
554 }
555
556 @Test void test425b() {
557 final String S42 = "fourty-two";
558 final JexlEngine jexl = new JexlBuilder().strictInterpolation(false).create();
559 run425bc(jexl, false);
560 run425bc(jexl, false);
561 }
562
563 @Test void test425c() {
564 final JexlEngine jexl = new JexlBuilder()
565 .cache(8)
566 .arithmetic(new Arithmetic425(true))
567 .strictInterpolation(true).create();
568 run425bc(jexl, true);
569 run425bc(jexl, true);
570 }
571
572 void run425bc(final JexlEngine jexl, final boolean strictInterpolation) {
573 final String S42 = "fourty-two";
574 JexlScript script;
575 Object result;
576 script = jexl.createScript("let x = 42; let y = `${x}`; y");
577 result = script.execute(null);
578 assertEquals(!strictInterpolation? 42 : "42", result);
579 Map<Object,Object> map = Collections.singletonMap(0, S42);
580 script = jexl.createScript("let x = 0; map.`${x}`", "map");
581 result = script.execute(null, map);
582 assertEquals(S42, result);
583 List<String> list = Collections.singletonList(S42);
584 result = script.execute(null, list);
585 assertEquals(S42, result);
586
587 map = new HashMap<>(map);
588 map.put(0, "nothing");
589 script = jexl.createScript("let x = 0; map.`${x}` = S42", "map", "S42");
590 result = script.execute(null, map, S42);
591 assertEquals(S42, result);
592 list = new ArrayList<>(list);
593 list.set(0, "nothing");
594 result = script.execute(null, map, S42);
595 assertEquals(S42, result);
596 }
597
598 @ParameterizedTest
599 @MethodSource("engines")
600 void testAssign(final JexlBuilder builder) {
601 init(builder);
602 final Froboz froboz = new Froboz(32);
603 context.set("froboz", froboz);
604 final JxltEngine.Expression assign = JXLT.createExpression("${froboz.value = 42}");
605 final JxltEngine.Expression check = JXLT.createExpression("${froboz.value}");
606 Object o = assign.evaluate(context);
607 assertEquals(Integer.valueOf(42), o);
608 o = check.evaluate(context);
609 assertEquals(Integer.valueOf(42), o);
610 }
611
612 @ParameterizedTest
613 @MethodSource("engines")
614 void testBadContextNested(final JexlBuilder builder) {
615 init(builder);
616 final JxltEngine.Expression expr = JXLT.createExpression("#{${hi}+'.world'}");
617 final JexlContext none = null;
618 final JxltEngine.Exception xjexl = assertThrows(JxltEngine.Exception.class, () -> expr.evaluate(none), "should be malformed");
619 LOGGER.debug(xjexl.getMessage());
620 }
621
622 @ParameterizedTest
623 @MethodSource("engines")
624 void testCharAtBug(final JexlBuilder builder) {
625 init(builder);
626 context.set("foo", "abcdef");
627 final JexlOptions options = context.getEngineOptions();
628 JxltEngine.Expression expr = JXLT.createExpression("${foo.substring(2,4)/*comment*/}");
629 Object o = expr.evaluate(context);
630 assertEquals("cd", o);
631 context.set("bar", "foo");
632 try {
633 options.setSilent(true);
634 expr = JXLT.createExpression("#{${bar}+'.charAt(-2)'}");
635 expr = expr.prepare(context);
636 o = expr.evaluate(context);
637 assertNull(o);
638 } finally {
639 options.setSilent(false);
640 }
641 }
642
643 @ParameterizedTest
644 @MethodSource("engines")
645 void testCommentedTemplate0(final JexlBuilder builder) {
646 init(builder);
647 final JexlContext ctxt = new MapContext();
648 final JexlEngine jexl = new JexlBuilder().create();
649 final JxltEngine jxlt = jexl.createJxltEngine();
650 JxltEngine.Template tmplt;
651
652 final String src = "$$/*\n"
653 + "Hello\n"
654 + "$$*/";
655 tmplt = jxlt.createTemplate(src);
656 assertNotNull(tmplt);
657 final Writer strw = new StringWriter();
658 tmplt.evaluate(ctxt, strw);
659 assertTrue(strw.toString().isEmpty());
660 }
661
662 @ParameterizedTest
663 @MethodSource("engines")
664 void testCommentedTemplate1(final JexlBuilder builder) {
665 init(builder);
666 final JexlContext ctxt = new MapContext();
667 final JexlEngine jexl = new JexlBuilder().create();
668 final JxltEngine jxlt = jexl.createJxltEngine();
669 JxltEngine.Template tmplt;
670
671 final String src = "$$/*\n"
672 + "one\n"
673 + "$$*/\n"
674 + "42\n"
675 + "$$/*\n"
676 + "three\n"
677 + "$$*/\n";
678
679 tmplt = jxlt.createTemplate(src);
680 assertNotNull(tmplt);
681 final Writer strw = new StringWriter();
682 tmplt.evaluate(ctxt, strw);
683 assertEquals("42\n", strw.toString());
684 }
685
686 @ParameterizedTest
687 @MethodSource("engines")
688 void testComposite(final JexlBuilder builder) {
689 init(builder);
690 final String source = "Dear ${p} ${name};";
691 final JxltEngine.Expression expr = JXLT.createExpression(source);
692 context.set("p", "Mr");
693 context.set("name", "Doe");
694 assertTrue(expr.isImmediate(), "expression should be immediate");
695 Object o = expr.evaluate(context);
696 assertEquals("Dear Mr Doe;", o);
697 context.set("p", "Ms");
698 context.set("name", "Jones");
699 o = expr.evaluate(context);
700 assertEquals("Dear Ms Jones;", o);
701 assertEquals(source, getSource(expr.toString()));
702 }
703
704 @ParameterizedTest
705 @MethodSource("engines")
706 void testConstant0(final JexlBuilder builder) {
707 init(builder);
708 final JexlContext none = null;
709 final String source = "Hello World!";
710 final JxltEngine.Expression expr = JXLT.createExpression(source);
711 assertSame(expr.prepare(none), expr, "prepare should return same expression");
712 final Object o = expr.evaluate(none);
713 assertTrue(expr.isImmediate(), "expression should be immediate");
714 assertEquals("Hello World!", o);
715
716 assertEquals(source, getSource(expr.toString()));
717 }
718
719 @ParameterizedTest
720 @MethodSource("engines")
721 void testConstant2(final JexlBuilder builder) {
722 init(builder);
723 final JexlContext none = null;
724 final String source = "${size({'map':123,'map2':456})}";
725 final JxltEngine.Expression expr = JXLT.createExpression(source);
726
727 final Object o = expr.evaluate(none);
728 assertTrue(expr.isImmediate(), "expression should be immediate");
729 assertEquals(2, o);
730
731 assertEquals(source, getSource(expr.toString()));
732 }
733
734 @ParameterizedTest
735 @MethodSource("engines")
736 void testConstant3(final JexlBuilder builder) {
737 init(builder);
738 final JexlContext none = null;
739 final String source = "#{size({'map':123,'map2':456})}";
740 final JxltEngine.Expression expr = JXLT.createExpression(source);
741
742 final Object o = expr.evaluate(none);
743 assertTrue(expr.isDeferred(), "expression should be deferred");
744 assertEquals(2, o);
745
746 assertEquals(source, getSource(expr.toString()));
747 }
748
749 @ParameterizedTest
750 @MethodSource("engines")
751 void testConstant4(final JexlBuilder builder) {
752 init(builder);
753 final JexlContext none = null;
754 final String source = "#{ ${size({'1':2,'2': 3})} }";
755 final JxltEngine.Expression expr = JXLT.createExpression(source);
756
757 final Object o = expr.evaluate(none);
758 assertTrue(expr.isDeferred(), "expression should be deferred");
759 assertEquals(2, o);
760
761 assertEquals(source, getSource(expr.toString()));
762 }
763
764 @ParameterizedTest
765 @MethodSource("engines")
766 void testConstantTemplate(final JexlBuilder builder) {
767 init(builder);
768
769 final String src = "<script>\n" +
770 " function test(src){\n" +
771 " var res = src.replace(/\\n\\t\\s/g, '\\n');\n" +
772 " }\n" +
773 " test();\n" +
774 " </script>";
775
776 final JexlContext ctxt = new MapContext();
777 final JexlEngine jexl = new JexlBuilder().create();
778 final JxltEngine jxlt = jexl.createJxltEngine();
779 JxltEngine.Template tmplt;
780 tmplt = jxlt.createTemplate(src);
781 assertNotNull(tmplt);
782 final Writer strw = new StringWriter();
783 tmplt.evaluate(ctxt, strw);
784 final String result = strw.toString();
785 assertEquals(src, result);
786 }
787
788 @ParameterizedTest
789 @MethodSource("engines")
790 void testDbgEscapes(final JexlBuilder builder) {
791 init(builder);
792
793 final String[] srcs = {
794 "jexl:print('hello\\'\\nworld')",
795 "'hello\\tworld'",
796 "'hello\\nworld'",
797 "'hello\\fworld'",
798 "'hello\\rworld'"
799 };
800
801 for(final String src : srcs) {
802 final JexlScript script = ENGINE.createScript(src);
803 final Debugger dbg = new Debugger();
804 dbg.debug(script);
805 final String msrc = dbg.toString();
806 assertEquals(src, msrc);
807 }
808 }
809
810 @ParameterizedTest
811 @MethodSource("engines")
812 void testDeferred(final JexlBuilder builder) {
813 init(builder);
814 final JexlContext none = null;
815 final String source = "#{'world'}";
816 final JxltEngine.Expression expr = JXLT.createExpression(source);
817 assertTrue(expr.isDeferred(), "expression should be deferred");
818 final String as = expr.prepare(none).asString();
819 assertEquals("${'world'}", as, "prepare should return immediate version");
820 final Object o = expr.evaluate(none);
821 assertEquals("world", o);
822
823 assertEquals(source, getSource(expr.toString()));
824 }
825
826 @ParameterizedTest
827 @MethodSource("engines")
828 void testEscape(final JexlBuilder builder) {
829 init(builder);
830 final JexlContext none = null;
831 JxltEngine.Expression expr;
832 Object o;
833
834 expr = JXLT.createExpression("\\#{'world'}");
835 o = expr.evaluate(none);
836 assertEquals("#{'world'}", o);
837 expr = JXLT.createExpression("\\${'world'}");
838 o = expr.evaluate(none);
839 assertEquals("${'world'}", o);
840 }
841
842 @ParameterizedTest
843 @MethodSource("engines")
844 void testEscapeString(final JexlBuilder builder) {
845 init(builder);
846 final JxltEngine.Expression expr = JXLT.createExpression("\\\"${'world\\'s finest'}\\\"");
847 final JexlContext none = null;
848 final Object o = expr.evaluate(none);
849 assertEquals("\"world's finest\"", o);
850 }
851
852 @ParameterizedTest
853 @MethodSource("engines")
854 void testImmediate(final JexlBuilder builder) {
855 init(builder);
856 final JexlContext none = null;
857 final String source = "${'Hello ' + 'World!'}";
858 final JxltEngine.Expression expr = JXLT.createExpression(source);
859 final JxltEngine.Expression prepared = expr.prepare(none);
860 assertEquals("Hello World!", prepared.asString(), "prepare should return same expression");
861 final Object o = expr.evaluate(none);
862 assertTrue(expr.isImmediate(), "expression should be immediate");
863 assertEquals("Hello World!", o);
864
865 assertEquals(source, getSource(expr.toString()));
866 }
867
868 @ParameterizedTest
869 @MethodSource("engines")
870 void testImmediateTemplate(final JexlBuilder builder) {
871 init(builder);
872 context.set("tables", new String[]{"table1", "table2"});
873 context.set("w" ,"x=1");
874
875 final JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader(
876 "select * from \n"+
877 "$$var comma = false; \n"+
878 "$$for(var c : tables) { \n"+
879 "$$ if (comma) $jexl.write(','); else comma = true;\n"+
880 "${c}"+
881 "\n$$}\n"+
882 "where ${w}\n"
883 ));
884
885 final StringWriter strw = new StringWriter();
886
887 t.evaluate(context, strw);
888 final String output = strw.toString();
889 assertTrue(output.contains("table1") && output.contains("table2"));
890 }
891 @ParameterizedTest
892 @MethodSource("engines")
893 void testInheritedDebugger(final JexlBuilder builder) {
894 init(builder);
895 final String src = "if ($A) { $B + 1; } else { $C - 2 }";
896 final JexlEngine jexl = JXLT.getEngine();
897 final JexlScript script = jexl.createScript(src);
898
899 final Debugger sd = new Debugger();
900 final String rscript = sd.debug(script)? sd.toString() : null;
901 assertNotNull(rscript);
902
903 final TemplateDebugger td = new TemplateDebugger();
904 final String refactored = td.debug(script)? td.toString() : null;
905 assertNotNull(refactored);
906 assertEquals(refactored, rscript);
907 }
908
909 @ParameterizedTest
910 @MethodSource("engines")
911 void testInterpolation(final JexlBuilder builder) {
912 init(builder);
913 final String expr = "`Hello \n${user}`";
914 final JexlScript script = ENGINE.createScript(expr);
915 context.set("user", "Dimitri");
916 Object value = script.execute(context);
917 assertEquals("Hello \nDimitri", value, expr);
918 context.set("user", "Rahul");
919 value = script.execute(context);
920 assertEquals("Hello \nRahul", value, expr);
921 }
922
923 @ParameterizedTest
924 @MethodSource("engines")
925 void testInterpolationGlobal(final JexlBuilder builder) {
926 init(builder);
927 if (isLexicalShade()) {
928 context.set("user", null);
929 }
930 final String expr = "user='Dimitri'; `Hello \n${user}`";
931 final Object value = ENGINE.createScript(expr).execute(context);
932 assertEquals("Hello \nDimitri", value, expr);
933 }
934
935 @ParameterizedTest
936 @MethodSource("engines")
937 void testInterpolationLocal(final JexlBuilder builder) {
938 init(builder);
939 final String expr = "var user='Henrib'; `Hello \n${user}`";
940 final Object value = ENGINE.createScript(expr).execute(context);
941 assertEquals("Hello \nHenrib", value, expr);
942 }
943
944 @ParameterizedTest
945 @MethodSource("engines")
946 void testInterpolationLvsG(final JexlBuilder builder) {
947 init(builder);
948 if (isLexicalShade()) {
949 context.set("user", null);
950 }
951 final String expr = "user='Dimitri'; var user='Henrib'; `H\\\"ello \n${user}`";
952 final Object value = ENGINE.createScript(expr).execute(context);
953 assertEquals("H\"ello \nHenrib", value, expr);
954 }
955
956 @ParameterizedTest
957 @MethodSource("engines")
958 void testInterpolationLvsG2(final JexlBuilder builder) {
959 init(builder);
960 if (isLexicalShade()) {
961 context.set("user", null);
962 }
963 final String expr = "user='Dimitri'; var user='Henrib'; `H\\`ello \n${user}`";
964 final Object value = ENGINE.createScript(expr).execute(context);
965 assertEquals("H`ello \nHenrib", value, expr);
966 }
967
968 @ParameterizedTest
969 @MethodSource("engines")
970 void testInterpolationParameter(final JexlBuilder builder) {
971 init(builder);
972 final String expr = "(user)->{`Hello \n${user}`}";
973 final JexlScript script = ENGINE.createScript(expr);
974 Object value = script.execute(context, "Henrib");
975 assertEquals("Hello \nHenrib", value, expr);
976 value = ENGINE.createScript(expr).execute(context, "Dimitri");
977 assertEquals("Hello \nDimitri", value, expr);
978 }
979
980 @ParameterizedTest
981 @MethodSource("engines")
982 void testLexicalTemplate(final JexlBuilder builder) {
983 init(builder);
984 final JexlOptions opts = new JexlOptions();
985 final JexlContext ctxt = new PragmaticContext(opts);
986 opts.setCancellable(false);
987 opts.setStrict(false);
988 opts.setSafe(true);
989 opts.setLexical(false);
990 opts.setLexicalShade(false);
991
992 final String src0 = "${$options.strict?'+':'-'}strict"
993 + " ${$options.cancellable?'+':'-'}cancellable"
994 + " ${$options.lexical?'+':'-'}lexical"
995 + " ${$options.lexicalShade?'+':'-'}lexicalShade"
996 + " ${$options.safe?'+':'-'}safe";
997
998 final JxltEngine.Template tmplt0 = JXLT.createTemplate("$$", new StringReader(src0));
999 final Writer strw0 = new StringWriter();
1000 tmplt0.evaluate(ctxt, strw0);
1001 final String output0 = strw0.toString();
1002 final JexlFeatures features = BUILDER.features();
1003 if (features != null && features.isLexical() && features.isLexicalShade()) {
1004 assertEquals("-strict -cancellable +lexical +lexicalShade +safe", output0);
1005 } else {
1006 assertEquals("-strict -cancellable -lexical -lexicalShade +safe", output0);
1007 }
1008
1009 final String src = "$$ #pragma script.mode pro50\n" + src0;
1010
1011 final JxltEngine.Template tmplt = JXLT.createTemplate("$$", new StringReader(src));
1012 final Writer strw = new StringWriter();
1013 tmplt.evaluate(ctxt, strw);
1014 final String output = strw.toString();
1015 assertEquals("+strict +cancellable +lexical +lexicalShade -safe", output);
1016 }
1017
1018 @ParameterizedTest
1019 @MethodSource("engines")
1020 void testMalformed(final JexlBuilder builder) {
1021 init(builder);
1022 final JxltEngine.Exception xjexl = assertThrows(JxltEngine.Exception.class, () -> JXLT.createExpression("${'world'"), "should be malformed");
1023 LOGGER.debug(xjexl.getMessage());
1024 }
1025
1026 @ParameterizedTest
1027 @MethodSource("engines")
1028 void testMalformedNested(final JexlBuilder builder) {
1029 init(builder);
1030 final JxltEngine.Exception xjexl = assertThrows(JxltEngine.Exception.class, () -> JXLT.createExpression("#{${hi} world}"), "should be malformed");
1031 LOGGER.debug(xjexl.getMessage());
1032 }
1033
1034 @ParameterizedTest
1035 @MethodSource("engines")
1036 void testMalformedNested2(final JexlBuilder builder) {
1037 init(builder);
1038 final JxltEngine.Exception xjexl = assertThrows(JxltEngine.Exception.class, () -> JXLT.createExpression("#{${hi} world}"), "should be malformed");
1039 LOGGER.debug(xjexl.getMessage());
1040 }
1041
1042 @ParameterizedTest
1043 @MethodSource("engines")
1044 void testNested(final JexlBuilder builder) {
1045 init(builder);
1046 final String source = "#{${hi}+'.world'}";
1047 final JxltEngine.Expression expr = JXLT.createExpression(source);
1048
1049 final Set<List<String>> evars = expr.getVariables();
1050 assertEquals(1, evars.size());
1051 assertTrue(contains(evars, Collections.singletonList("hi")));
1052
1053 context.set("hi", "greeting");
1054 context.set("greeting.world", "Hello World!");
1055 assertTrue(expr.isDeferred(), "expression should be deferred");
1056 final Object o = expr.evaluate(context);
1057 assertEquals("Hello World!", o);
1058
1059 assertEquals(source, getSource(expr.toString()));
1060 }
1061
1062 @ParameterizedTest
1063 @MethodSource("engines")
1064 void testNestedTemplate(final JexlBuilder builder) {
1065 init(builder);
1066 final String source = "#{${hi}+'.world'}";
1067 final JxltEngine.Template expr = JXLT.createTemplate(source, "hi");
1068
1069 context.set("greeting.world", "Hello World!");
1070 final StringWriter strw = new StringWriter();
1071 expr.evaluate(context, strw, "greeting");
1072 final String o = strw.toString();
1073 assertEquals("Hello World!", o);
1074
1075 assertEquals(source, getSource(expr.toString()));
1076 }
1077
1078 @ParameterizedTest
1079 @MethodSource("engines")
1080 void testNonEscapeString(final JexlBuilder builder) {
1081 init(builder);
1082 final JxltEngine.Expression expr = JXLT.createExpression("c:\\some\\windows\\path");
1083 final JexlContext none = null;
1084 final Object o = expr.evaluate(none);
1085 assertEquals("c:\\some\\windows\\path", o);
1086 }
1087
1088 @ParameterizedTest
1089 @MethodSource("engines")
1090 void testOneLiner(final JexlBuilder builder) {
1091 init(builder);
1092 final JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader("fourty-two"));
1093 final StringWriter strw = new StringWriter();
1094 t.evaluate(context, strw);
1095 final String output = strw.toString();
1096 assertEquals("fourty-two", output);
1097 }
1098
1099 @ParameterizedTest
1100 @MethodSource("engines")
1101 void testOneLinerVar(final JexlBuilder builder) {
1102 init(builder);
1103 final JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader("fourty-${x}"));
1104 final StringWriter strw = new StringWriter();
1105 context.set("x", "two");
1106 t.evaluate(context, strw);
1107 final String output = strw.toString();
1108 assertEquals("fourty-two", output);
1109 }
1110
1111 @ParameterizedTest
1112 @MethodSource("engines")
1113 void testPrepareEvaluate(final JexlBuilder builder) {
1114 init(builder);
1115 final String source = "Dear #{p} ${name};";
1116 final JxltEngine.Expression expr = JXLT.createExpression("Dear #{p} ${name};");
1117 assertTrue(expr.isDeferred(), "expression should be deferred");
1118
1119 final Set<List<String>> evars = expr.getVariables();
1120 assertEquals(1, evars.size());
1121 assertTrue(contains(evars, Collections.singletonList("name")));
1122 context.set("name", "Doe");
1123 final JxltEngine.Expression phase1 = expr.prepare(context);
1124 final String as = phase1.asString();
1125 assertEquals("Dear ${p} Doe;", as);
1126 final Set<List<String>> evars1 = phase1.getVariables();
1127 assertEquals(1, evars1.size());
1128 assertTrue(contains(evars1, Collections.singletonList("p")));
1129 vars.clear();
1130 context.set("p", "Mr");
1131 context.set("name", "Should not be used in 2nd phase");
1132 final Object o = phase1.evaluate(context);
1133 assertEquals("Dear Mr Doe;", o);
1134
1135 final String p1 = getSource(phase1.toString());
1136 assertEquals(source, getSource(phase1.toString()));
1137 assertEquals(source, getSource(expr.toString()));
1138 }
1139
1140 @ParameterizedTest
1141 @MethodSource("engines")
1142 void testPrepareTemplate(final JexlBuilder builder) {
1143 init(builder);
1144
1145 final String source
1146 = "$$ for(var x : list) {\n"
1147 + "${l10n}=#{x}\n"
1148 + "$$ }\n";
1149
1150 final int[] args = {42};
1151 final JxltEngine.Template tl10n = JXLT.createTemplate(source, "list");
1152 final String dstr = tl10n.asString();
1153 assertNotNull(dstr);
1154 final Set<List<String>> vars = tl10n.getVariables();
1155 assertFalse(vars.isEmpty());
1156 context.set("l10n", "valeur");
1157 final JxltEngine.Template tpFR = tl10n.prepare(context);
1158 context.set("l10n", "value");
1159 final JxltEngine.Template tpEN = tl10n.prepare(context);
1160 context.set("l10n", null);
1161
1162 StringWriter strw;
1163 strw = new StringWriter();
1164 tpFR.evaluate(context, strw, args);
1165 final String outFR = strw.toString();
1166 assertEquals("valeur=42\n", outFR);
1167
1168 context.set("l10n", null);
1169 strw = new StringWriter();
1170 tpEN.evaluate(context, strw, args);
1171 final String outEN = strw.toString();
1172 assertEquals("value=42\n", outEN);
1173 }
1174
1175 @ParameterizedTest
1176 @MethodSource("engines")
1177 void testReport(final JexlBuilder builder) {
1178 init(builder);
1179
1180 final String rpt
1181 = "<report>\n"
1182 + "\n"
1183 + "\n$$ var a = 1;"
1184 + "\n$$ var x = 2;"
1185 + "\n"
1186 + "\n$$ var y = 9;"
1187 + "\n"
1188 + "\n ${x + y}"
1189 + "\n</report>\n";
1190
1191 final JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader(rpt));
1192 final StringWriter strw = new StringWriter();
1193 t.evaluate(context, strw);
1194 final String output = strw.toString();
1195 final String ctl = "<report>\n\n\n\n\n 11\n</report>\n";
1196 assertEquals(ctl, output);
1197
1198 final TemplateDebugger td = new TemplateDebugger();
1199 final String refactored = refactor(td, t);
1200 assertNotNull(refactored);
1201 assertEquals(rpt, refactored);
1202 }
1203
1204 @ParameterizedTest
1205 @MethodSource("engines")
1206 void testReport1(final JexlBuilder builder) {
1207 init(builder);
1208
1209 final String rpt
1210 = "<report>\n"
1211 + "this is ${x}\n"
1212 + "${x + 1}\n"
1213 + "${x + 2}\n"
1214 + "${x + 3}\n"
1215 + "</report>\n";
1216
1217 final JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader(rpt));
1218 final StringWriter strw = new StringWriter();
1219 context.set("x", 42);
1220 t.evaluate(context, strw, 42);
1221 final String output = strw.toString();
1222 int count = 0;
1223 for (int i = 0; i < output.length(); ++i) {
1224 final char c = output.charAt(i);
1225 if ('\n' == c) {
1226 count += 1;
1227 }
1228 }
1229 assertEquals(6, count);
1230 assertTrue(output.indexOf("42") > 0);
1231 assertTrue(output.indexOf("43") > 0);
1232 assertTrue(output.indexOf("44") > 0);
1233 assertTrue(output.indexOf("45") > 0);
1234 }
1235
1236 @ParameterizedTest
1237 @MethodSource("engines")
1238 void testReport2(final JexlBuilder builder) {
1239 init(builder);
1240
1241 final String rpt
1242 = "<report>\n"
1243 + "this is ${x}\n"
1244 + "${x + 1}\n"
1245 + "${x + 2}\n"
1246 + "${x + 3}\n"
1247 + "</report>\n";
1248
1249 final JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader(rpt), "x");
1250 final StringWriter strw = new StringWriter();
1251 t.evaluate(context, strw, 42);
1252 final String output = strw.toString();
1253 int count = 0;
1254 for (int i = 0; i < output.length(); ++i) {
1255 final char c = output.charAt(i);
1256 if ('\n' == c) {
1257 count += 1;
1258 }
1259 }
1260 assertEquals(6, count);
1261 assertTrue(output.indexOf("42") > 0);
1262 assertTrue(output.indexOf("43") > 0);
1263 assertTrue(output.indexOf("44") > 0);
1264 assertTrue(output.indexOf("45") > 0);
1265
1266 final TemplateDebugger td = new TemplateDebugger();
1267 final String xxx = refactor(td, t);
1268 assertNotNull(xxx);
1269 assertEquals(rpt, xxx);
1270 }
1271
1272 @ParameterizedTest
1273 @MethodSource("engines")
1274 void testSanboxed311i(final JexlBuilder builder) {
1275 init(builder);
1276
1277 final Uberspect uberspect = new Uberspect(LogFactory.getLog(JXLTTest.class), null, NOJEXL3);
1278 final Method method = uberspect.getMethod(TemplateInterpreter.class, "print", new Object[]{Integer.TYPE});
1279 final JexlEngine jexl= new JexlBuilder().uberspect(uberspect).create();
1280 final JxltEngine jxlt = jexl.createJxltEngine();
1281 final JexlContext ctx311 = new Context311();
1282
1283 final String rpt
1284 = "$$var u = 'Universe'; exec('4').execute((a, b)->{"
1285 + "\n<p>${u} ${a}${b}</p>"
1286 + "\n$$}, '2')";
1287
1288 final JxltEngine.Template t = jxlt.createTemplate("$$", new StringReader(rpt));
1289 final StringWriter strw = new StringWriter();
1290 t.evaluate(ctx311, strw, 42);
1291 final String output = strw.toString();
1292 assertEquals("<p>Universe 42</p>\n", output);
1293 }
1294
1295 @ParameterizedTest
1296 @MethodSource("engines")
1297 void testSanboxedTemplate(final JexlBuilder builder) {
1298 init(builder);
1299 final String src = "Hello ${user}";
1300 final JexlContext ctxt = new MapContext();
1301 ctxt.set("user", "Francesco");
1302
1303 final Uberspect uberspect = new Uberspect(LogFactory.getLog(JXLTTest.class), null, NOJEXL3);
1304 final Method method = uberspect.getMethod(TemplateInterpreter.class, "print", new Object[]{Integer.TYPE});
1305 assertNull(method);
1306
1307 final JexlEngine jexl= new JexlBuilder().uberspect(uberspect).create();
1308 final JxltEngine jxlt = jexl.createJxltEngine();
1309
1310 final JxltEngine.Template tmplt = jxlt.createTemplate(src);
1311 final Writer strw = new StringWriter();
1312 tmplt.evaluate(ctxt, strw);
1313 final String result = strw.toString();
1314 assertEquals("Hello Francesco", result);
1315 }
1316
1317 @ParameterizedTest
1318 @MethodSource("engines")
1319 void testStatement(final JexlBuilder builder) {
1320 init(builder);
1321 final Froboz froboz = new Froboz(32);
1322 context.set("froboz", froboz);
1323 final JxltEngine.Expression check = JXLT.createExpression("${ froboz.plus10() }");
1324 final Object o = check.evaluate(context);
1325 assertEquals(Integer.valueOf(32), o);
1326 assertEquals(42, froboz.getValue());
1327 final Set<List<String>> evars = check.getVariables();
1328 assertEquals(1, evars.size());
1329 }
1330
1331 @ParameterizedTest
1332 @MethodSource("engines")
1333 void testTemplate0(final JexlBuilder builder) {
1334 init(builder);
1335 final String source = " $$ if(x) {\nx is ${x}\n $$ } else {\n${'no x'}\n$$ }\n";
1336 StringWriter strw;
1337 String output;
1338
1339 final JxltEngine.Template t = JXLT.createTemplate(source);
1340
1341 context.set("x", 42);
1342 strw = new StringWriter();
1343 t.evaluate(context, strw);
1344 output = strw.toString();
1345 assertEquals("x is 42\n", output);
1346
1347 strw = new StringWriter();
1348 context.set("x", "");
1349 t.evaluate(context, strw);
1350 output = strw.toString();
1351 assertEquals("no x\n", output);
1352
1353 final String dstr = t.toString();
1354 assertNotNull(dstr);
1355 }
1356
1357 @ParameterizedTest
1358 @MethodSource("engines")
1359 void testTemplate1(final JexlBuilder builder) {
1360 init(builder);
1361 final String source = "$$ if(x) {\nx is ${x}\n$$ } else {\n${'no x'}\n$$ }\n";
1362 StringWriter strw;
1363 String output;
1364
1365 final JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader(source), "x");
1366 final String dstr = t.asString();
1367 assertNotNull(dstr);
1368
1369 strw = new StringWriter();
1370 t.evaluate(context, strw, 42);
1371 output = strw.toString();
1372 assertEquals("x is 42\n", output);
1373
1374 strw = new StringWriter();
1375 t.evaluate(context, strw, "");
1376 output = strw.toString();
1377 assertEquals("no x\n", output);
1378 }
1379
1380 @ParameterizedTest
1381 @MethodSource("engines")
1382 void testTemplate10(final JexlBuilder builder) {
1383 init(builder);
1384 final String source = "$$(x)->{ if(x) {\nx is ${x}\n$$ } else {\n${'no x'}\n$$ } }\n";
1385 StringWriter strw;
1386 String output;
1387
1388 final JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader(source), (String[]) null);
1389 final String dstr = t.asString();
1390 assertNotNull(dstr);
1391
1392 final String[] ps = t.getParameters();
1393 assertTrue(Arrays.asList(ps).contains("x"));
1394
1395 strw = new StringWriter();
1396 t.evaluate(context, strw, 42);
1397 output = strw.toString();
1398 assertEquals("x is 42\n", output);
1399 }
1400
1401 @ParameterizedTest
1402 @MethodSource("engines")
1403 void testTemplate2(final JexlBuilder builder) {
1404 init(builder);
1405 final String source = "The answer: ${x}";
1406 StringWriter strw;
1407 String output;
1408
1409 final JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader(source), "x");
1410 final String dstr = t.asString();
1411 assertNotNull(dstr);
1412
1413 strw = new StringWriter();
1414 t.evaluate(context, strw, 42);
1415 output = strw.toString();
1416 assertEquals("The answer: 42", output);
1417 }
1418
1419 @ParameterizedTest
1420 @MethodSource("engines")
1421 void testTemplateOutOfScope(final JexlBuilder builder) {
1422 init(builder);
1423 final JexlOptions opts = new JexlOptions();
1424 opts.setCancellable(false);
1425 opts.setStrict(false);
1426 opts.setLexical(false);
1427 opts.setLexicalShade(false);
1428 opts.setSharedInstance(true);
1429 final JexlContext ctxt = new PragmaticContext(opts);
1430 final String src = "$$if (false) { var tab = 42; }\n" + "${tab}";
1431 JxltEngine.Template tmplt;
1432 final JexlFeatures features = BUILDER.features();
1433 try {
1434 tmplt = JXLT.createTemplate("$$", new StringReader(src));
1435 } catch (final JexlException xparse) {
1436 if (features != null && features.isLexicalShade()) {
1437 return;
1438 }
1439 throw xparse;
1440 }
1441 final Writer strw = new StringWriter();
1442 opts.setSafe(true);
1443 assertDoesNotThrow(() -> tmplt.evaluate(ctxt, strw), "safe should prevent local shade");
1444 assertTrue(strw.toString().isEmpty());
1445 opts.setStrict(true);
1446 opts.setSafe(false);
1447 final JexlException.Variable xvar = assertThrows(JexlException.Variable.class, () -> tmplt.evaluate(ctxt, strw));
1448 assertTrue("tab".equals(xvar.getVariable()));
1449 assertTrue(xvar.isUndefined());
1450 }
1451
1452 @ParameterizedTest
1453 @MethodSource("engines")
1454 void testTemplatePragmaPro50(final JexlBuilder builder) {
1455 init(builder);
1456 final JexlOptions opts = new JexlOptions();
1457 opts.setCancellable(false);
1458 opts.setStrict(false);
1459 opts.setSafe(true);
1460 opts.setLexical(false);
1461 opts.setLexicalShade(false);
1462 opts.setSharedInstance(true);
1463 final JexlContext ctxt = new PragmaticContext(opts);
1464
1465 final String src = "$$ #pragma script.mode pro50\n"
1466 + "$$ var tab = null;\n"
1467 + "$$ tab.dummy();";
1468
1469 final JxltEngine.Template tmplt = JXLT.createTemplate("$$", new StringReader(src));
1470 final Writer strw = new StringWriter();
1471 final JexlException.Variable xvar = assertThrows(JexlException.Variable.class, () -> tmplt.evaluate(ctxt, strw));
1472 assertEquals("tab", xvar.getVariable());
1473 assertFalse(xvar.isUndefined());
1474 }
1475
1476 @ParameterizedTest
1477 @MethodSource("engines")
1478 void testWriter(final JexlBuilder builder) {
1479 init(builder);
1480 final Froboz froboz = new Froboz(42);
1481 final Writer writer = new FrobozWriter(new StringWriter());
1482 final JxltEngine.Template t = JXLT.createTemplate("$$", new StringReader("$$$jexl.print(froboz)"), "froboz");
1483 t.evaluate(context, writer, froboz);
1484 assertEquals("froboz{42}", writer.toString());
1485 }
1486 }