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