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 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 final JexlUberspect uber = conf.uberspect() == null
365 ? getUberspect(conf.logger(), conf.strategy(), conf.permissions())
366 : conf.uberspect();
367 if (uber == null) {
368 throw new IllegalArgumentException("uberspect cannot be null");
369 }
370
371 this.options = conf.options().copy();
372 this.strict = options.isStrict();
373 this.safe = options.isSafe();
374 this.silent = options.isSilent();
375 this.cancellable = option(conf.cancellable(), !silent && strict);
376 options.setCancellable(cancellable);
377 this.debug = option(conf.debug(), true);
378 this.collectMode = conf.collectMode();
379 this.stackOverflow = conf.stackOverflow() > 0? conf.stackOverflow() : Integer.MAX_VALUE;
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
408 if (!features.supportsNamespaceInstantiation()) {
409 options.setNamespaceInstantiation(false);
410 }
411 this.charset = conf.charset();
412
413 final IntFunction<JexlCache<?, ?>> factory = conf.cacheFactory();
414 this.metaCache = new MetaCache(factory == null ? SoftCache::new : factory);
415 this.cache = metaCache.createCache(conf.cache());
416 this.cacheThreshold = conf.cacheThreshold();
417 this.parserFactory = conf.parserFactory() == null ?
418 () -> new Parser(new StringProvider(";"))
419 : conf.parserFactory();
420 this.parser = parserFactory.get();
421 }
422
423 @Override
424 public void clearCache() {
425 if (cache != null) {
426 cache.clear();
427 }
428 }
429
430 JexlCache<Source, Object> getCache() {
431 return cache;
432 }
433
434 @Override
435 public Script createExpression(final JexlInfo info, final String expression) {
436 return createScript(expressionFeatures, info, expression);
437 }
438
439
440
441
442
443
444
445
446
447 protected Interpreter createInterpreter(final JexlContext context, final Frame frame, final JexlOptions opts) {
448 return new Interpreter(this, opts, context, frame);
449 }
450
451 @Override
452 public TemplateEngine createJxltEngine(final boolean noScript, final int cacheSize, final char immediate, final char deferred) {
453 return new TemplateEngine(this, noScript, cacheSize, immediate, deferred);
454 }
455
456 @Override
457 public Script createScript(final JexlFeatures features, final JexlInfo info, final String scriptText, final String... names) {
458 Objects.requireNonNull(scriptText, "scriptText");
459 final String source = trimSource(scriptText);
460 final Scope scope = names == null || names.length == 0? null : new Scope(null, names);
461 final JexlFeatures ftrs = features == null ? scriptFeatures : features;
462 final ASTJexlScript tree = parse(info, ftrs, source, scope);
463 return new Script(this, source, tree);
464 }
465
466
467
468
469
470
471 protected Interpreter createTemplateInterpreter(final TemplateInterpreter.Arguments args) {
472 return new TemplateInterpreter(args);
473 }
474
475
476
477
478
479
480
481
482
483 protected Object doCreateInstance(final Object clazz, final Object... args) {
484 JexlException xjexl = null;
485 Object result = null;
486 final JexlInfo info = createInfo();
487 try {
488 JexlMethod ctor = uberspect.getConstructor(clazz, args);
489 if (ctor == null && arithmetic.narrowArguments(args)) {
490 ctor = uberspect.getConstructor(clazz, args);
491 }
492 if (ctor != null) {
493 result = ctor.invoke(clazz, args);
494 } else {
495 xjexl = new JexlException.Method(info, clazz.toString(), args);
496 }
497 } catch (final JexlException xany) {
498 xjexl = xany;
499 } catch (final Exception xany) {
500 xjexl = new JexlException.Method(info, clazz.toString(), args, xany);
501 }
502 if (xjexl != null) {
503 if (silent) {
504 if (logger.isWarnEnabled()) {
505 logger.warn(xjexl.getMessage(), xjexl.getCause());
506 }
507 return null;
508 }
509 throw xjexl.clean();
510 }
511 return result;
512 }
513
514
515
516
517
518
519
520
521
522 protected JexlOptions evalOptions(final ASTJexlScript script, final JexlContext context) {
523 final JexlOptions opts = evalOptions(context);
524 if (opts != options) {
525
526 if (scriptFeatures.isLexical()) {
527 opts.setLexical(true);
528 }
529 if (scriptFeatures.isLexicalShade()) {
530 opts.setLexicalShade(true);
531 }
532 if (scriptFeatures.supportsConstCapture()) {
533 opts.setConstCapture(true);
534 }
535 }
536 if (script != null) {
537
538 processPragmas(script, context, opts);
539 }
540
541 if (opts != options && !scriptFeatures.supportsNamespaceInstantiation()) {
542 opts.setNamespaceInstantiation(false);
543 }
544 return opts;
545 }
546
547
548
549
550
551
552
553
554
555
556 protected JexlOptions evalOptions(final JexlContext context) {
557
558 if (context instanceof JexlContext.OptionsHandle) {
559 final JexlOptions jexlo = ((JexlContext.OptionsHandle) context).getEngineOptions();
560 if (jexlo != null) {
561 return jexlo.isSharedInstance()? jexlo : jexlo.copy();
562 }
563 } else if (context instanceof JexlEngine.Options) {
564 return evalOptions((JexlEngine.Options) context);
565 }
566 return options;
567 }
568
569
570
571
572
573
574
575 private JexlOptions evalOptions(final JexlEngine.Options opts) {
576
577 final JexlOptions jexlo = options.copy();
578 final JexlEngine jexl = this;
579 jexlo.setCancellable(option(opts.isCancellable(), jexl.isCancellable()));
580 jexlo.setSilent(option(opts.isSilent(), jexl.isSilent()));
581 jexlo.setStrict(option(opts.isStrict(), jexl.isStrict()));
582 final JexlArithmetic jexla = jexl.getArithmetic();
583 jexlo.setStrictArithmetic(option(opts.isStrictArithmetic(), jexla.isStrict()));
584 jexlo.setMathContext(opts.getArithmeticMathContext());
585 jexlo.setMathScale(opts.getArithmeticMathScale());
586 return jexlo;
587 }
588
589 @Override
590 public JexlArithmetic getArithmetic() {
591 return arithmetic;
592 }
593
594 @Override
595 public Charset getCharset() {
596 return charset;
597 }
598
599
600
601
602
603
604
605
606
607 @Deprecated()
608 protected String[] getLocalVariables(final JexlScript script) {
609 return script.getLocalVariables();
610 }
611
612
613
614
615
616
617
618 final Object getNamespace(final String name) {
619 return functions.get(name);
620 }
621
622
623
624
625
626
627
628
629
630 @Deprecated
631 protected String[] getParameters(final JexlScript script) {
632 return script.getParameters();
633 }
634
635 @Override
636 public Object getProperty(final JexlContext context, final Object bean, final String expr) {
637
638 String src = trimSource(expr);
639 src = "#0" + (src.charAt(0) == '[' ? "" : ".") + src;
640 try {
641 final Scope scope = new Scope(null, "#0");
642 final ASTJexlScript script = parse(createInfo(), PROPERTY_FEATURES, src, scope);
643 final JexlNode node = script.jjtGetChild(0);
644 final Frame frame = script.createFrame(bean);
645 final Interpreter interpreter = createInterpreter(context == null ? EMPTY_CONTEXT : context, frame, options);
646 return interpreter.visitLexicalNode(node, null);
647 } catch (final JexlException xjexl) {
648 if (silent) {
649 if (logger.isWarnEnabled()) {
650 logger.warn(xjexl.getMessage(), xjexl.getCause());
651 }
652 return null;
653 }
654 throw xjexl.clean();
655 }
656 }
657
658 @Override
659 public Object getProperty(final Object bean, final String expr) {
660 return getProperty(null, bean, expr);
661 }
662
663 @Override
664 public JexlUberspect getUberspect() {
665 return uberspect;
666 }
667
668
669
670
671
672
673
674
675
676
677 protected Set<List<String>> getVariables(final ASTJexlScript script) {
678 final VarCollector collector = varCollector();
679 getVariables(script, script, collector);
680 return collector.collected();
681 }
682
683
684
685
686
687
688
689
690 protected void getVariables(final ASTJexlScript script, final JexlNode node, final VarCollector collector) {
691 if (node instanceof ASTIdentifier) {
692 final JexlNode parent = node.jjtGetParent();
693 if (parent instanceof ASTMethodNode || parent instanceof ASTFunctionNode) {
694
695 collector.collect(null);
696 return;
697 }
698 final ASTIdentifier identifier = (ASTIdentifier) node;
699 final int symbol = identifier.getSymbol();
700
701 if (symbol >= 0 && script != null && !script.isCapturedSymbol(symbol)) {
702 collector.collect(null);
703 } else {
704
705 collector.collect(identifier);
706 collector.add(identifier.getName());
707 }
708 } else if (node instanceof ASTIdentifierAccess) {
709 final JexlNode parent = node.jjtGetParent();
710 if (parent instanceof ASTMethodNode || parent instanceof ASTFunctionNode) {
711
712 collector.collect(null);
713 return;
714 }
715
716 if (collector.isCollecting()) {
717 collector.add(((ASTIdentifierAccess) node).getName());
718 }
719 } else if (node instanceof ASTArrayAccess && collector.mode > 0) {
720 final int num = node.jjtGetNumChildren();
721
722 boolean collecting = collector.isCollecting();
723 for (int i = 0; i < num; ++i) {
724 final JexlNode child = node.jjtGetChild(i);
725 if (collecting && child.isConstant()) {
726
727 final boolean collect = collector.mode > 1
728 || child instanceof ASTStringLiteral || child instanceof ASTNumberLiteral;
729 if (collect) {
730 final String image = child.toString();
731 collector.add(image);
732 }
733 } else {
734 collecting = false;
735 collector.collect(null);
736 getVariables(script, child, collector);
737 collector.collect(null);
738 }
739 }
740 } else {
741 final int num = node.jjtGetNumChildren();
742 for (int i = 0; i < num; ++i) {
743 getVariables(script, node.jjtGetChild(i), collector);
744 }
745 collector.collect(null);
746 }
747 }
748
749 @Override
750 public Object invokeMethod(final Object obj, final String meth, final Object... args) {
751 JexlException xjexl = null;
752 Object result = null;
753 final JexlInfo info = createInfo();
754 try {
755 JexlMethod method = uberspect.getMethod(obj, meth, args);
756 if (method == null && arithmetic.narrowArguments(args)) {
757 method = uberspect.getMethod(obj, meth, args);
758 }
759 if (method != null) {
760 result = method.invoke(obj, args);
761 } else {
762 xjexl = new JexlException.Method(info, meth, args);
763 }
764 } catch (final JexlException xany) {
765 xjexl = xany;
766 } catch (final Exception xany) {
767 xjexl = new JexlException.Method(info, meth, args, xany);
768 }
769 if (xjexl != null) {
770 if (!silent) {
771 throw xjexl.clean();
772 }
773 if (logger.isWarnEnabled()) {
774 logger.warn(xjexl.getMessage(), xjexl.getCause());
775 }
776 }
777 return result;
778 }
779
780 @Override
781 public boolean isCancellable() {
782 return this.cancellable;
783 }
784
785 @Override
786 public boolean isDebug() {
787 return this.debug;
788 }
789
790 @Override
791 public boolean isSilent() {
792 return this.silent;
793 }
794
795 @Override
796 public boolean isStrict() {
797 return this.strict;
798 }
799
800 @Override
801 public <T> T newInstance(final Class<? extends T> clazz, final Object... args) {
802 return clazz.cast(doCreateInstance(clazz, args));
803 }
804
805 @Override
806 public Object newInstance(final String clazz, final Object... args) {
807 return doCreateInstance(clazz, args);
808 }
809
810 @Override
811 public JexlUberspect.ClassConstantResolver createConstantResolver(final Collection<String> imports) {
812 return imports == null || imports.isEmpty()
813 ? classNameSolver
814 : new FqcnResolver(classNameSolver).importPackages(imports);
815 }
816
817
818
819
820
821
822
823 public JexlOptions optionsSet(final JexlOptions opts) {
824 if (opts != null) {
825 opts.set(options);
826 }
827 return opts;
828 }
829
830
831
832
833
834
835
836
837
838
839
840 protected ASTJexlScript parse(final JexlInfo info, final JexlFeatures parsingf, final String src, final Scope scope) {
841 final boolean cached = src.length() < cacheThreshold && cache != null;
842 final JexlFeatures features = parsingf != null ? parsingf : DEFAULT_FEATURES;
843 final Source source = cached ? new Source(features, Scope.getSymbolsMap(scope), src) : null;
844 if (source != null) {
845 final Object c = cache.get(source);
846 if (c instanceof ASTJexlScript) {
847 return (ASTJexlScript) c;
848 }
849 }
850 final JexlInfo ninfo = info != null ? info : createInfo();
851 final JexlEngine se = putThreadEngine(this);
852 ASTJexlScript script;
853 try {
854
855 if (parsing.compareAndSet(false, true)) {
856 synchronized (parsing) {
857 try {
858
859 script = parser.parse(ninfo, features, src, scope);
860 } finally {
861
862 parsing.set(false);
863 }
864 }
865 } else {
866
867 script = parserFactory.get().parse(ninfo, features, src, scope);
868 }
869 if (source != null) {
870 cache.put(source, script);
871 }
872 } finally {
873
874 putThreadEngine(se);
875 }
876 return script;
877 }
878
879
880
881
882
883
884
885
886
887
888 protected ASTJexlScript jxltParse(final JexlInfo info, final boolean expr, final String src, final Scope scope) {
889 synchronized(parsing) {
890 return parser.jxltParse(info, expr ? this.expressionFeatures : this.scriptFeatures, src, scope);
891 }
892 }
893
894
895
896
897
898
899
900
901
902
903
904
905
906 private void processPragmaModule(final Map<String, Object> ns, final String key, final Object value, final JexlInfo info,
907 final JexlContext context) {
908
909 final String module = key.substring(PRAGMA_MODULE.length());
910 if (module.isEmpty()) {
911 if (logger.isWarnEnabled()) {
912 logger.warn(module + ": invalid module declaration");
913 }
914 } else {
915 withValueSet(value, o -> {
916 if (!(o instanceof CharSequence)) {
917 if (logger.isWarnEnabled()) {
918 logger.warn(module + ": unable to define module from " + value);
919 }
920 } else {
921 final String moduleSrc = o.toString();
922 final Object functor;
923 if (context instanceof JexlContext.ModuleProcessor) {
924 final JexlContext.ModuleProcessor processor = (JexlContext.ModuleProcessor) context;
925 functor = processor.processModule(this, info, module, moduleSrc);
926 } else {
927 final Object moduleObject = createExpression(info, moduleSrc).evaluate(context);
928 functor = moduleObject instanceof Script ? ((Script) moduleObject).execute(context) : moduleObject;
929 }
930 if (functor != null) {
931 ns.put(module, functor);
932 } else {
933 ns.remove(module);
934 }
935 }
936 });
937 }
938 }
939
940
941
942
943
944
945
946
947 private void processPragmaNamespace(final Map<String, Object> ns, final String key, final Object value) {
948 if (value instanceof String) {
949
950 final String namespaceName = key.substring(PRAGMA_JEXLNS.length());
951 if (!namespaceName.isEmpty()) {
952 final String nsclass = value.toString();
953 final Class<?> clazz = uberspect.getClassByName(nsclass);
954 if (clazz == null) {
955 if (logger.isWarnEnabled()) {
956 logger.warn(key + ": unable to find class " + nsclass);
957 }
958 } else {
959 ns.put(namespaceName, clazz);
960 }
961 }
962 } else if (logger.isWarnEnabled()) {
963 logger.warn(key + ": ambiguous declaration " + value);
964 }
965 }
966
967
968
969
970
971
972
973
974
975 protected void processPragmas(final ASTJexlScript script, final JexlContext context, final JexlOptions opts) {
976 final Map<String, Object> pragmas = script.getPragmas();
977 if (pragmas != null && !pragmas.isEmpty()) {
978 final JexlContext.PragmaProcessor processor =
979 context instanceof JexlContext.PragmaProcessor
980 ? (JexlContext.PragmaProcessor) context
981 : null;
982 Map<String, Object> ns = null;
983 for (final Map.Entry<String, Object> pragma : pragmas.entrySet()) {
984 final String key = pragma.getKey();
985 final Object value = pragma.getValue();
986 if (PRAGMA_OPTIONS.equals(key)) {
987 if (value instanceof String) {
988
989 final String[] vs = value.toString().split(" ");
990 opts.setFlags(vs);
991 }
992 } else if (PRAGMA_IMPORT.equals(key)) {
993
994 final Set<String> is = new LinkedHashSet<>();
995 withValueSet(value, o -> {
996 if (o instanceof String) {
997 is.add(o.toString());
998 }
999 });
1000 if (!is.isEmpty()) {
1001 opts.setImports(is);
1002 }
1003 } else if (key.startsWith(PRAGMA_JEXLNS)) {
1004 if (ns == null) {
1005 ns = new LinkedHashMap<>();
1006 }
1007 processPragmaNamespace(ns, key, value);
1008 if (!ns.isEmpty()) {
1009 opts.setNamespaces(ns);
1010 }
1011 } else if (key.startsWith(PRAGMA_MODULE)) {
1012 if (ns == null) {
1013 ns = new LinkedHashMap<>();
1014 }
1015 processPragmaModule(ns, key, value, script.jexlInfo(), context);
1016 if (!ns.isEmpty()) {
1017 opts.setNamespaces(ns);
1018 }
1019 }
1020
1021 if (processor != null) {
1022 processor.processPragma(opts, key, value);
1023 }
1024 }
1025 }
1026 }
1027
1028
1029
1030
1031
1032
1033
1034 protected static JexlEngine putThreadEngine(final JexlEngine jexl) {
1035 final JexlEngine engine = ENGINE.get();
1036 if (engine != jexl) {
1037 ENGINE.set(jexl);
1038 return engine;
1039 }
1040 return jexl;
1041 }
1042
1043
1044
1045
1046
1047
1048
1049 protected JexlContext.ThreadLocal putThreadLocal(final JexlContext.ThreadLocal tls) {
1050 final JexlContext.ThreadLocal local = CONTEXT.get();
1051 CONTEXT.set(tls);
1052 return local;
1053 }
1054
1055 @Override
1056 public void setClassLoader(final ClassLoader classLoader) {
1057 final ClassLoader loader = classLoader == null? JexlUberspect.class.getClassLoader() : classLoader;
1058 uberspect.setClassLoader(loader);
1059 if (functions != null) {
1060 final Iterable<String> names = new ArrayList<>(functions.keySet());
1061 for(final String name : names) {
1062 final Object functor = functions.get(name);
1063 if (functor instanceof Class<?>) {
1064 final Class<?> fclass = (Class<?>) functor;
1065 try {
1066 final Class<?> nclass = loader.loadClass(fclass.getName());
1067 if (nclass != fclass) {
1068 functions.put(name, nclass);
1069 }
1070 } catch (final ClassNotFoundException xany) {
1071 functions.put(name, fclass.getName());
1072 }
1073 }
1074 }
1075 }
1076 metaCache.clearCaches();
1077 }
1078
1079 @Override
1080 public void setProperty(final JexlContext context, final Object bean, final String expr, final Object value) {
1081
1082 String src = trimSource(expr);
1083 src = "#0" + (src.charAt(0) == '[' ? "" : ".") + src + "=#1";
1084 try {
1085 final Scope scope = new Scope(null, "#0", "#1");
1086 final ASTJexlScript script = parse(createInfo(), PROPERTY_FEATURES, src, scope);
1087 final JexlNode node = script.jjtGetChild(0);
1088 final Frame frame = script.createFrame(bean, value);
1089 final Interpreter interpreter = createInterpreter(context != null ? context : EMPTY_CONTEXT, frame, options);
1090 interpreter.visitLexicalNode(node, null);
1091 } catch (final JexlException xjexl) {
1092 if (silent) {
1093 if (logger.isWarnEnabled()) {
1094 logger.warn(xjexl.getMessage(), xjexl.getCause());
1095 }
1096 return;
1097 }
1098 throw xjexl.clean();
1099 }
1100 }
1101
1102 @Override
1103 public void setProperty(final Object bean, final String expr, final Object value) {
1104 setProperty(null, bean, expr, value);
1105 }
1106
1107
1108
1109
1110
1111
1112
1113 protected String trimSource(final CharSequence str) {
1114 if (str != null) {
1115 int start = 0;
1116 int end = str.length();
1117 if (end > 0) {
1118
1119 while (start < end && Character.isSpaceChar(str.charAt(start))) {
1120 ++start;
1121 }
1122
1123 while (end > start && Character.isSpaceChar(str.charAt(end - 1))) {
1124 --end;
1125 }
1126 return str.subSequence(start, end).toString();
1127 }
1128 return "";
1129 }
1130 return null;
1131 }
1132
1133
1134
1135
1136
1137
1138 protected VarCollector varCollector() {
1139 return new VarCollector(this.collectMode);
1140 }
1141
1142
1143
1144
1145
1146
1147
1148 private void withValueSet(final Object value, final Consumer<Object> consumer) {
1149 final Set<?> values = value instanceof Set<?>
1150 ? (Set<?>) value
1151 : Collections.singleton(value);
1152 for (final Object o : values) {
1153 consumer.accept(o);
1154 }
1155 }
1156
1157
1158
1159
1160
1161
1162
1163
1164 protected JexlCache<Source, Object> createCache(final int capacity) {
1165 return capacity < 0 ? cache : capacity > 0 ? metaCache.createCache(capacity) : null;
1166 }
1167
1168 }