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 Exception xcause = null;
621 try {
622 final JexlOperator operator;
623 if (node != null) {
624
625 if (cache) {
626 final Object cached = node.jjtGetValue();
627 if (cached instanceof JexlPropertyGet) {
628 final JexlPropertyGet vg = (JexlPropertyGet) cached;
629 final Object value = vg.tryInvoke(object, attribute);
630 if (!vg.tryFailed(value)) {
631 return value;
632 }
633 }
634 }
635
636 operator = node.jjtGetParent() instanceof ASTArrayAccess ? JexlOperator.ARRAY_GET : JexlOperator.PROPERTY_GET;
637 final Object result = operators.tryOverload(node, operator, object, attribute);
638 if (result != JexlEngine.TRY_FAILED) {
639 return result;
640 }
641 } else {
642 operator = JexlOperator.PROPERTY_GET;
643 }
644
645 final List<JexlUberspect.PropertyResolver> resolvers = uberspect.getResolvers(operator, object);
646 final JexlPropertyGet vg = uberspect.getPropertyGet(resolvers, object, attribute);
647 if (vg != null) {
648 final Object value = vg.invoke(object);
649
650 if (node != null && cache && vg.isCacheable()) {
651 node.jjtSetValue(vg);
652 }
653 return value;
654 }
655 } catch (final Exception xany) {
656 xcause = xany;
657 }
658
659 if (node == null) {
660
661 final String error = "unable to get object property"
662 + ", class: " + object.getClass().getName()
663 + ", property: " + attribute;
664 throw new UnsupportedOperationException(error, xcause);
665 }
666 final boolean safe = node instanceof ASTIdentifierAccess && ((ASTIdentifierAccess) node).isSafe();
667 if (safe) {
668 return null;
669 }
670 final String attrStr = Objects.toString(attribute, null);
671 return unsolvableProperty(node, attrStr, true, xcause);
672 }
673
674
675
676
677
678
679
680
681
682 protected Object getVariable(final Frame frame, final LexicalScope block, final ASTIdentifier identifier) {
683 final int symbol = identifier.getSymbol();
684 final String name = identifier.getName();
685
686 if ((options.isLexicalShade() || identifier.isLexical()) && identifier.isShaded()) {
687 return undefinedVariable(identifier, name);
688 }
689
690 if (symbol >= 0 && frame.has(symbol)) {
691 final Object value = frame.get(symbol);
692
693 if (value != Scope.UNDEFINED) {
694
695 if (value == null && isStrictOperand(identifier)) {
696 return unsolvableVariable(identifier, name, false);
697 }
698 return value;
699 }
700 }
701
702 final Object value = context.get(name);
703
704 if (value == null) {
705
706 if (!context.has(name)) {
707
708 final boolean ignore = identifier.jjtGetParent() instanceof ASTReference
709 || isSafe() && (symbol >= 0 || identifier.jjtGetParent() instanceof ASTAssignment);
710 if (!ignore) {
711 return undefinedVariable(identifier, name);
712 }
713 } else if (isStrictOperand(identifier)) {
714 return unsolvableVariable(identifier, name, false);
715 }
716 }
717 return value;
718 }
719
720
721
722
723
724
725
726
727
728 protected JexlException invocationException(final JexlNode node, final String methodName, final Throwable xany) {
729 final Throwable cause = xany.getCause();
730 if (cause instanceof JexlException) {
731 return (JexlException) cause;
732 }
733 if (cause instanceof InterruptedException) {
734 return new JexlException.Cancel(node);
735 }
736 return new JexlException(node, methodName, xany);
737 }
738
739
740
741
742 protected boolean isCancellable() {
743 return options.isCancellable();
744 }
745
746
747
748
749
750
751 protected boolean isCancelled() {
752 return cancelled.get() || Thread.currentThread().isInterrupted();
753 }
754
755
756
757
758
759
760 protected boolean isSafe() {
761 return options.isSafe();
762 }
763
764
765
766
767
768
769 protected boolean isSilent() {
770 return options.isSilent();
771 }
772
773
774
775
776
777
778 protected boolean isStrictEngine() {
779 return options.isStrict();
780 }
781
782
783
784
785
786 protected boolean isStrictOperand(final JexlNode node) {
787 return node.jjtGetParent().isStrictOperator(arithmetic);
788 }
789
790
791
792
793
794
795
796
797
798
799
800 protected boolean isTernaryProtected(final JexlNode startNode) {
801 JexlNode node = startNode;
802 for (JexlNode walk = node.jjtGetParent(); walk != null; walk = walk.jjtGetParent()) {
803
804 if (walk instanceof ASTTernaryNode
805 || walk instanceof ASTNullpNode) {
806 return node == walk.jjtGetChild(0);
807 }
808 if (!(walk instanceof ASTReference || walk instanceof ASTArrayAccess)) {
809 break;
810 }
811 node = walk;
812 }
813 return false;
814 }
815
816
817
818
819
820
821
822
823
824
825
826 protected boolean isVariableDefined(final Frame frame, final LexicalScope block, final String name) {
827 if (frame != null && block != null) {
828 final Integer ref = frame.getScope().getSymbol(name);
829 final int symbol = ref != null ? ref : -1;
830 if (symbol >= 0 && block.hasSymbol(symbol)) {
831 final Object value = frame.get(symbol);
832 return value != Scope.UNDEFINED && value != Scope.UNDECLARED;
833 }
834 }
835 return context.has(name);
836 }
837
838
839
840
841
842
843
844
845
846 protected Object operatorError(final JexlNode node, final JexlOperator operator, final Throwable cause) {
847 if (isStrictEngine()) {
848 throw new JexlException.Operator(node, operator.getOperatorSymbol(), cause);
849 }
850 if (logger.isDebugEnabled()) {
851 logger.debug(JexlException.operatorError(node, operator.getOperatorSymbol()), cause);
852 }
853 return null;
854 }
855
856
857
858
859
860
861
862
863 protected Object redefinedVariable(final JexlNode node, final String variable) {
864 return variableError(node, variable, VariableIssue.REDEFINED);
865 }
866
867
868
869
870
871
872
873
874
875
876 protected Object resolveNamespace(final String prefix, final JexlNode node) {
877 Object namespace;
878
879 synchronized (this) {
880 if (functors != null) {
881 namespace = functors.get(prefix);
882 if (namespace != null) {
883 return namespace;
884 }
885 }
886 }
887
888 namespace = ns.resolveNamespace(prefix);
889 if (namespace == null) {
890 namespace = functions.get(prefix);
891 if (namespace == null) {
892 namespace = jexl.getNamespace(prefix);
893 }
894 if (prefix != null && namespace == null) {
895 throw new JexlException(node, "no such function namespace " + prefix, null);
896 }
897 }
898 Object functor = null;
899
900 if (namespace instanceof Class<?> || namespace instanceof String) {
901
902 final ASTIdentifier nsNode = (ASTIdentifier) node.jjtGetChild(0);
903 final boolean cacheable = cache && prefix != null;
904 final Object cached = cacheable ? nsNode.jjtGetValue() : null;
905
906 if (cached instanceof Class<?>) {
907 return cached;
908 }
909
910 if (cached instanceof JexlContext.NamespaceFunctor) {
911 final Object eval = ((JexlContext.NamespaceFunctor) cached).createFunctor(context);
912 if (JexlEngine.TRY_FAILED != eval) {
913 functor = eval;
914 namespace = cached;
915 }
916 }
917 if (functor == null) {
918
919 for (int tried = 0; tried < 2; ++tried) {
920 final boolean withContext = tried == 0;
921 final JexlMethod ctor = withContext
922 ? uberspect.getConstructor(namespace, context)
923 : uberspect.getConstructor(namespace);
924 if (ctor != null) {
925 try {
926 functor = withContext
927 ? ctor.invoke(namespace, context)
928 : ctor.invoke(namespace);
929
930 if (functor != null) {
931
932
933 final Object nsFinal = namespace;
934
935 namespace = (NamespaceFunctor) context -> withContext
936 ? ctor.tryInvoke(null, nsFinal, context)
937 : ctor.tryInvoke(null, nsFinal);
938 if (cacheable && ctor.isCacheable()) {
939 nsNode.jjtSetValue(namespace);
940 }
941 break;
942 }
943 } catch (final Exception xinst) {
944 throw new JexlException(node, "unable to instantiate namespace " + prefix, xinst);
945 }
946 }
947 }
948
949 if (functor == null) {
950 try {
951
952 if (namespace instanceof String) {
953 namespace = uberspect.getClassLoader().loadClass((String) namespace);
954 }
955
956 if (cacheable) {
957 nsNode.jjtSetValue(namespace);
958 }
959 } catch (final ClassNotFoundException e) {
960
961 throw new JexlException(node, "no such class namespace " + prefix, e);
962 }
963 }
964 }
965 }
966
967 if (functor == null && namespace instanceof JexlContext.NamespaceFunctor) {
968 functor = ((JexlContext.NamespaceFunctor) namespace).createFunctor(context);
969 }
970
971 if (functor != null) {
972 synchronized (this) {
973 if (functors == null) {
974 functors = new HashMap<>();
975 }
976 functors.put(prefix, functor);
977 }
978 return functor;
979 }
980 return namespace;
981 }
982
983
984
985
986
987
988
989
990
991 protected void setAttribute(final Object object, final Object attribute, final Object value, final JexlNode node) {
992 cancelCheck(node);
993 Exception xcause = null;
994 try {
995 final JexlOperator operator;
996 if (node != null) {
997
998 if (cache) {
999 final Object cached = node.jjtGetValue();
1000 if (cached instanceof JexlPropertySet) {
1001 final JexlPropertySet setter = (JexlPropertySet) cached;
1002 final Object eval = setter.tryInvoke(object, attribute, value);
1003 if (!setter.tryFailed(eval)) {
1004 return;
1005 }
1006 }
1007 }
1008
1009 operator = node.jjtGetParent() instanceof ASTArrayAccess ? JexlOperator.ARRAY_SET : JexlOperator.PROPERTY_SET;
1010 final Object result = operators.tryOverload(node, operator, object, attribute, value);
1011 if (result != JexlEngine.TRY_FAILED) {
1012 return;
1013 }
1014 } else {
1015 operator = JexlOperator.PROPERTY_SET;
1016 }
1017
1018 final List<JexlUberspect.PropertyResolver> resolvers = uberspect.getResolvers(operator, object);
1019 JexlPropertySet vs = uberspect.getPropertySet(resolvers, object, attribute, value);
1020
1021 if (vs == null) {
1022
1023 final Object[] narrow = {value};
1024 if (arithmetic.narrowArguments(narrow)) {
1025 vs = uberspect.getPropertySet(resolvers, object, attribute, narrow[0]);
1026 }
1027 }
1028 if (vs != null) {
1029
1030 vs.invoke(object, value);
1031 if (node != null && cache && vs.isCacheable()) {
1032 node.jjtSetValue(vs);
1033 }
1034 return;
1035 }
1036 } catch (final Exception xany) {
1037 xcause = xany;
1038 }
1039
1040 if (node == null) {
1041
1042 final String error = "unable to set object property"
1043 + ", class: " + object.getClass().getName()
1044 + ", property: " + attribute
1045 + ", argument: " + value.getClass().getSimpleName();
1046 throw new UnsupportedOperationException(error, xcause);
1047 }
1048 final String attrStr = Objects.toString(attribute, null);
1049 unsolvableProperty(node, attrStr, true, xcause);
1050 }
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061 protected void setContextVariable(final JexlNode node, final String name, final Object value) {
1062 boolean lexical = options.isLexicalShade();
1063 if (!lexical && node instanceof ASTIdentifier) {
1064 lexical = ((ASTIdentifier) node).isLexical();
1065 }
1066 if (lexical && !context.has(name)) {
1067 throw new JexlException.Variable(node, name, true);
1068 }
1069 try {
1070 context.set(name, value);
1071 } catch (final UnsupportedOperationException xsupport) {
1072 throw new JexlException(node, "context is readonly", xsupport);
1073 }
1074 }
1075
1076
1077
1078
1079
1080
1081
1082
1083 protected String stringifyProperty(final JexlNode node) {
1084 if (node instanceof ASTArrayAccess) {
1085 return "[" + stringifyPropertyValue(node.jjtGetChild(0)) + "]";
1086 }
1087 if (node instanceof ASTMethodNode) {
1088 return stringifyPropertyValue(node.jjtGetChild(0));
1089 }
1090 if (node instanceof ASTFunctionNode) {
1091 return stringifyPropertyValue(node.jjtGetChild(0));
1092 }
1093 if (node instanceof ASTIdentifier) {
1094 return ((ASTIdentifier) node).getName();
1095 }
1096 if (node instanceof ASTReference) {
1097 return stringifyProperty(node.jjtGetChild(0));
1098 }
1099 return stringifyPropertyValue(node);
1100 }
1101
1102
1103
1104
1105
1106
1107
1108
1109 protected Object undefinedVariable(final JexlNode node, final String variable) {
1110 return variableError(node, variable, VariableIssue.UNDEFINED);
1111 }
1112
1113
1114
1115
1116
1117
1118
1119
1120 protected Object unsolvableMethod(final JexlNode node, final String method) {
1121 return unsolvableMethod(node, method, null);
1122 }
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132 protected Object unsolvableMethod(final JexlNode node, final String method, final Object[] args) {
1133 if (isStrictEngine()) {
1134 throw new JexlException.Method(node, method, args);
1135 }
1136 if (logger.isDebugEnabled()) {
1137 logger.debug(JexlException.methodError(node, method, args));
1138 }
1139 return null;
1140 }
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151 protected Object unsolvableProperty(final JexlNode node, final String property, final boolean undef, final Throwable cause) {
1152 if (isStrictEngine() && !isTernaryProtected(node)) {
1153 throw new JexlException.Property(node, property, undef, cause);
1154 }
1155 if (logger.isDebugEnabled()) {
1156 logger.debug(JexlException.propertyError(node, property, undef));
1157 }
1158 return null;
1159 }
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169 protected Object unsolvableVariable(final JexlNode node, final String variable, final boolean undef) {
1170 return variableError(node, variable, undef? VariableIssue.UNDEFINED : VariableIssue.NULLVALUE);
1171 }
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181 protected Object variableError(final JexlNode node, final String variable, final VariableIssue issue) {
1182 if (isStrictEngine() && !isTernaryProtected(node)) {
1183 throw new JexlException.Variable(node, variable, issue);
1184 }
1185 if (logger.isDebugEnabled()) {
1186 logger.debug(JexlException.variableError(node, variable, issue));
1187 }
1188 return null;
1189 }
1190 }