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 org.apache.commons.jexl3.JexlArithmetic;
20 import org.apache.commons.jexl3.JexlEngine;
21 import org.apache.commons.jexl3.JexlException;
22 import org.apache.commons.jexl3.JexlOperator;
23 import org.apache.commons.jexl3.internal.introspection.MethodExecutor;
24 import org.apache.commons.jexl3.introspection.JexlMethod;
25 import org.apache.commons.jexl3.introspection.JexlUberspect;
26 import org.apache.commons.jexl3.parser.JexlNode;
27
28 import java.lang.reflect.Method;
29 import java.util.function.Consumer;
30
31
32
33
34
35 public class Operators {
36
37 protected final InterpreterBase interpreter;
38
39 protected final JexlArithmetic.Uberspect operators;
40
41
42
43
44
45 protected Operators(final InterpreterBase owner) {
46 final JexlArithmetic arithmetic = owner.arithmetic;
47 final JexlUberspect uberspect = owner.uberspect;
48 this.interpreter = owner;
49 this.operators = uberspect.getArithmetic(arithmetic);
50 }
51
52
53
54
55
56
57 private boolean returnsBoolean(final JexlMethod vm) {
58 if (vm !=null) {
59 final Class<?> rc = vm.getReturnType();
60 return Boolean.TYPE.equals(rc) || Boolean.class.equals(rc);
61 }
62 return false;
63 }
64
65
66
67
68
69
70 private boolean returnsInteger(final JexlMethod vm) {
71 if (vm !=null) {
72 final Class<?> rc = vm.getReturnType();
73 return Integer.TYPE.equals(rc) || Integer.class.equals(rc);
74 }
75 return false;
76 }
77
78
79
80
81
82
83 private boolean isArithmetic(final JexlMethod vm) {
84 if (vm instanceof MethodExecutor) {
85 final Method method = ((MethodExecutor) vm).getMethod();
86 return JexlArithmetic.class.equals(method.getDeclaringClass());
87 }
88 return false;
89 }
90
91
92
93
94
95
96
97
98 protected void controlNullOperands(final JexlArithmetic arithmetic, final JexlOperator operator, final Object...args) {
99 for (final Object arg : args) {
100
101 if (arg == null) {
102
103 if (arithmetic.isStrict(operator)) {
104 throw new JexlArithmetic.NullOperand();
105 }
106 break;
107 }
108 }
109 }
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124 protected Object tryOverload(final JexlNode node, final JexlOperator operator, final Object... args) {
125 final JexlArithmetic arithmetic = interpreter.arithmetic;
126 controlNullOperands(arithmetic, operator, args);
127 if (operators != null && operators.overloads(operator)) {
128 final boolean cache = interpreter.cache;
129 try {
130 if (cache) {
131 final Object cached = node.jjtGetValue();
132 if (cached instanceof JexlMethod) {
133 final JexlMethod me = (JexlMethod) cached;
134 final Object eval = me.tryInvoke(operator.getMethodName(), arithmetic, args);
135 if (!me.tryFailed(eval)) {
136 return eval;
137 }
138 }
139 }
140 final JexlMethod vm = operators.getOperator(operator, args);
141 if (vm != null && !isArithmetic(vm)) {
142 final Object result = vm.invoke(arithmetic, args);
143 if (cache && !vm.tryFailed(result)) {
144 node.jjtSetValue(vm);
145 }
146 return result;
147 }
148 } catch (final Exception xany) {
149
150 interpreter.operatorError(node, operator, xany);
151 }
152 }
153 return JexlEngine.TRY_FAILED;
154 }
155
156
157
158
159
160
161 private static boolean isPostfix(final JexlOperator operator) {
162 return operator == JexlOperator.GET_AND_INCREMENT || operator == JexlOperator.GET_AND_DECREMENT;
163 }
164
165
166
167
168
169
170
171
172 private Object[] arguments(final JexlOperator operator, final Object...args) {
173 return operator.getArity() == 1 && args.length > 1 ? new Object[]{args[0]} : args;
174 }
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190 protected Object tryAssignOverload(final JexlNode node,
191 final JexlOperator operator,
192 final Consumer<Object> assignFun,
193 final Object...args) {
194 final JexlArithmetic arithmetic = interpreter.arithmetic;
195 if (args.length < operator.getArity()) {
196 return JexlEngine.TRY_FAILED;
197 }
198 Object result;
199 try {
200
201 if (operators != null) {
202
203 result = tryOverload(node, operator, arguments(operator, args));
204 if (result != JexlEngine.TRY_FAILED) {
205 return result;
206 }
207
208 final JexlOperator base = operator.getBaseOperator();
209 if (base != null && operators.overloads(base)) {
210 result = tryOverload(node, base, arguments(base, args));
211 if (result != JexlEngine.TRY_FAILED) {
212 assignFun.accept(result);
213 return isPostfix(operator) ? args[0] : result;
214 }
215 }
216 }
217
218 switch (operator) {
219 case SELF_ADD:
220 result = arithmetic.add(args[0], args[1]);
221 break;
222 case SELF_SUBTRACT:
223 result = arithmetic.subtract(args[0], args[1]);
224 break;
225 case SELF_MULTIPLY:
226 result = arithmetic.multiply(args[0], args[1]);
227 break;
228 case SELF_DIVIDE:
229 result = arithmetic.divide(args[0], args[1]);
230 break;
231 case SELF_MOD:
232 result = arithmetic.mod(args[0], args[1]);
233 break;
234 case SELF_AND:
235 result = arithmetic.and(args[0], args[1]);
236 break;
237 case SELF_OR:
238 result = arithmetic.or(args[0], args[1]);
239 break;
240 case SELF_XOR:
241 result = arithmetic.xor(args[0], args[1]);
242 break;
243 case SELF_SHIFTLEFT:
244 result = arithmetic.shiftLeft(args[0], args[1]);
245 break;
246 case SELF_SHIFTRIGHT:
247 result = arithmetic.shiftRight(args[0], args[1]);
248 break;
249 case SELF_SHIFTRIGHTU:
250 result = arithmetic.shiftRightUnsigned(args[0], args[1]);
251 break;
252 case INCREMENT_AND_GET:
253 result = arithmetic.increment(args[0]);
254 break;
255 case DECREMENT_AND_GET:
256 result = arithmetic.decrement(args[0]);
257 break;
258 case GET_AND_INCREMENT:
259 result = args[0];
260 assignFun.accept(arithmetic.increment(result));
261 return result;
262 case GET_AND_DECREMENT: {
263 result = args[0];
264 assignFun.accept(arithmetic.decrement(result));
265 return result;
266 }
267 default:
268
269 throw new UnsupportedOperationException(operator.getOperatorSymbol());
270 }
271 assignFun.accept(result);
272 return result;
273 } catch (final Exception xany) {
274 interpreter.operatorError(node, operator, xany);
275 }
276 return JexlEngine.TRY_FAILED;
277 }
278
279
280
281
282
283
284
285
286
287 protected boolean startsWith(final JexlNode node, final String operator, final Object left, final Object right) {
288 final JexlArithmetic arithmetic = interpreter.arithmetic;
289 final JexlUberspect uberspect = interpreter.uberspect;
290 try {
291
292 final Object result = tryOverload(node, JexlOperator.STARTSWITH, left, right);
293 if (result instanceof Boolean) {
294 return (Boolean) result;
295 }
296
297 final Boolean matched = arithmetic.startsWith(left, right);
298 if (matched != null) {
299 return matched;
300 }
301
302 try {
303 final Object[] argv = {right};
304 JexlMethod vm = uberspect.getMethod(left, "startsWith", argv);
305 if (returnsBoolean(vm)) {
306 return (Boolean) vm.invoke(left, argv);
307 }
308 if (arithmetic.narrowArguments(argv)) {
309 vm = uberspect.getMethod(left, "startsWith", argv);
310 if (returnsBoolean(vm)) {
311 return (Boolean) vm.invoke(left, argv);
312 }
313 }
314 } catch (final Exception e) {
315 throw new JexlException(node, operator + " error", e);
316 }
317
318 return arithmetic.equals(left, right);
319 } catch (final ArithmeticException xrt) {
320 throw new JexlException(node, operator + " error", xrt);
321 }
322 }
323
324
325
326
327
328
329
330
331
332 protected boolean endsWith(final JexlNode node, final String operator, final Object left, final Object right) {
333 final JexlArithmetic arithmetic = interpreter.arithmetic;
334 final JexlUberspect uberspect = interpreter.uberspect;
335 try {
336
337 final Object result = tryOverload(node, JexlOperator.ENDSWITH, left, right);
338 if (result instanceof Boolean) {
339 return (Boolean) result;
340 }
341
342 final Boolean matched = arithmetic.endsWith(left, right);
343 if (matched != null) {
344 return matched;
345 }
346
347 try {
348 final Object[] argv = {right};
349 JexlMethod vm = uberspect.getMethod(left, "endsWith", argv);
350 if (returnsBoolean(vm)) {
351 return (Boolean) vm.invoke(left, argv);
352 }
353 if (arithmetic.narrowArguments(argv)) {
354 vm = uberspect.getMethod(left, "endsWith", argv);
355 if (returnsBoolean(vm)) {
356 return (Boolean) vm.invoke(left, argv);
357 }
358 }
359 } catch (final Exception e) {
360 throw new JexlException(node, operator + " error", e);
361 }
362
363 return arithmetic.equals(left, right);
364 } catch (final ArithmeticException xrt) {
365 throw new JexlException(node, operator + " error", xrt);
366 }
367 }
368
369
370
371
372
373
374
375
376
377
378
379
380
381 protected boolean contains(final JexlNode node, final String op, final Object left, final Object right) {
382 final JexlArithmetic arithmetic = interpreter.arithmetic;
383 final JexlUberspect uberspect = interpreter.uberspect;
384 try {
385
386 final Object result = tryOverload(node, JexlOperator.CONTAINS, left, right);
387 if (result instanceof Boolean) {
388 return (Boolean) result;
389 }
390
391 final Boolean matched = arithmetic.contains(left, right);
392 if (matched != null) {
393 return matched;
394 }
395
396 try {
397 final Object[] argv = {right};
398 JexlMethod vm = uberspect.getMethod(left, "contains", argv);
399 if (returnsBoolean(vm)) {
400 return (Boolean) vm.invoke(left, argv);
401 }
402 if (arithmetic.narrowArguments(argv)) {
403 vm = uberspect.getMethod(left, "contains", argv);
404 if (returnsBoolean(vm)) {
405 return (Boolean) vm.invoke(left, argv);
406 }
407 }
408 } catch (final Exception e) {
409 throw new JexlException(node, op + " error", e);
410 }
411
412 return arithmetic.equals(left, right);
413 } catch (final ArithmeticException xrt) {
414 throw new JexlException(node, op + " error", xrt);
415 }
416 }
417
418
419
420
421
422
423
424
425
426
427 protected Object empty(final JexlNode node, final Object object) {
428 if (object == null) {
429 return true;
430 }
431 Object result = tryOverload(node, JexlOperator.EMPTY, object);
432 if (result != JexlEngine.TRY_FAILED) {
433 return result;
434 }
435 final JexlArithmetic arithmetic = interpreter.arithmetic;
436 result = arithmetic.isEmpty(object, null);
437 if (result == null) {
438 final JexlUberspect uberspect = interpreter.uberspect;
439 result = false;
440
441
442 final JexlMethod vm = uberspect.getMethod(object, "isEmpty", InterpreterBase.EMPTY_PARAMS);
443 if (returnsBoolean(vm)) {
444 try {
445 result = vm.invoke(object, InterpreterBase.EMPTY_PARAMS);
446 } catch (final Exception xany) {
447 interpreter.operatorError(node, JexlOperator.EMPTY, xany);
448 }
449 }
450 }
451 return !(result instanceof Boolean) || (Boolean) result;
452 }
453
454
455
456
457
458
459
460
461
462
463 protected Object size(final JexlNode node, final Object object) {
464 if (object == null) {
465 return 0;
466 }
467 Object result = tryOverload(node, JexlOperator.SIZE, object);
468 if (result != JexlEngine.TRY_FAILED) {
469 return result;
470 }
471 final JexlArithmetic arithmetic = interpreter.arithmetic;
472 result = arithmetic.size(object, null);
473 if (result == null) {
474 final JexlUberspect uberspect = interpreter.uberspect;
475
476
477 final JexlMethod vm = uberspect.getMethod(object, "size", InterpreterBase.EMPTY_PARAMS);
478 if (returnsInteger(vm)) {
479 try {
480 result = vm.invoke(object, InterpreterBase.EMPTY_PARAMS);
481 } catch (final Exception xany) {
482 interpreter.operatorError(node, JexlOperator.SIZE, xany);
483 }
484 }
485 }
486 return result instanceof Number ? ((Number) result).intValue() : 0;
487 }
488 }