1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jexl3.internal;
18
19 import org.apache.commons.jexl3.JexlArithmetic;
20 import org.apache.commons.jexl3.JexlBuilder;
21 import org.apache.commons.jexl3.JexlContext;
22 import org.apache.commons.jexl3.JexlEngine;
23 import org.apache.commons.jexl3.JexlException;
24 import org.apache.commons.jexl3.JexlFeatures;
25 import org.apache.commons.jexl3.JexlInfo;
26 import org.apache.commons.jexl3.JexlOptions;
27 import org.apache.commons.jexl3.JexlScript;
28 import org.apache.commons.jexl3.internal.introspection.SandboxUberspect;
29 import org.apache.commons.jexl3.internal.introspection.Uberspect;
30 import org.apache.commons.jexl3.introspection.JexlMethod;
31 import org.apache.commons.jexl3.introspection.JexlPermissions;
32 import org.apache.commons.jexl3.introspection.JexlSandbox;
33 import org.apache.commons.jexl3.introspection.JexlUberspect;
34 import org.apache.commons.jexl3.parser.ASTArrayAccess;
35 import org.apache.commons.jexl3.parser.ASTFunctionNode;
36 import org.apache.commons.jexl3.parser.ASTIdentifier;
37 import org.apache.commons.jexl3.parser.ASTIdentifierAccess;
38 import org.apache.commons.jexl3.parser.ASTJexlScript;
39 import org.apache.commons.jexl3.parser.ASTMethodNode;
40 import org.apache.commons.jexl3.parser.ASTNumberLiteral;
41 import org.apache.commons.jexl3.parser.ASTStringLiteral;
42 import org.apache.commons.jexl3.parser.JexlNode;
43 import org.apache.commons.jexl3.parser.Parser;
44 import org.apache.commons.jexl3.parser.StringProvider;
45 import org.apache.commons.logging.Log;
46 import org.apache.commons.logging.LogFactory;
47
48 import java.nio.charset.Charset;
49 import java.util.ArrayList;
50 import java.util.Collections;
51 import java.util.LinkedHashMap;
52 import java.util.LinkedHashSet;
53 import java.util.List;
54 import java.util.Map;
55 import java.util.Set;
56 import java.util.concurrent.atomic.AtomicBoolean;
57 import java.util.function.Consumer;
58 import java.util.function.Predicate;
59
60 import static org.apache.commons.jexl3.parser.JexlParser.PRAGMA_IMPORT;
61 import static org.apache.commons.jexl3.parser.JexlParser.PRAGMA_MODULE;
62 import static org.apache.commons.jexl3.parser.JexlParser.PRAGMA_JEXLNS;
63 import static org.apache.commons.jexl3.parser.JexlParser.PRAGMA_OPTIONS;
64
65
66
67
68
69 public class Engine extends JexlEngine {
70
71
72
73
74
75
76
77
78 private static final class UberspectHolder {
79
80 static final Uberspect UBERSPECT =
81 new Uberspect(LogFactory.getLog(JexlEngine.class),
82 JexlUberspect.JEXL_STRATEGY,
83 JexlPermissions.parse());
84
85
86 private UberspectHolder() {}
87 }
88
89
90
91 protected final Log logger;
92
93
94
95 protected final JexlUberspect uberspect;
96
97
98
99 protected final JexlArithmetic arithmetic;
100
101
102
103 protected final Map<String, Object> functions;
104
105
106
107 protected final FqcnResolver classNameSolver;
108
109
110
111 protected final int stackOverflow;
112
113
114
115 protected final boolean strict;
116
117
118
119 protected final boolean safe;
120
121
122
123
124 protected final boolean silent;
125
126
127
128
129
130 protected final boolean cancellable;
131
132
133
134 protected final boolean debug;
135
136
137
138 protected final JexlFeatures scriptFeatures;
139
140
141
142 protected final JexlFeatures expressionFeatures;
143
144
145
146 protected final Charset charset;
147
148
149
150 protected final AtomicBoolean parsing = new AtomicBoolean(false);
151
152
153
154
155 protected final Parser parser = new Parser(new StringProvider(";"));
156
157
158
159 protected final int cacheThreshold;
160
161
162
163 protected final SoftCache<Source, ASTJexlScript> cache;
164
165
166
167 protected volatile TemplateEngine jxlt = null;
168
169
170
171 protected final int collectMode;
172
173
174
175 protected final JexlOptions options;
176
177
178
179
180 public Engine() {
181 this(new JexlBuilder());
182 }
183
184
185
186
187
188 public Engine(final JexlBuilder conf) {
189
190 this.options = conf.options().copy();
191 this.strict = options.isStrict();
192 this.safe = options.isSafe();
193 this.silent = options.isSilent();
194 this.cancellable = option(conf.cancellable(), !silent && strict);
195 options.setCancellable(cancellable);
196 this.debug = option(conf.debug(), true);
197 this.collectMode = conf.collectMode();
198 this.stackOverflow = conf.stackOverflow() > 0? conf.stackOverflow() : Integer.MAX_VALUE;
199
200 final JexlUberspect uber = conf.uberspect() == null
201 ? getUberspect(conf.logger(), conf.strategy(), conf.permissions())
202 : conf.uberspect();
203 final ClassLoader loader = conf.loader();
204 if (loader != null) {
205 uber.setClassLoader(loader);
206 }
207 final JexlSandbox sandbox = conf.sandbox();
208 if (sandbox == null) {
209 this.uberspect = uber;
210 } else {
211 this.uberspect = new SandboxUberspect(uber, sandbox);
212 }
213 this.logger = conf.logger() == null ? LogFactory.getLog(JexlEngine.class) : conf.logger();
214 this.arithmetic = conf.arithmetic() == null ? new JexlArithmetic(this.strict) : conf.arithmetic();
215 options.setMathContext(arithmetic.getMathContext());
216 options.setMathScale(arithmetic.getMathScale());
217 options.setStrictArithmetic(arithmetic.isStrict());
218 final Map<String, Object> ns = conf.namespaces();
219 this.functions = ns == null || ns.isEmpty()? Collections.emptyMap() : ns;
220 this.classNameSolver = new FqcnResolver(uberspect, conf.imports());
221
222 final JexlFeatures features = conf.features() == null? DEFAULT_FEATURES : conf.features();
223 Predicate<String> nsTest = features.namespaceTest();
224 final Set<String> nsNames = functions.keySet();
225 if (!nsNames.isEmpty()) {
226 nsTest = nsTest == JexlFeatures.TEST_STR_FALSE ?nsNames::contains : nsTest.or(nsNames::contains);
227 }
228 this.expressionFeatures = new JexlFeatures(features).script(false).namespaceTest(nsTest);
229 this.scriptFeatures = new JexlFeatures(features).script(true).namespaceTest(nsTest);
230 this.charset = conf.charset();
231
232 this.cache = conf.cache() <= 0 ? null : new SoftCache<>(conf.cache());
233 this.cacheThreshold = conf.cacheThreshold();
234 if (uberspect == null) {
235 throw new IllegalArgumentException("uberspect can not be null");
236 }
237 }
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252 public static Uberspect getUberspect(
253 final Log logger,
254 final JexlUberspect.ResolverStrategy strategy,
255 final JexlPermissions permissions) {
256 if ((logger == null || logger.equals(LogFactory.getLog(JexlEngine.class)))
257 && (strategy == null || strategy == JexlUberspect.JEXL_STRATEGY)
258 && (permissions == null || permissions == JexlPermissions.UNRESTRICTED)) {
259 return UberspectHolder.UBERSPECT;
260 }
261 return new Uberspect(logger, strategy, permissions);
262 }
263
264
265
266
267
268
269
270
271 @Deprecated
272 public static Uberspect getUberspect(final Log logger, final JexlUberspect.ResolverStrategy strategy) {
273 return getUberspect(logger, strategy, null);
274 }
275
276 @Override
277 public JexlUberspect getUberspect() {
278 return uberspect;
279 }
280
281 @Override
282 public JexlArithmetic getArithmetic() {
283 return arithmetic;
284 }
285
286 @Override
287 public boolean isDebug() {
288 return this.debug;
289 }
290
291 @Override
292 public boolean isSilent() {
293 return this.silent;
294 }
295
296 @Override
297 public boolean isStrict() {
298 return this.strict;
299 }
300
301 @Override
302 public boolean isCancellable() {
303 return this.cancellable;
304 }
305
306 @Override
307 public void setClassLoader(final ClassLoader loader) {
308 jxlt = null;
309 uberspect.setClassLoader(loader);
310 if (functions != null) {
311 final Iterable<String> names = new ArrayList<>(functions.keySet());
312 for(final String name : names) {
313 final Object functor = functions.get(name);
314 if (functor instanceof Class<?>) {
315 final Class<?> fclass = ((Class<?>) functor);
316 try {
317 final Class<?> nclass = loader.loadClass(fclass.getName());
318 if (nclass != fclass) {
319 functions.put(name, nclass);
320 }
321 } catch (final ClassNotFoundException xany) {
322 functions.put(name, fclass.getName());
323 }
324 }
325 }
326 }
327 if (cache != null) {
328 cache.clear();
329 }
330 }
331
332 @Override
333 public Charset getCharset() {
334 return charset;
335 }
336
337
338
339
340
341
342 final Object getNamespace(String name) {
343 return functions.get(name);
344 }
345
346
347
348
349
350
351
352
353 private static <T> T option(final T conf, final T def) {
354 return conf == null? def : conf;
355 }
356
357
358
359
360
361
362
363
364
365 protected JexlOptions evalOptions(final JexlContext context) {
366
367 if (context instanceof JexlContext.OptionsHandle) {
368 final JexlOptions jexlo = ((JexlContext.OptionsHandle) context).getEngineOptions();
369 if (jexlo != null) {
370 return jexlo.isSharedInstance()? jexlo : jexlo.copy();
371 }
372 } else if (context instanceof JexlEngine.Options) {
373 return evalOptions((JexlEngine.Options) context);
374 }
375 return options;
376 }
377
378
379
380
381
382
383 private JexlOptions evalOptions(final JexlEngine.Options opts) {
384
385 final JexlOptions jexlo = options.copy();
386 final JexlEngine jexl = this;
387 jexlo.setCancellable(option(opts.isCancellable(), jexl.isCancellable()));
388 jexlo.setSilent(option(opts.isSilent(), jexl.isSilent()));
389 jexlo.setStrict(option(opts.isStrict(), jexl.isStrict()));
390 final JexlArithmetic jexla = jexl.getArithmetic();
391 jexlo.setStrictArithmetic(option(opts.isStrictArithmetic(), jexla.isStrict()));
392 jexlo.setMathContext(opts.getArithmeticMathContext());
393 jexlo.setMathScale(opts.getArithmeticMathScale());
394 return jexlo;
395 }
396
397
398
399
400
401
402
403
404 protected JexlOptions evalOptions(final ASTJexlScript script, final JexlContext context) {
405 final JexlOptions opts = evalOptions(context);
406 if (opts != options) {
407
408 if (scriptFeatures.isLexical()) {
409 opts.setLexical(true);
410 }
411 if (scriptFeatures.isLexicalShade()) {
412 opts.setLexicalShade(true);
413 }
414 }
415 if (script != null) {
416
417 processPragmas(script, context, opts);
418 }
419 return opts;
420 }
421
422
423
424
425
426
427
428
429 protected void processPragmas(final ASTJexlScript script, final JexlContext context, final JexlOptions opts) {
430 final Map<String, Object> pragmas = script.getPragmas();
431 if (pragmas != null && !pragmas.isEmpty()) {
432 final JexlContext.PragmaProcessor processor =
433 context instanceof JexlContext.PragmaProcessor
434 ? (JexlContext.PragmaProcessor) context
435 : null;
436 Map<String, Object> ns = null;
437 for (final Map.Entry<String, Object> pragma : pragmas.entrySet()) {
438 final String key = pragma.getKey();
439 final Object value = pragma.getValue();
440 if (PRAGMA_OPTIONS.equals(key)) {
441 if (value instanceof String) {
442
443 final String[] vs = value.toString().split(" ");
444 opts.setFlags(vs);
445 }
446 } else if (PRAGMA_IMPORT.equals(key)) {
447
448 final Set<String> is = new LinkedHashSet<>();
449 withValueSet(value, (o)->{
450 if (o instanceof String) {
451 is.add(o.toString());
452 }
453 });
454 if (!is.isEmpty()) {
455 opts.setImports(is);
456 }
457 } else if (key.startsWith(PRAGMA_JEXLNS)) {
458 if (ns == null) {
459 ns = new LinkedHashMap<>();
460 }
461 processPragmaNamespace(ns, key, value);
462 if (!ns.isEmpty()) {
463 opts.setNamespaces(ns);
464 }
465 } else if (key.startsWith(PRAGMA_MODULE)) {
466 if (ns == null) {
467 ns = new LinkedHashMap<>();
468 }
469 processPragmaModule(ns, key, value, script.jexlInfo(), context);
470 if (!ns.isEmpty()) {
471 opts.setNamespaces(ns);
472 }
473 }
474
475 if (processor != null) {
476 processor.processPragma(opts, key, value);
477 }
478 }
479 }
480 }
481
482
483
484
485
486
487 private void withValueSet(Object value, Consumer<Object> consumer) {
488 final Set<?> values = value instanceof Set<?>
489 ? (Set<?>) value
490 : Collections.singleton(value);
491 for (final Object o : values) {
492 consumer.accept(o);
493 }
494 }
495
496
497
498
499
500
501
502 private void processPragmaNamespace(Map<String, Object> ns, String key, Object value) {
503 if (value instanceof String) {
504
505 final String nsname = key.substring(PRAGMA_JEXLNS.length());
506 if (!nsname.isEmpty()) {
507 final String nsclass = value.toString();
508 final Class<?> clazz = uberspect.getClassByName(nsclass);
509 if (clazz == null) {
510 logger.warn(key + ": unable to find class " + nsclass);
511 } else {
512 ns.put(nsname, clazz);
513 }
514 }
515 } else {
516 logger.warn(key + ": ambiguous declaration " + value);
517 }
518 }
519
520
521
522
523
524
525
526
527
528
529
530 private void processPragmaModule(Map<String, Object> ns, String key, Object value, JexlInfo info, JexlContext context) {
531
532 final String module = key.substring(PRAGMA_MODULE.length());
533 if (module.isEmpty()) {
534 logger.warn(module + ": invalid module declaration");
535 } else {
536 withValueSet(value, (o)->{
537 if (!(o instanceof CharSequence)) {
538 logger.warn(module + ": unable to define module from " + value);
539 } else {
540 final String moduleSrc = o.toString();
541 final Object functor;
542 if (context instanceof JexlContext.ModuleProcessor) {
543 final JexlContext.ModuleProcessor processor = (JexlContext.ModuleProcessor) context;
544 functor = processor.processModule(this, info, module, moduleSrc);
545 } else {
546 final Object moduleObject = createExpression(info, moduleSrc).evaluate(context);
547 functor = moduleObject instanceof Script
548 ? ((Script) moduleObject).execute(context)
549 : moduleObject;
550 }
551 if (functor != null) {
552 ns.put(module, functor);
553 } else {
554 ns.remove(module);
555 }
556 }
557 });
558 }
559 }
560
561
562
563
564
565
566 public JexlOptions optionsSet(final JexlOptions opts) {
567 if (opts != null) {
568 opts.set(options);
569 }
570 return opts;
571 }
572
573 @Override
574 public TemplateEngine createJxltEngine(final boolean noScript, final int cacheSize, final char immediate, final char deferred) {
575 return new TemplateEngine(this, noScript, cacheSize, immediate, deferred);
576 }
577
578 @Override
579 public void clearCache() {
580 if (cache != null) {
581 cache.clear();
582 }
583 }
584
585
586
587
588
589
590
591
592 protected Interpreter createInterpreter(final JexlContext context, final Frame frame, final JexlOptions opts) {
593 return new Interpreter(this, opts, context, frame);
594 }
595
596
597
598
599
600 protected Interpreter createTemplateInterpreter(final TemplateInterpreter.Arguments args) {
601 return new TemplateInterpreter(args);
602 }
603
604 @Override
605 public Script createExpression(final JexlInfo info, final String expression) {
606 return createScript(expressionFeatures, info, expression);
607 }
608
609 @Override
610 public Script createScript(final JexlFeatures features, final JexlInfo info, final String scriptText, final String... names) {
611 if (scriptText == null) {
612 throw new NullPointerException("source is null");
613 }
614 final String source = trimSource(scriptText);
615 final Scope scope = names == null || names.length == 0? null : new Scope(null, names);
616 final JexlFeatures ftrs = features == null? scriptFeatures : features;
617 final ASTJexlScript tree = parse(info, ftrs, source, scope);
618 return new Script(this, source, tree);
619 }
620
621
622
623
624 protected static final JexlFeatures PROPERTY_FEATURES = new JexlFeatures()
625 .localVar(false)
626 .loops(false)
627 .lambda(false)
628 .script(false)
629 .arrayReferenceExpr(false)
630 .methodCall(false)
631 .register(true);
632
633 @Override
634 public Object getProperty(final Object bean, final String expr) {
635 return getProperty(null, bean, expr);
636 }
637
638 @Override
639 public Object getProperty(final JexlContext context, final Object bean, final String expr) {
640
641 String src = trimSource(expr);
642 src = "#0" + (src.charAt(0) == '[' ? "" : ".") + src;
643 try {
644 final Scope scope = new Scope(null, "#0");
645 final ASTJexlScript script = parse(null, PROPERTY_FEATURES, src, scope);
646 final JexlNode node = script.jjtGetChild(0);
647 final Frame frame = script.createFrame(bean);
648 final Interpreter interpreter = createInterpreter(context == null? EMPTY_CONTEXT : context, frame, options);
649 return interpreter.visitLexicalNode(node, null);
650 } catch (final JexlException xjexl) {
651 if (silent) {
652 if (logger.isWarnEnabled()) {
653 logger.warn(xjexl.getMessage(), xjexl.getCause());
654 }
655 return null;
656 }
657 throw xjexl.clean();
658 }
659 }
660
661 @Override
662 public void setProperty(final Object bean, final String expr, final Object value) {
663 setProperty(null, bean, expr, value);
664 }
665
666 @Override
667 public void setProperty(final JexlContext context, final Object bean, final String expr, final Object value) {
668
669 String src = trimSource(expr);
670 src = "#0" + (src.charAt(0) == '[' ? "" : ".") + src + "=" + "#1";
671 try {
672 final Scope scope = new Scope(null, "#0", "#1");
673 final ASTJexlScript script = parse(null, PROPERTY_FEATURES, src, scope);
674 final JexlNode node = script.jjtGetChild(0);
675 final Frame frame = script.createFrame(bean, value);
676 final Interpreter interpreter = createInterpreter(context != null? context : EMPTY_CONTEXT, frame, options);
677 interpreter.visitLexicalNode(node, null);
678 } catch (final JexlException xjexl) {
679 if (silent) {
680 if (logger.isWarnEnabled()) {
681 logger.warn(xjexl.getMessage(), xjexl.getCause());
682 }
683 return;
684 }
685 throw xjexl.clean();
686 }
687 }
688
689 @Override
690 public Object invokeMethod(final Object obj, final String meth, final Object... args) {
691 JexlException xjexl = null;
692 Object result = null;
693 final JexlInfo info = debug ? createInfo() : null;
694 try {
695 JexlMethod method = uberspect.getMethod(obj, meth, args);
696 if (method == null && arithmetic.narrowArguments(args)) {
697 method = uberspect.getMethod(obj, meth, args);
698 }
699 if (method != null) {
700 result = method.invoke(obj, args);
701 } else {
702 xjexl = new JexlException.Method(info, meth, args);
703 }
704 } catch (final JexlException xany) {
705 xjexl = xany;
706 } catch (final Exception xany) {
707 xjexl = new JexlException.Method(info, meth, args, xany);
708 }
709 if (xjexl != null) {
710 if (!silent) {
711 throw xjexl.clean();
712 }
713 if (logger.isWarnEnabled()) {
714 logger.warn(xjexl.getMessage(), xjexl.getCause());
715 }
716 }
717 return result;
718 }
719
720 @Override
721 public <T> T newInstance(final Class<? extends T> clazz, final Object... args) {
722 return clazz.cast(doCreateInstance(clazz, args));
723 }
724
725 @Override
726 public Object newInstance(final String clazz, final Object... args) {
727 return doCreateInstance(clazz, args);
728 }
729
730
731
732
733
734
735
736
737 protected Object doCreateInstance(final Object clazz, final Object... args) {
738 JexlException xjexl = null;
739 Object result = null;
740 final JexlInfo info = debug ? createInfo() : null;
741 try {
742 JexlMethod ctor = uberspect.getConstructor(clazz, args);
743 if (ctor == null && arithmetic.narrowArguments(args)) {
744 ctor = uberspect.getConstructor(clazz, args);
745 }
746 if (ctor != null) {
747 result = ctor.invoke(clazz, args);
748 } else {
749 xjexl = new JexlException.Method(info, clazz.toString(), args);
750 }
751 } catch (final JexlException xany) {
752 xjexl = xany;
753 } catch (final Exception xany) {
754 xjexl = new JexlException.Method(info, clazz.toString(), args, xany);
755 }
756 if (xjexl != null) {
757 if (silent) {
758 if (logger.isWarnEnabled()) {
759 logger.warn(xjexl.getMessage(), xjexl.getCause());
760 }
761 return null;
762 }
763 throw xjexl.clean();
764 }
765 return result;
766 }
767
768
769
770
771
772
773 protected JexlContext.ThreadLocal putThreadLocal(final JexlContext.ThreadLocal tls) {
774 final JexlContext.ThreadLocal local = CONTEXT.get();
775 CONTEXT.set(tls);
776 return local;
777 }
778
779
780
781
782
783
784 protected JexlEngine putThreadEngine(final JexlEngine jexl) {
785 final JexlEngine pjexl = ENGINE.get();
786 ENGINE.set(jexl);
787 return pjexl;
788 }
789
790
791
792
793
794
795
796
797
798 protected Set<List<String>> getVariables(final ASTJexlScript script) {
799 final VarCollector collector = varCollector();
800 getVariables(script, script, collector);
801 return collector.collected();
802 }
803
804
805
806
807
808 protected VarCollector varCollector() {
809 return new VarCollector(this.collectMode);
810 }
811
812
813
814
815 protected static class VarCollector {
816
817
818
819 private final Set<List<String>> refs = new LinkedHashSet<>();
820
821
822
823 private List<String> ref = new ArrayList<>();
824
825
826
827 private JexlNode root = null;
828
829
830
831
832
833 final int mode;
834
835
836
837
838
839 protected VarCollector(final int constaa) {
840 mode = constaa;
841 }
842
843
844
845
846
847 public void collect(final JexlNode node) {
848 if (!ref.isEmpty()) {
849 refs.add(ref);
850 ref = new ArrayList<>();
851 }
852 root = node;
853 }
854
855
856
857
858 public boolean isCollecting() {
859 return root instanceof ASTIdentifier;
860 }
861
862
863
864
865
866 public void add(final String name) {
867 ref.add(name);
868 }
869
870
871
872
873 public Set<List<String>> collected() {
874 return refs;
875 }
876 }
877
878
879
880
881
882
883
884 protected void getVariables(final ASTJexlScript script, final JexlNode node, final VarCollector collector) {
885 if (node instanceof ASTIdentifier) {
886 final JexlNode parent = node.jjtGetParent();
887 if (parent instanceof ASTMethodNode || parent instanceof ASTFunctionNode) {
888
889 collector.collect(null);
890 return;
891 }
892 final ASTIdentifier identifier = (ASTIdentifier) node;
893 final int symbol = identifier.getSymbol();
894
895 if (symbol >= 0 && script != null && !script.isCapturedSymbol(symbol)) {
896 collector.collect(null);
897 } else {
898
899 collector.collect(identifier);
900 collector.add(identifier.getName());
901 }
902 } else if (node instanceof ASTIdentifierAccess) {
903 final JexlNode parent = node.jjtGetParent();
904 if (parent instanceof ASTMethodNode || parent instanceof ASTFunctionNode) {
905
906 collector.collect(null);
907 return;
908 }
909
910 if (collector.isCollecting()) {
911 collector.add(((ASTIdentifierAccess) node).getName());
912 }
913 } else if (node instanceof ASTArrayAccess && collector.mode > 0) {
914 final int num = node.jjtGetNumChildren();
915
916 boolean collecting = collector.isCollecting();
917 for (int i = 0; i < num; ++i) {
918 final JexlNode child = node.jjtGetChild(i);
919 if (collecting && child.isConstant()) {
920
921 final boolean collect = collector.mode > 1
922 || (child instanceof ASTStringLiteral || child instanceof ASTNumberLiteral);
923 if (collect) {
924 final String image = child.toString();
925 collector.add(image);
926 }
927 } else {
928 collecting = false;
929 collector.collect(null);
930 getVariables(script, child, collector);
931 collector.collect(null);
932 }
933 }
934 } else {
935 final int num = node.jjtGetNumChildren();
936 for (int i = 0; i < num; ++i) {
937 getVariables(script, node.jjtGetChild(i), collector);
938 }
939 collector.collect(null);
940 }
941 }
942
943
944
945
946
947
948
949 protected String[] getParameters(final JexlScript script) {
950 return script.getParameters();
951 }
952
953
954
955
956
957
958
959 protected String[] getLocalVariables(final JexlScript script) {
960 return script.getLocalVariables();
961 }
962
963
964
965
966
967
968
969
970
971
972
973 protected ASTJexlScript parse(final JexlInfo info, final boolean expr, final String src, final Scope scope) {
974 return parse(info, expr? this.expressionFeatures : this.scriptFeatures, src, scope);
975 }
976
977
978
979
980
981
982
983
984
985
986
987 protected ASTJexlScript parse(final JexlInfo info, final JexlFeatures parsingf, final String src, final Scope scope) {
988 final boolean cached = src.length() < cacheThreshold && cache != null;
989 final JexlFeatures features = parsingf != null? parsingf : DEFAULT_FEATURES;
990
991
992
993 final Source source = cached? new Source(features, src) : null;
994 ASTJexlScript script;
995 if (source != null) {
996 script = cache.get(source);
997 if (script != null) {
998 final Scope f = script.getScope();
999 if ((f == null && scope == null) || (f != null && f.equals(scope))) {
1000 return script;
1001 }
1002 }
1003 }
1004 final JexlInfo ninfo = info == null && debug ? createInfo() : info;
1005
1006 if (parsing.compareAndSet(false, true)) {
1007 try {
1008
1009 script = parser.parse(ninfo, features, src, scope);
1010 } finally {
1011
1012 parsing.set(false);
1013 }
1014 } else {
1015
1016 final Parser lparser = new Parser(new StringProvider(";"));
1017 script = lparser.parse(ninfo, features, src, scope);
1018 }
1019 if (source != null) {
1020 cache.put(source, script);
1021 }
1022 return script;
1023 }
1024
1025
1026
1027
1028
1029
1030 protected String trimSource(final CharSequence str) {
1031 if (str != null) {
1032 int start = 0;
1033 int end = str.length();
1034 if (end > 0) {
1035
1036 while (start < end && Character.isSpaceChar(str.charAt(start))) {
1037 ++start;
1038 }
1039
1040 while (end > start && Character.isSpaceChar(str.charAt(end - 1))) {
1041 --end;
1042 }
1043 return str.subSequence(start, end).toString();
1044 }
1045 return "";
1046 }
1047 return null;
1048 }
1049
1050
1051
1052
1053
1054 protected TemplateEngine jxlt() {
1055 TemplateEngine e = jxlt;
1056 if (e == null) {
1057 synchronized(this) {
1058 e = jxlt;
1059 if (e == null) {
1060 e = new TemplateEngine(this, true, 0, '$', '#');
1061 jxlt = e;
1062 }
1063 }
1064 }
1065 return e;
1066 }
1067 }