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