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