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