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