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