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 java.util.Collection;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Objects;
24 import java.util.Queue;
25 import java.util.concurrent.atomic.AtomicBoolean;
26
27 import org.apache.commons.jexl3.JexlArithmetic;
28 import org.apache.commons.jexl3.JexlContext;
29 import org.apache.commons.jexl3.JexlContext.NamespaceFunctor;
30 import org.apache.commons.jexl3.JexlEngine;
31 import org.apache.commons.jexl3.JexlException;
32 import org.apache.commons.jexl3.JexlException.VariableIssue;
33 import org.apache.commons.jexl3.JexlOperator;
34 import org.apache.commons.jexl3.JexlOptions;
35 import org.apache.commons.jexl3.introspection.JexlMethod;
36 import org.apache.commons.jexl3.introspection.JexlPropertyGet;
37 import org.apache.commons.jexl3.introspection.JexlPropertySet;
38 import org.apache.commons.jexl3.introspection.JexlUberspect;
39 import org.apache.commons.jexl3.parser.ASTArrayAccess;
40 import org.apache.commons.jexl3.parser.ASTAssignment;
41 import org.apache.commons.jexl3.parser.ASTFunctionNode;
42 import org.apache.commons.jexl3.parser.ASTIdentifier;
43 import org.apache.commons.jexl3.parser.ASTIdentifierAccess;
44 import org.apache.commons.jexl3.parser.ASTMethodNode;
45 import org.apache.commons.jexl3.parser.ASTNullpNode;
46 import org.apache.commons.jexl3.parser.ASTReference;
47 import org.apache.commons.jexl3.parser.ASTTernaryNode;
48 import org.apache.commons.jexl3.parser.ASTVar;
49 import org.apache.commons.jexl3.parser.JexlNode;
50 import org.apache.commons.jexl3.parser.ParserVisitor;
51 import org.apache.commons.logging.Log;
52
53
54
55
56
57 public abstract class InterpreterBase extends ParserVisitor {
58
59
60
61 protected static class ArithmeticFuncall extends Funcall {
62
63
64
65
66
67 protected ArithmeticFuncall(final JexlMethod jme, final boolean flag) {
68 super(jme, flag);
69 }
70
71 @Override
72 protected Object tryInvoke(final InterpreterBase ii, final String name, final Object target, final Object[] args) {
73 return me.tryInvoke(name, ii.arithmetic, ii.functionArguments(target, narrow, args));
74 }
75 }
76
77
78
79 protected class CallDispatcher {
80
81 final JexlNode node;
82
83 final boolean cacheable;
84
85 boolean narrow;
86
87 JexlMethod vm;
88
89 Object target;
90
91 Object[] argv;
92
93 Funcall funcall;
94
95
96
97
98
99
100
101 CallDispatcher(final JexlNode anode, final boolean acacheable) {
102 this.node = anode;
103 this.cacheable = acacheable;
104 }
105
106
107
108
109
110
111
112
113 protected Object eval(final String methodName) throws Exception {
114
115 if (vm != null) {
116
117 final Object eval = vm.invoke(target, argv);
118
119 if (funcall != null) {
120 node.jjtSetValue(funcall);
121 }
122 return eval;
123 }
124 return unsolvableMethod(node, methodName, argv);
125 }
126
127
128
129
130
131
132
133
134 protected boolean isArithmeticMethod(final String methodName, final Object[] arguments) {
135 vm = uberspect.getMethod(arithmetic, methodName, arguments);
136 if (vm != null) {
137 argv = arguments;
138 target = arithmetic;
139 if (cacheable && vm.isCacheable()) {
140 funcall = new ArithmeticFuncall(vm, narrow);
141 }
142 return true;
143 }
144 return false;
145 }
146
147
148
149
150
151
152
153
154 protected boolean isContextMethod(final String methodName, final Object[] arguments) {
155 vm = uberspect.getMethod(context, methodName, arguments);
156 if (vm != null) {
157 argv = arguments;
158 target = context;
159 if (cacheable && vm.isCacheable()) {
160 funcall = new ContextFuncall(vm, narrow);
161 }
162 return true;
163 }
164 return false;
165 }
166
167
168
169
170
171
172
173
174
175 protected boolean isTargetMethod(final Object ntarget, final String methodName, final Object[] arguments) {
176
177 vm = uberspect.getMethod(ntarget, methodName, arguments);
178 if (vm != null) {
179 argv = arguments;
180 target = ntarget;
181 if (cacheable && vm.isCacheable()) {
182 funcall = new Funcall(vm, narrow);
183 }
184 return true;
185 }
186 return false;
187 }
188
189
190
191
192
193
194
195
196
197
198
199 protected Object tryEval(final Object ntarget, final String methodName, final Object[] arguments) {
200
201
202 if (methodName != null && cacheable && ntarget != null) {
203 final Object cached = node.jjtGetValue();
204 if (cached instanceof Funcall) {
205 return ((Funcall) cached).tryInvoke(InterpreterBase.this, methodName, ntarget, arguments);
206 }
207 }
208 return JexlEngine.TRY_FAILED;
209 }
210 }
211
212
213
214 protected static class ContextFuncall extends Funcall {
215
216
217
218
219
220 protected ContextFuncall(final JexlMethod jme, final boolean flag) {
221 super(jme, flag);
222 }
223
224 @Override
225 protected Object tryInvoke(final InterpreterBase ii, final String name, final Object target, final Object[] args) {
226 return me.tryInvoke(name, ii.context, ii.functionArguments(target, narrow, args));
227 }
228 }
229
230
231
232 protected static class ContextualCtor extends Funcall {
233
234
235
236
237
238 protected ContextualCtor(final JexlMethod jme, final boolean flag) {
239 super(jme, flag);
240 }
241
242 @Override
243 protected Object tryInvoke(final InterpreterBase ii, final String name, final Object target, final Object[] args) {
244 return me.tryInvoke(name, target, ii.callArguments(ii.context, narrow, args));
245 }
246 }
247
248
249
250 protected static class Funcall implements JexlNode.Funcall {
251
252 protected final boolean narrow;
253
254 protected final JexlMethod me;
255
256
257
258
259
260 protected Funcall(final JexlMethod jme, final boolean flag) {
261 this.me = jme;
262 this.narrow = flag;
263 }
264
265
266
267
268
269
270
271
272
273 protected Object tryInvoke(final InterpreterBase ii, final String name, final Object target, final Object[] args) {
274 return me.tryInvoke(name, target, ii.functionArguments(null, narrow, args));
275 }
276 }
277
278 protected static final Object[] EMPTY_PARAMS = {};
279
280
281
282
283
284
285 protected static String stringifyPropertyValue(final JexlNode node) {
286 return node != null ? new Debugger().depth(1).data(node) : "???";
287 }
288
289 protected final Engine jexl;
290
291 protected final Log logger;
292
293 protected final JexlUberspect uberspect;
294
295 protected final JexlArithmetic arithmetic;
296
297 protected final JexlContext context;
298
299 protected final JexlOptions options;
300
301 protected final boolean cache;
302
303
304 protected final AtomicBoolean cancelled;
305
306
307 protected final JexlContext.NamespaceResolver ns;
308
309
310 protected final JexlContext.ClassNameResolver fqcnSolver;
311
312
313 protected final Operators operators;
314
315
316 protected final Map<String, Object> functions;
317
318
319 protected Map<String, Object> functors;
320
321
322
323
324
325
326
327 protected InterpreterBase(final Engine engine, final JexlOptions opts, final JexlContext aContext) {
328 this.jexl = engine;
329 this.logger = jexl.logger;
330 this.uberspect = jexl.uberspect;
331 this.context = aContext != null ? aContext : JexlEngine.EMPTY_CONTEXT;
332 this.cache = engine.cache != null;
333 final JexlArithmetic jexla = jexl.arithmetic;
334 this.options = opts == null ? engine.evalOptions(aContext) : opts;
335 this.arithmetic = jexla.options(options);
336 if (arithmetic != jexla && !arithmetic.getClass().equals(jexla.getClass()) && logger.isWarnEnabled()) {
337 logger.warn("expected arithmetic to be " + jexla.getClass().getSimpleName()
338 + ", got " + arithmetic.getClass().getSimpleName()
339 );
340 }
341 if (this.context instanceof JexlContext.NamespaceResolver) {
342 ns = (JexlContext.NamespaceResolver) context;
343 } else {
344 ns = JexlEngine.EMPTY_NS;
345 }
346 AtomicBoolean acancel = null;
347 if (this.context instanceof JexlContext.CancellationHandle) {
348 acancel = ((JexlContext.CancellationHandle) context).getCancellation();
349 }
350 this.cancelled = acancel != null ? acancel : new AtomicBoolean();
351 this.functions = options.getNamespaces();
352 this.functors = null;
353 this.operators = new Operators(this);
354
355 final Collection<String> imports = options.getImports();
356 this.fqcnSolver = imports.isEmpty()
357 ? engine.classNameSolver
358 : new FqcnResolver(engine.classNameSolver).importPackages(imports);
359 }
360
361
362
363
364
365
366 protected InterpreterBase(final InterpreterBase ii, final JexlArithmetic jexla) {
367 jexl = ii.jexl;
368 logger = ii.logger;
369 uberspect = ii.uberspect;
370 arithmetic = jexla;
371 context = ii.context;
372 options = ii.options.copy();
373 cache = ii.cache;
374 ns = ii.ns;
375 operators = ii.operators;
376 cancelled = ii.cancelled;
377 functions = ii.functions;
378 functors = ii.functors;
379 fqcnSolver = ii.fqcnSolver;
380 }
381
382
383
384
385
386
387
388
389 protected Object annotationError(final JexlNode node, final String annotation, final Throwable cause) {
390 if (isStrictEngine()) {
391 throw new JexlException.Annotation(node, annotation, cause);
392 }
393 if (logger.isDebugEnabled()) {
394 logger.debug(JexlException.annotationError(node, annotation), cause);
395 }
396 return null;
397 }
398
399
400
401
402
403
404
405
406 protected Object[] callArguments(final Object target, final boolean narrow, final Object[] args) {
407
408 final Object[] nargv = new Object[args.length + 1];
409 if (narrow) {
410 nargv[0] = functionArgument(true, target);
411 for (int a = 1; a <= args.length; ++a) {
412 nargv[a] = functionArgument(true, args[a - 1]);
413 }
414 } else {
415 nargv[0] = target;
416 System.arraycopy(args, 0, nargv, 1, args.length);
417 }
418 return nargv;
419 }
420
421
422
423
424
425 protected boolean cancel() {
426 return cancelled.compareAndSet(false, true);
427 }
428
429
430
431
432
433 protected void cancelCheck(final JexlNode node) {
434 if (isCancelled()) {
435 throw new JexlException.Cancel(node);
436 }
437 }
438
439
440
441
442
443
444 protected void closeIfSupported(final Object closeable) {
445 if (closeable != null) {
446 final JexlMethod mclose = uberspect.getMethod(closeable, "close", EMPTY_PARAMS);
447 if (mclose != null) {
448 try {
449 mclose.invoke(closeable, EMPTY_PARAMS);
450 } catch (final Exception xignore) {
451 logger.warn(xignore);
452 }
453 }
454 }
455 }
456
457
458
459
460
461
462 protected void closeIfSupported(final Queue<Object> closeables) {
463 for(final Object closeable : closeables) {
464 closeIfSupported(closeable);
465 }
466 }
467
468
469
470
471
472
473
474 protected Object constVariable(final JexlNode node, final String var) {
475 return variableError(node, var, VariableIssue.CONST);
476 }
477
478
479
480
481
482
483
484 protected boolean defineVariable(final ASTVar var, final LexicalFrame frame) {
485 final int symbol = var.getSymbol();
486 if (symbol < 0) {
487 return false;
488 }
489 if (var.isRedefined()) {
490 return false;
491 }
492 return frame.defineSymbol(symbol, var.isCaptured());
493 }
494
495
496
497
498
499
500
501
502 protected JexlNode findNullOperand(final JexlNode node, final Object left, final Object right) {
503 if (left == null) {
504 return node.jjtGetChild(0);
505 }
506 if (right == null) {
507 return node.jjtGetChild(1);
508 }
509 return node;
510 }
511
512 @Deprecated
513 protected JexlNode findNullOperand(final RuntimeException xrt, final JexlNode node, final Object left, final Object right) {
514 return findNullOperand(node, left, right);
515 }
516
517
518
519
520
521
522
523 protected Object functionArgument(final boolean narrow, final Object arg) {
524 return narrow && arg instanceof Number ? arithmetic.narrow((Number) arg) : arg;
525 }
526
527
528
529
530
531
532
533
534
535 protected Object[] functionArguments(final Object target, final boolean narrow, final Object[] args) {
536
537 if (target == null || target == context) {
538 if (narrow) {
539 arithmetic.narrowArguments(args);
540 }
541 return args;
542 }
543
544 final Object[] nargv = new Object[args.length + 1];
545 if (narrow) {
546 nargv[0] = functionArgument(true, target);
547 for (int a = 1; a <= args.length; ++a) {
548 nargv[a] = functionArgument(true, args[a - 1]);
549 }
550 } else {
551 nargv[0] = target;
552 System.arraycopy(args, 0, nargv, 1, args.length);
553 }
554 return nargv;
555 }
556
557
558
559
560
561
562
563
564
565 protected Object getAttribute(final Object object, final Object attribute, final JexlNode node) {
566 if (object == null) {
567 throw new JexlException(node, "object is null");
568 }
569 cancelCheck(node);
570 final JexlOperator operator = node != null && node.jjtGetParent() instanceof ASTArrayAccess
571 ? JexlOperator.ARRAY_GET : JexlOperator.PROPERTY_GET;
572 final Object result = operators.tryOverload(node, operator, object, attribute);
573 if (result != JexlEngine.TRY_FAILED) {
574 return result;
575 }
576 Exception xcause = null;
577 try {
578
579 if (node != null && cache) {
580 final Object cached = node.jjtGetValue();
581 if (cached instanceof JexlPropertyGet) {
582 final JexlPropertyGet vg = (JexlPropertyGet) cached;
583 final Object value = vg.tryInvoke(object, attribute);
584 if (!vg.tryFailed(value)) {
585 return value;
586 }
587 }
588 }
589
590 final List<JexlUberspect.PropertyResolver> resolvers = uberspect.getResolvers(operator, object);
591 final JexlPropertyGet vg = uberspect.getPropertyGet(resolvers, object, attribute);
592 if (vg != null) {
593 final Object value = vg.invoke(object);
594
595 if (node != null && cache && vg.isCacheable()) {
596 node.jjtSetValue(vg);
597 }
598 return value;
599 }
600 } catch (final Exception xany) {
601 xcause = xany;
602 }
603
604 if (node == null) {
605
606 final String error = "unable to get object property"
607 + ", class: " + object.getClass().getName()
608 + ", property: " + attribute;
609 throw new UnsupportedOperationException(error, xcause);
610 }
611 final boolean safe = node instanceof ASTIdentifierAccess && ((ASTIdentifierAccess) node).isSafe();
612 if (safe) {
613 return null;
614 }
615 final String attrStr = Objects.toString(attribute, null);
616 return unsolvableProperty(node, attrStr, true, xcause);
617 }
618
619
620
621
622
623
624
625
626 protected Object getVariable(final Frame frame, final LexicalScope block, final ASTIdentifier identifier) {
627 final int symbol = identifier.getSymbol();
628 final String name = identifier.getName();
629
630 if ((options.isLexicalShade() || identifier.isLexical()) && identifier.isShaded()) {
631 return undefinedVariable(identifier, name);
632 }
633
634 if (symbol >= 0 && frame.has(symbol)) {
635 final Object value = frame.get(symbol);
636
637 if (value != Scope.UNDEFINED) {
638
639 if (value == null && isStrictOperand(identifier)) {
640 return unsolvableVariable(identifier, name, false);
641 }
642 return value;
643 }
644 }
645
646 final Object value = context.get(name);
647
648 if (value == null) {
649
650 if (!context.has(name)) {
651
652 final boolean ignore = identifier.jjtGetParent() instanceof ASTReference
653 || isSafe() && (symbol >= 0 || identifier.jjtGetParent() instanceof ASTAssignment);
654 if (!ignore) {
655 return undefinedVariable(identifier, name);
656 }
657 } else if (isStrictOperand(identifier)) {
658 return unsolvableVariable(identifier, name, false);
659 }
660 }
661 return value;
662 }
663
664
665
666
667
668
669
670 protected JexlException invocationException(final JexlNode node, final String methodName, final Throwable xany) {
671 final Throwable cause = xany.getCause();
672 if (cause instanceof JexlException) {
673 return (JexlException) cause;
674 }
675 if (cause instanceof InterruptedException) {
676 return new JexlException.Cancel(node);
677 }
678 return new JexlException(node, methodName, xany);
679 }
680
681
682
683
684 protected boolean isCancellable() {
685 return options.isCancellable();
686 }
687
688
689
690
691
692 protected boolean isCancelled() {
693 return cancelled.get() | Thread.currentThread().isInterrupted();
694 }
695
696
697
698
699
700 protected boolean isSafe() {
701 return options.isSafe();
702 }
703
704
705
706
707
708 protected boolean isSilent() {
709 return options.isSilent();
710 }
711
712
713
714
715
716 protected boolean isStrictEngine() {
717 return options.isStrict();
718 }
719
720
721
722
723
724 protected boolean isStrictOperand(final JexlNode node) {
725 return node.jjtGetParent().isStrictOperator(arithmetic);
726 }
727
728
729
730
731
732
733
734
735
736
737 protected boolean isTernaryProtected(final JexlNode startNode) {
738 JexlNode node = startNode;
739 for (JexlNode walk = node.jjtGetParent(); walk != null; walk = walk.jjtGetParent()) {
740
741 if (walk instanceof ASTTernaryNode
742 || walk instanceof ASTNullpNode) {
743 return node == walk.jjtGetChild(0);
744 }
745 if (!(walk instanceof ASTReference || walk instanceof ASTArrayAccess)) {
746 break;
747 }
748 node = walk;
749 }
750 return false;
751 }
752
753
754
755
756
757
758
759
760
761
762 protected boolean isVariableDefined(final Frame frame, final LexicalScope block, final String name) {
763 if (frame != null && block != null) {
764 final Integer ref = frame.getScope().getSymbol(name);
765 final int symbol = ref != null ? ref : -1;
766 if (symbol >= 0 && block.hasSymbol(symbol)) {
767 final Object value = frame.get(symbol);
768 return value != Scope.UNDEFINED && value != Scope.UNDECLARED;
769 }
770 }
771 return context.has(name);
772 }
773
774
775
776
777
778
779
780
781 protected Object operatorError(final JexlNode node, final JexlOperator operator, final Throwable cause) {
782 if (isStrictEngine()) {
783 throw new JexlException.Operator(node, operator.getOperatorSymbol(), cause);
784 }
785 if (logger.isDebugEnabled()) {
786 logger.debug(JexlException.operatorError(node, operator.getOperatorSymbol()), cause);
787 }
788 return null;
789 }
790
791
792
793
794
795
796
797 protected Object redefinedVariable(final JexlNode node, final String var) {
798 return variableError(node, var, VariableIssue.REDEFINED);
799 }
800
801
802
803
804
805
806
807
808
809 protected Object resolveNamespace(final String prefix, final JexlNode node) {
810 Object namespace;
811
812 synchronized (this) {
813 if (functors != null) {
814 namespace = functors.get(prefix);
815 if (namespace != null) {
816 return namespace;
817 }
818 }
819 }
820
821 namespace = ns.resolveNamespace(prefix);
822 if (namespace == null) {
823 namespace = functions.get(prefix);
824 if (namespace == null) {
825 namespace = jexl.getNamespace(prefix);
826 }
827 if (prefix != null && namespace == null) {
828 throw new JexlException(node, "no such function namespace " + prefix, null);
829 }
830 }
831 Object functor = null;
832
833 if (namespace instanceof Class<?> || namespace instanceof String) {
834
835 final ASTIdentifier nsNode = (ASTIdentifier) node.jjtGetChild(0);
836 final boolean cacheable = cache && prefix != null;
837 final Object cached = cacheable ? nsNode.jjtGetValue() : null;
838
839 if (cached instanceof Class<?>) {
840 return cached;
841 }
842
843 if (cached instanceof JexlContext.NamespaceFunctor) {
844 final Object eval = ((JexlContext.NamespaceFunctor) cached).createFunctor(context);
845 if (JexlEngine.TRY_FAILED != eval) {
846 functor = eval;
847 namespace = cached;
848 }
849 }
850 if (functor == null) {
851
852 for (int tried = 0; tried < 2; ++tried) {
853 final boolean withContext = tried == 0;
854 final JexlMethod ctor = withContext
855 ? uberspect.getConstructor(namespace, context)
856 : uberspect.getConstructor(namespace);
857 if (ctor != null) {
858 try {
859 functor = withContext
860 ? ctor.invoke(namespace, context)
861 : ctor.invoke(namespace);
862
863 if (functor != null) {
864
865
866 final Object ns = namespace;
867
868 namespace = (NamespaceFunctor) context -> withContext
869 ? ctor.tryInvoke(null, ns, context)
870 : ctor.tryInvoke(null, ns);
871 if (cacheable && ctor.isCacheable()) {
872 nsNode.jjtSetValue(namespace);
873 }
874 break;
875 }
876 } catch (final Exception xinst) {
877 throw new JexlException(node, "unable to instantiate namespace " + prefix, xinst);
878 }
879 }
880 }
881
882 if (functor == null) {
883 try {
884
885 if (namespace instanceof String) {
886 namespace = uberspect.getClassLoader().loadClass((String) namespace);
887 }
888
889 if (cacheable) {
890 nsNode.jjtSetValue(namespace);
891 }
892 } catch (final ClassNotFoundException e) {
893
894 throw new JexlException(node, "no such class namespace " + prefix, e);
895 }
896 }
897 }
898 }
899
900 if (functor == null && namespace instanceof JexlContext.NamespaceFunctor) {
901 functor = ((JexlContext.NamespaceFunctor) namespace).createFunctor(context);
902 }
903
904 if (functor != null) {
905 synchronized (this) {
906 if (functors == null) {
907 functors = new HashMap<>();
908 }
909 functors.put(prefix, functor);
910 }
911 return functor;
912 }
913 return namespace;
914 }
915
916
917
918
919
920
921
922
923
924 protected void setAttribute(final Object object, final Object attribute, final Object value, final JexlNode node) {
925 cancelCheck(node);
926 final JexlOperator operator = node != null && node.jjtGetParent() instanceof ASTArrayAccess
927 ? JexlOperator.ARRAY_SET : JexlOperator.PROPERTY_SET;
928 final Object result = operators.tryOverload(node, operator, object, attribute, value);
929 if (result != JexlEngine.TRY_FAILED) {
930 return;
931 }
932 Exception xcause = null;
933 try {
934
935 if (node != null && cache) {
936 final Object cached = node.jjtGetValue();
937 if (cached instanceof JexlPropertySet) {
938 final JexlPropertySet setter = (JexlPropertySet) cached;
939 final Object eval = setter.tryInvoke(object, attribute, value);
940 if (!setter.tryFailed(eval)) {
941 return;
942 }
943 }
944 }
945 final List<JexlUberspect.PropertyResolver> resolvers = uberspect.getResolvers(operator, object);
946 JexlPropertySet vs = uberspect.getPropertySet(resolvers, object, attribute, value);
947
948 if (vs == null) {
949
950 final Object[] narrow = {value};
951 if (arithmetic.narrowArguments(narrow)) {
952 vs = uberspect.getPropertySet(resolvers, object, attribute, narrow[0]);
953 }
954 }
955 if (vs != null) {
956
957 vs.invoke(object, value);
958 if (node != null && cache && vs.isCacheable()) {
959 node.jjtSetValue(vs);
960 }
961 return;
962 }
963 } catch (final Exception xany) {
964 xcause = xany;
965 }
966
967 if (node == null) {
968
969 final String error = "unable to set object property"
970 + ", class: " + object.getClass().getName()
971 + ", property: " + attribute
972 + ", argument: " + value.getClass().getSimpleName();
973 throw new UnsupportedOperationException(error, xcause);
974 }
975 final String attrStr = Objects.toString(attribute, null);
976 unsolvableProperty(node, attrStr, true, xcause);
977 }
978
979
980
981
982
983
984
985
986
987 protected void setContextVariable(final JexlNode node, final String name, final Object value) {
988 boolean lexical = options.isLexicalShade();
989 if (!lexical && node instanceof ASTIdentifier) {
990 lexical = ((ASTIdentifier) node).isLexical();
991 }
992 if (lexical && !context.has(name)) {
993 throw new JexlException.Variable(node, name, true);
994 }
995 try {
996 context.set(name, value);
997 } catch (final UnsupportedOperationException xsupport) {
998 throw new JexlException(node, "context is readonly", xsupport);
999 }
1000 }
1001
1002
1003
1004
1005
1006
1007
1008 protected String stringifyProperty(final JexlNode node) {
1009 if (node instanceof ASTArrayAccess) {
1010 return "[" + stringifyPropertyValue(node.jjtGetChild(0)) + "]";
1011 }
1012 if (node instanceof ASTMethodNode) {
1013 return stringifyPropertyValue(node.jjtGetChild(0));
1014 }
1015 if (node instanceof ASTFunctionNode) {
1016 return stringifyPropertyValue(node.jjtGetChild(0));
1017 }
1018 if (node instanceof ASTIdentifier) {
1019 return ((ASTIdentifier) node).getName();
1020 }
1021 if (node instanceof ASTReference) {
1022 return stringifyProperty(node.jjtGetChild(0));
1023 }
1024 return stringifyPropertyValue(node);
1025 }
1026
1027
1028
1029
1030
1031
1032
1033 protected Object undefinedVariable(final JexlNode node, final String var) {
1034 return variableError(node, var, VariableIssue.UNDEFINED);
1035 }
1036
1037
1038
1039
1040
1041
1042
1043 protected Object unsolvableMethod(final JexlNode node, final String method) {
1044 return unsolvableMethod(node, method, null);
1045 }
1046
1047
1048
1049
1050
1051
1052
1053
1054 protected Object unsolvableMethod(final JexlNode node, final String method, final Object[] args) {
1055 if (isStrictEngine()) {
1056 throw new JexlException.Method(node, method, args);
1057 }
1058 if (logger.isDebugEnabled()) {
1059 logger.debug(JexlException.methodError(node, method, args));
1060 }
1061 return null;
1062 }
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072 protected Object unsolvableProperty(final JexlNode node, final String property, final boolean undef, final Throwable cause) {
1073 if (isStrictEngine() && !isTernaryProtected(node)) {
1074 throw new JexlException.Property(node, property, undef, cause);
1075 }
1076 if (logger.isDebugEnabled()) {
1077 logger.debug(JexlException.propertyError(node, property, undef));
1078 }
1079 return null;
1080 }
1081
1082
1083
1084
1085
1086
1087
1088
1089 protected Object unsolvableVariable(final JexlNode node, final String var, final boolean undef) {
1090 return variableError(node, var, undef? VariableIssue.UNDEFINED : VariableIssue.NULLVALUE);
1091 }
1092
1093
1094
1095
1096
1097
1098
1099
1100 protected Object variableError(final JexlNode node, final String var, final VariableIssue issue) {
1101 if (isStrictEngine() && !isTernaryProtected(node)) {
1102 throw new JexlException.Variable(node, var, issue);
1103 }
1104 if (logger.isDebugEnabled()) {
1105 logger.debug(JexlException.variableError(node, var, issue));
1106 }
1107 return null;
1108 }
1109 }