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