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 final ClassLoader loader = conf.loader();
341 if (loader != null) {
342 uber.setClassLoader(loader);
343 }
344 final JexlSandbox sandbox = conf.sandbox();
345 if (sandbox == null) {
346 this.uberspect = uber;
347 } else {
348 this.uberspect = new SandboxUberspect(uber, sandbox);
349 }
350 this.logger = conf.logger() == null ? LogFactory.getLog(JexlEngine.class) : conf.logger();
351 this.arithmetic = conf.arithmetic() == null ? new JexlArithmetic(this.strict) : conf.arithmetic();
352 options.setMathContext(arithmetic.getMathContext());
353 options.setMathScale(arithmetic.getMathScale());
354 options.setStrictArithmetic(arithmetic.isStrict());
355 final Map<String, Object> ns = conf.namespaces();
356 this.functions = ns == null || ns.isEmpty()? Collections.emptyMap() : ns;
357 this.classNameSolver = new FqcnResolver(uberspect, conf.imports());
358
359 final JexlFeatures features = conf.features() == null ? DEFAULT_FEATURES : conf.features();
360 Predicate<String> nsTest = features.namespaceTest();
361 final Set<String> nsNames = functions.keySet();
362 if (!nsNames.isEmpty()) {
363 nsTest = nsTest == JexlFeatures.TEST_STR_FALSE ?nsNames::contains : nsTest.or(nsNames::contains);
364 }
365 this.expressionFeatures = new JexlFeatures(features).script(false).namespaceTest(nsTest);
366 this.scriptFeatures = new JexlFeatures(features).script(true).namespaceTest(nsTest);
367 this.charset = conf.charset();
368
369 final IntFunction<JexlCache<?, ?>> factory = conf.cacheFactory();
370 this.metaCache = new MetaCache(factory == null ? SoftCache::new : factory);
371 this.cache = metaCache.createCache(conf.cache());
372 this.cacheThreshold = conf.cacheThreshold();
373 if (uberspect == null) {
374 throw new IllegalArgumentException("uberspect cannot be null");
375 }
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 = debug ? createInfo() : null;
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 protected String[] getLocalVariables(final JexlScript script) {
555 return script.getLocalVariables();
556 }
557
558
559
560
561
562
563 final Object getNamespace(final String name) {
564 return functions.get(name);
565 }
566
567
568
569
570
571
572
573 protected String[] getParameters(final JexlScript script) {
574 return script.getParameters();
575 }
576
577 @Override
578 public Object getProperty(final JexlContext context, final Object bean, final String expr) {
579
580 String src = trimSource(expr);
581 src = "#0" + (src.charAt(0) == '[' ? "" : ".") + src;
582 try {
583 final Scope scope = new Scope(null, "#0");
584 final ASTJexlScript script = parse(null, PROPERTY_FEATURES, src, scope);
585 final JexlNode node = script.jjtGetChild(0);
586 final Frame frame = script.createFrame(bean);
587 final Interpreter interpreter = createInterpreter(context == null ? EMPTY_CONTEXT : context, frame, options);
588 return interpreter.visitLexicalNode(node, null);
589 } catch (final JexlException xjexl) {
590 if (silent) {
591 if (logger.isWarnEnabled()) {
592 logger.warn(xjexl.getMessage(), xjexl.getCause());
593 }
594 return null;
595 }
596 throw xjexl.clean();
597 }
598 }
599
600 @Override
601 public Object getProperty(final Object bean, final String expr) {
602 return getProperty(null, bean, expr);
603 }
604
605 @Override
606 public JexlUberspect getUberspect() {
607 return uberspect;
608 }
609
610
611
612
613
614
615
616
617
618 protected Set<List<String>> getVariables(final ASTJexlScript script) {
619 final VarCollector collector = varCollector();
620 getVariables(script, script, collector);
621 return collector.collected();
622 }
623
624
625
626
627
628
629
630 protected void getVariables(final ASTJexlScript script, final JexlNode node, final VarCollector collector) {
631 if (node instanceof ASTIdentifier) {
632 final JexlNode parent = node.jjtGetParent();
633 if (parent instanceof ASTMethodNode || parent instanceof ASTFunctionNode) {
634
635 collector.collect(null);
636 return;
637 }
638 final ASTIdentifier identifier = (ASTIdentifier) node;
639 final int symbol = identifier.getSymbol();
640
641 if (symbol >= 0 && script != null && !script.isCapturedSymbol(symbol)) {
642 collector.collect(null);
643 } else {
644
645 collector.collect(identifier);
646 collector.add(identifier.getName());
647 }
648 } else if (node instanceof ASTIdentifierAccess) {
649 final JexlNode parent = node.jjtGetParent();
650 if (parent instanceof ASTMethodNode || parent instanceof ASTFunctionNode) {
651
652 collector.collect(null);
653 return;
654 }
655
656 if (collector.isCollecting()) {
657 collector.add(((ASTIdentifierAccess) node).getName());
658 }
659 } else if (node instanceof ASTArrayAccess && collector.mode > 0) {
660 final int num = node.jjtGetNumChildren();
661
662 boolean collecting = collector.isCollecting();
663 for (int i = 0; i < num; ++i) {
664 final JexlNode child = node.jjtGetChild(i);
665 if (collecting && child.isConstant()) {
666
667 final boolean collect = collector.mode > 1
668 || child instanceof ASTStringLiteral || child instanceof ASTNumberLiteral;
669 if (collect) {
670 final String image = child.toString();
671 collector.add(image);
672 }
673 } else {
674 collecting = false;
675 collector.collect(null);
676 getVariables(script, child, collector);
677 collector.collect(null);
678 }
679 }
680 } else {
681 final int num = node.jjtGetNumChildren();
682 for (int i = 0; i < num; ++i) {
683 getVariables(script, node.jjtGetChild(i), collector);
684 }
685 collector.collect(null);
686 }
687 }
688
689 @Override
690 public Object invokeMethod(final Object obj, final String meth, final Object... args) {
691 JexlException xjexl = null;
692 Object result = null;
693 final JexlInfo info = debug ? createInfo() : null;
694 try {
695 JexlMethod method = uberspect.getMethod(obj, meth, args);
696 if (method == null && arithmetic.narrowArguments(args)) {
697 method = uberspect.getMethod(obj, meth, args);
698 }
699 if (method != null) {
700 result = method.invoke(obj, args);
701 } else {
702 xjexl = new JexlException.Method(info, meth, args);
703 }
704 } catch (final JexlException xany) {
705 xjexl = xany;
706 } catch (final Exception xany) {
707 xjexl = new JexlException.Method(info, meth, args, xany);
708 }
709 if (xjexl != null) {
710 if (!silent) {
711 throw xjexl.clean();
712 }
713 if (logger.isWarnEnabled()) {
714 logger.warn(xjexl.getMessage(), xjexl.getCause());
715 }
716 }
717 return result;
718 }
719
720 @Override
721 public boolean isCancellable() {
722 return this.cancellable;
723 }
724
725 @Override
726 public boolean isDebug() {
727 return this.debug;
728 }
729
730 @Override
731 public boolean isSilent() {
732 return this.silent;
733 }
734
735 @Override
736 public boolean isStrict() {
737 return this.strict;
738 }
739
740 @Override
741 public <T> T newInstance(final Class<? extends T> clazz, final Object... args) {
742 return clazz.cast(doCreateInstance(clazz, args));
743 }
744
745 @Override
746 public Object newInstance(final String clazz, final Object... args) {
747 return doCreateInstance(clazz, args);
748 }
749
750 @Override
751 public JexlUberspect.ClassConstantResolver createConstantResolver(final Collection<String> imports) {
752 return imports == null || imports.isEmpty()
753 ? classNameSolver
754 : new FqcnResolver(classNameSolver).importPackages(imports);
755 }
756
757
758
759
760
761
762 public JexlOptions optionsSet(final JexlOptions opts) {
763 if (opts != null) {
764 opts.set(options);
765 }
766 return opts;
767 }
768
769
770
771
772
773
774
775
776
777
778
779 protected ASTJexlScript parse(final JexlInfo info, final JexlFeatures parsingf, final String src, final Scope scope) {
780 final boolean cached = src.length() < cacheThreshold && cache != null;
781 final JexlFeatures features = parsingf != null ? parsingf : DEFAULT_FEATURES;
782 final Source source = cached ? new Source(features, Scope.getSymbolsMap(scope), src) : null;
783 if (source != null) {
784 final Object c = cache.get(source);
785 if (c instanceof ASTJexlScript) {
786 return (ASTJexlScript) c;
787 }
788 }
789 final JexlInfo ninfo = info == null && debug ? createInfo() : info;
790 final JexlEngine se = putThreadEngine(this);
791 ASTJexlScript script;
792 try {
793
794 if (parsing.compareAndSet(false, true)) {
795 synchronized (parsing) {
796 try {
797
798 script = parser.parse(ninfo, features, src, scope);
799 } finally {
800
801 parsing.set(false);
802 }
803 }
804 } else {
805
806 script = parserFactory.get().parse(ninfo, features, src, scope);
807 }
808 if (source != null) {
809 cache.put(source, script);
810 }
811 } finally {
812
813 putThreadEngine(se);
814 }
815 return script;
816 }
817
818
819
820
821
822
823
824
825
826 protected ASTJexlScript jxltParse(final JexlInfo info, final boolean expr, final String src, final Scope scope) {
827 synchronized(parsing) {
828 return parser.jxltParse(info, expr ? this.expressionFeatures : this.scriptFeatures, src, scope);
829 }
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 uberspect.setClassLoader(loader);
988 if (functions != null) {
989 final Iterable<String> names = new ArrayList<>(functions.keySet());
990 for(final String name : names) {
991 final Object functor = functions.get(name);
992 if (functor instanceof Class<?>) {
993 final Class<?> fclass = (Class<?>) functor;
994 try {
995 final Class<?> nclass = loader.loadClass(fclass.getName());
996 if (nclass != fclass) {
997 functions.put(name, nclass);
998 }
999 } catch (final ClassNotFoundException xany) {
1000 functions.put(name, fclass.getName());
1001 }
1002 }
1003 }
1004 }
1005 metaCache.clearCaches();
1006 }
1007
1008 @Override
1009 public void setProperty(final JexlContext context, final Object bean, final String expr, final Object value) {
1010
1011 String src = trimSource(expr);
1012 src = "#0" + (src.charAt(0) == '[' ? "" : ".") + src + "=#1";
1013 try {
1014 final Scope scope = new Scope(null, "#0", "#1");
1015 final ASTJexlScript script = parse(null, PROPERTY_FEATURES, src, scope);
1016 final JexlNode node = script.jjtGetChild(0);
1017 final Frame frame = script.createFrame(bean, value);
1018 final Interpreter interpreter = createInterpreter(context != null ? context : EMPTY_CONTEXT, frame, options);
1019 interpreter.visitLexicalNode(node, null);
1020 } catch (final JexlException xjexl) {
1021 if (silent) {
1022 if (logger.isWarnEnabled()) {
1023 logger.warn(xjexl.getMessage(), xjexl.getCause());
1024 }
1025 return;
1026 }
1027 throw xjexl.clean();
1028 }
1029 }
1030
1031 @Override
1032 public void setProperty(final Object bean, final String expr, final Object value) {
1033 setProperty(null, bean, expr, value);
1034 }
1035
1036
1037
1038
1039
1040
1041 protected String trimSource(final CharSequence str) {
1042 if (str != null) {
1043 int start = 0;
1044 int end = str.length();
1045 if (end > 0) {
1046
1047 while (start < end && Character.isSpaceChar(str.charAt(start))) {
1048 ++start;
1049 }
1050
1051 while (end > start && Character.isSpaceChar(str.charAt(end - 1))) {
1052 --end;
1053 }
1054 return str.subSequence(start, end).toString();
1055 }
1056 return "";
1057 }
1058 return null;
1059 }
1060
1061
1062
1063
1064
1065 protected VarCollector varCollector() {
1066 return new VarCollector(this.collectMode);
1067 }
1068
1069
1070
1071
1072
1073
1074 private void withValueSet(final Object value, final Consumer<Object> consumer) {
1075 final Set<?> values = value instanceof Set<?>
1076 ? (Set<?>) value
1077 : Collections.singleton(value);
1078 for (final Object o : values) {
1079 consumer.accept(o);
1080 }
1081 }
1082
1083
1084
1085
1086
1087
1088
1089 protected JexlCache<Source, Object> createCache(final int capacity) {
1090 return capacity < 0 ? cache : capacity > 0 ? metaCache.createCache(capacity) : null;
1091 }
1092
1093 }