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