1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.jexl3;
19
20 import java.util.function.Consumer;
21
22 /**
23 * The JEXL operators.
24 *
25 * These are the operators that are executed by JexlArithmetic methods.
26 *
27 * <p>Each of them associates a symbol to a method signature.
28 * For instance, '+' is associated to 'T add(L x, R y)'.</p>
29 *
30 * <p>The default JexlArithmetic implements generic versions of these methods using Object as arguments.
31 * You can use your own derived JexlArithmetic that override and/or overload those operator methods.
32 * Note that these are overloads by convention, not actual Java overloads.
33 * The following rules apply to all operator methods:</p>
34 * <ul>
35 * <li>Operator methods should be public</li>
36 * <li>Operators return type should be respected when primitive (int, boolean,...)</li>
37 * <li>Operators may be overloaded multiple times with different signatures</li>
38 * <li>Operators may return JexlEngine.TRY_AGAIN to fallback on default JEXL implementation</li>
39 * </ul>
40 *
41 * For side effect operators, operators that modify the left-hand size value (+=, -=, etc.), the user implemented
42 * overload methods may return:
43 * <ul>
44 * <li>JexlEngine.TRY_FAIL to let the default fallback behavior be executed.</li>
45 * <li>Any other value will be used as the new value to be assigned to the left-hand-side.</li>
46 * </ul>
47 * Note that side effect operators always return the left-hand side value (with an exception for postfix ++ and --).
48 *
49 * @since 3.0
50 */
51 public enum JexlOperator {
52 /**
53 * Add operator.
54 * <br><strong>Syntax:</strong> {@code x + y}
55 * <br><strong>Method:</strong> {@code T add(L x, R y);}.
56 * @see JexlArithmetic#add(Object, Object)
57 */
58 ADD("+", "add", 2),
59
60 /**
61 * Subtract operator.
62 * <br><strong>Syntax:</strong> {@code x - y}
63 * <br><strong>Method:</strong> {@code T subtract(L x, R y);}.
64 * @see JexlArithmetic#subtract(Object, Object)
65 */
66 SUBTRACT("-", "subtract", 2),
67
68 /**
69 * Multiply operator.
70 * <br><strong>Syntax:</strong> {@code x * y}
71 * <br><strong>Method:</strong> {@code T multiply(L x, R y);}.
72 * @see JexlArithmetic#multiply(Object, Object)
73 */
74 MULTIPLY("*", "multiply", 2),
75
76 /**
77 * Divide operator.
78 * <br><strong>Syntax:</strong> {@code x / y}
79 * <br><strong>Method:</strong> {@code T divide(L x, R y);}.
80 * @see JexlArithmetic#divide(Object, Object)
81 */
82 DIVIDE("/", "divide", 2),
83
84 /**
85 * Modulo operator.
86 * <br><strong>Syntax:</strong> {@code x % y}
87 * <br><strong>Method:</strong> {@code T mod(L x, R y);}.
88 * @see JexlArithmetic#mod(Object, Object)
89 */
90 MOD("%", "mod", 2),
91
92 /**
93 * Bitwise-and operator.
94 * <br><strong>Syntax:</strong> {@code x & y}
95 * <br><strong>Method:</strong> {@code T and(L x, R y);}.
96 * @see JexlArithmetic#and(Object, Object)
97 */
98 AND("&", "and", 2),
99
100 /**
101 * Bitwise-or operator.
102 * <br><strong>Syntax:</strong> {@code x | y}
103 * <br><strong>Method:</strong> {@code T or(L x, R y);}.
104 * @see JexlArithmetic#or(Object, Object)
105 */
106 OR("|", "or", 2),
107
108 /**
109 * Bitwise-xor operator.
110 * <br><strong>Syntax:</strong> {@code x ^ y}
111 * <br><strong>Method:</strong> {@code T xor(L x, R y);}.
112 * @see JexlArithmetic#xor(Object, Object)
113 */
114 XOR("^", "xor", 2),
115
116 /**
117 * Bit-pattern right-shift operator.
118 * <br><strong>Syntax:</strong> {@code x >> y}
119 * <br><strong>Method:</strong> {@code T rightShift(L x, R y);}.
120 * @see JexlArithmetic#shiftRight(Object, Object)
121 */
122 SHIFTRIGHT(">>", "shiftRight", 2),
123
124 /**
125 * Bit-pattern right-shift unsigned operator.
126 * <br><strong>Syntax:</strong> {@code x >>> y}
127 * <br><strong>Method:</strong> {@code T rightShiftUnsigned(L x, R y);}.
128 * @see JexlArithmetic#shiftRightUnsigned(Object, Object)
129 */
130 SHIFTRIGHTU(">>>", "shiftRightUnsigned", 2),
131
132 /**
133 * Bit-pattern left-shift operator.
134 * <br><strong>Syntax:</strong> {@code x << y}
135 * <br><strong>Method:</strong> {@code T leftShift(L x, R y);}.
136 * @see JexlArithmetic#shiftLeft(Object, Object)
137 */
138 SHIFTLEFT("<<", "shiftLeft", 2),
139
140 /**
141 * Equals operator.
142 * <br><strong>Syntax:</strong> {@code x == y}
143 * <br><strong>Method:</strong> {@code boolean equals(L x, R y);}.
144 * @see JexlArithmetic#equals(Object, Object)
145 */
146 EQ("==", "equals", 2),
147
148 /**
149 * Equal-strict operator.
150 * <br><strong>Syntax:</strong> {@code x === y}
151 * <br><strong>Method:</strong> {@code boolean strictEquals(L x, R y);}.
152 * @see JexlArithmetic#strictEquals(Object, Object)
153 */
154 EQSTRICT("===", "strictEquals", 2),
155
156 /**
157 * Less-than operator.
158 * <br><strong>Syntax:</strong> {@code x < y}
159 * <br><strong>Method:</strong> {@code boolean lessThan(L x, R y);}.
160 * @see JexlArithmetic#lessThan(Object, Object)
161 */
162 LT("<", "lessThan", 2),
163
164 /**
165 * Less-than-or-equal operator.
166 * <br><strong>Syntax:</strong> {@code x <= y}
167 * <br><strong>Method:</strong> {@code boolean lessThanOrEqual(L x, R y);}.
168 * @see JexlArithmetic#lessThanOrEqual(Object, Object)
169 */
170 LTE("<=", "lessThanOrEqual", 2),
171
172 /**
173 * Greater-than operator.
174 * <br><strong>Syntax:</strong> {@code x > y}
175 * <br><strong>Method:</strong> {@code boolean greaterThan(L x, R y);}.
176 * @see JexlArithmetic#greaterThan(Object, Object)
177 */
178 GT(">", "greaterThan", 2),
179
180 /**
181 * Greater-than-or-equal operator.
182 * <br><strong>Syntax:</strong> {@code x >= y}
183 * <br><strong>Method:</strong> {@code boolean greaterThanOrEqual(L x, R y);}.
184 * @see JexlArithmetic#greaterThanOrEqual(Object, Object)
185 */
186 GTE(">=", "greaterThanOrEqual", 2),
187
188 /**
189 * Contains operator.
190 * <br><strong>Syntax:</strong> {@code x =~ y}
191 * <br><strong>Method:</strong> {@code boolean contains(L x, R y);}.
192 * @see JexlArithmetic#contains(Object, Object)
193 */
194 CONTAINS("=~", "contains", 2),
195
196 /**
197 * Starts-with operator.
198 * <br><strong>Syntax:</strong> {@code x =^ y}
199 * <br><strong>Method:</strong> {@code boolean startsWith(L x, R y);}.
200 * @see JexlArithmetic#startsWith(Object, Object)
201 */
202 STARTSWITH("=^", "startsWith", 2),
203
204 /**
205 * Ends-with operator.
206 * <br><strong>Syntax:</strong> {@code x =$ y}
207 * <br><strong>Method:</strong> {@code boolean endsWith(L x, R y);}.
208 * @see JexlArithmetic#endsWith(Object, Object)
209 */
210 ENDSWITH("=$", "endsWith", 2),
211
212 /**
213 * Not operator.
214 * <br><strong>Syntax:</strong> {@code !x}
215 * <br><strong>Method:</strong> {@code T not(L x);}.
216 * @see JexlArithmetic#not(Object)
217 */
218 NOT("!", "not", 1),
219
220 /**
221 * Complement operator.
222 * <br><strong>Syntax:</strong> {@code ~x}
223 * <br><strong>Method:</strong> {@code T complement(L x);}.
224 * @see JexlArithmetic#complement(Object)
225 */
226 COMPLEMENT("~", "complement", 1),
227
228 /**
229 * Negate operator.
230 * <br><strong>Syntax:</strong> {@code -x}
231 * <br><strong>Method:</strong> {@code T negate(L x);}.
232 * @see JexlArithmetic#negate(Object)
233 */
234 NEGATE("-", "negate", 1),
235
236 /**
237 * Positivize operator.
238 * <br><strong>Syntax:</strong> {@code +x}
239 * <br><strong>Method:</strong> {@code T positivize(L x);}.
240 * @see JexlArithmetic#positivize(Object)
241 */
242 POSITIVIZE("+", "positivize", 1),
243
244 /**
245 * Empty operator.
246 * <br><strong>Syntax:</strong> {@code empty x} or {@code empty(x)}
247 * <br><strong>Method:</strong> {@code boolean empty(L x);}.
248 * @see JexlArithmetic#empty(Object)
249 */
250 EMPTY("empty", "empty", 1),
251
252 /**
253 * Size operator.
254 * <br><strong>Syntax:</strong> {@code size x} or {@code size(x)}
255 * <br><strong>Method:</strong> {@code int size(L x);}.
256 * @see JexlArithmetic#size(Object)
257 */
258 SIZE("size", "size", 1),
259
260 /**
261 * Self-add operator.
262 * <br><strong>Syntax:</strong> {@code x += y}
263 * <br><strong>Method:</strong> {@code T selfAdd(L x, R y);}.
264 */
265 SELF_ADD("+=", "selfAdd", ADD),
266
267 /**
268 * Self-subtract operator.
269 * <br><strong>Syntax:</strong> {@code x -= y}
270 * <br><strong>Method:</strong> {@code T selfSubtract(L x, R y);}.
271 */
272 SELF_SUBTRACT("-=", "selfSubtract", SUBTRACT),
273
274 /**
275 * Self-multiply operator.
276 * <br><strong>Syntax:</strong> {@code x *= y}
277 * <br><strong>Method:</strong> {@code T selfMultiply(L x, R y);}.
278 */
279 SELF_MULTIPLY("*=", "selfMultiply", MULTIPLY),
280
281 /**
282 * Self-divide operator.
283 * <br><strong>Syntax:</strong> {@code x /= y}
284 * <br><strong>Method:</strong> {@code T selfDivide(L x, R y);}.
285 */
286 SELF_DIVIDE("/=", "selfDivide", DIVIDE),
287
288 /**
289 * Self-modulo operator.
290 * <br><strong>Syntax:</strong> {@code x %= y}
291 * <br><strong>Method:</strong> {@code T selfMod(L x, R y);}.
292 */
293 SELF_MOD("%=", "selfMod", MOD),
294
295 /**
296 * Self-and operator.
297 * <br><strong>Syntax:</strong> {@code x &= y}
298 * <br><strong>Method:</strong> {@code T selfAnd(L x, R y);}.
299 */
300 SELF_AND("&=", "selfAnd", AND),
301
302 /**
303 * Self-or operator.
304 * <br><strong>Syntax:</strong> {@code x |= y}
305 * <br><strong>Method:</strong> {@code T selfOr(L x, R y);}.
306 */
307 SELF_OR("|=", "selfOr", OR),
308
309 /**
310 * Self-xor operator.
311 * <br><strong>Syntax:</strong> {@code x ^= y}
312 * <br><strong>Method:</strong> {@code T selfXor(L x, R y);}.
313 */
314 SELF_XOR("^=", "selfXor", XOR),
315
316 /**
317 * Self-right-shift operator.
318 * <br><strong>Syntax:</strong> {@code x >>= y}
319 * <br><strong>Method:</strong> {@code T selfShiftRight(L x, R y);}.
320 */
321 SELF_SHIFTRIGHT(">>=", "selfShiftRight", SHIFTRIGHT),
322
323 /**
324 * Self-right-shift unsigned operator.
325 * <br><strong>Syntax:</strong> {@code x >>> y}
326 * <br><strong>Method:</strong> {@code T selfShiftRightUnsigned(L x, R y);}.
327 */
328 SELF_SHIFTRIGHTU(">>>=", "selfShiftRightUnsigned", SHIFTRIGHTU),
329
330 /**
331 * Self-left-shift operator.
332 * <br><strong>Syntax:</strong> {@code x << y}
333 * <br><strong>Method:</strong> {@code T selfShiftLeft(L x, R y);}.
334 */
335 SELF_SHIFTLEFT("<<=", "selfShiftLeft", SHIFTLEFT),
336
337 /**
338 * Increment pseudo-operator.
339 * <br>No syntax, used as helper for the prefix and postfix versions of {@code ++}.
340 * @see JexlArithmetic#increment(Object)
341 */
342 INCREMENT("+1", "increment", 1),
343
344 /**
345 * Decrement pseudo-operator.
346 * <br>No syntax, used as helper for the prefix and postfix versions of {@code --}.
347 * @see JexlArithmetic#decrement(Object)
348 */
349 DECREMENT("-1", "decrement", 1),
350
351 /**
352 * Prefix ++ operator, increments and returns the value after incrementing.
353 * <br><strong>Syntax:</strong> {@code ++x}
354 * <br><strong>Method:</strong> {@code T incrementAndGet(L x);}.
355 */
356 INCREMENT_AND_GET("++.", "incrementAndGet", INCREMENT, 1),
357
358 /**
359 * Postfix ++, increments and returns the value before incrementing.
360 * <br><strong>Syntax:</strong> {@code x++}
361 * <br><strong>Method:</strong> {@code T getAndIncrement(L x);}.
362 */
363 GET_AND_INCREMENT(".++", "getAndIncrement", INCREMENT, 1),
364
365 /**
366 * Prefix --, decrements and returns the value after decrementing.
367 * <br><strong>Syntax:</strong> {@code --x}
368 * <br><strong>Method:</strong> {@code T decrementAndGet(L x);}.
369 */
370 DECREMENT_AND_GET("--.", "decrementAndGet", DECREMENT, 1),
371
372 /**
373 * Postfix --, decrements and returns the value before decrementing.
374 * <br><strong>Syntax:</strong> {@code x--}
375 * <br><strong>Method:</strong> {@code T getAndDecrement(L x);}.
376 */
377 GET_AND_DECREMENT(".--", "getAndDecrement", DECREMENT, 1),
378
379 /**
380 * Marker for side effect.
381 * <p>
382 * Returns this from 'self*' overload method to let the engine know the side effect has been performed and
383 * there is no need to assign the result.
384 * </p>
385 *
386 * @deprecated 3.5.0
387 */
388 @Deprecated
389 ASSIGN("=", null, null),
390
391 /**
392 * Property get operator as in: x.y.
393 * <br><strong>Syntax:</strong> {@code x.y}
394 * <br><strong>Method:</strong> {@code Object propertyGet(L x, R y);}.
395 */
396 PROPERTY_GET(".", "propertyGet", 2),
397
398 /**
399 * Property set operator as in: x.y = z.
400 * <br><strong>Syntax:</strong> {@code x.y = z}
401 * <br><strong>Method:</strong> {@code void propertySet(L x, R y, V z);}.
402 */
403 PROPERTY_SET(".=", "propertySet", 3),
404
405 /**
406 * Array get operator as in: x[y].
407 * <br><strong>Syntax:</strong> {@code x.y}
408 * <br><strong>Method:</strong> {@code Object arrayGet(L x, R y);}.
409 */
410 ARRAY_GET("[]", "arrayGet", 2),
411
412 /**
413 * Array set operator as in: x[y] = z.
414 * <br><strong>Syntax:</strong> {@code x[y] = z}
415 * <br><strong>Method:</strong> {@code void arraySet(L x, R y, V z);}.
416 */
417 ARRAY_SET("[]=", "arraySet", 3),
418
419 /**
420 * Iterator generator as in for(var x : y).
421 * If the returned Iterator is AutoCloseable, close will be called after the last execution of the loop block.
422 * <br><strong>Syntax:</strong> <code>for(var x : y){...}</code>
423 * <br><strong>Method:</strong> {@code Iterator<Object> forEach(R y);}.
424 * @since 3.1
425 */
426 FOR_EACH("for(...)", "forEach", 1),
427
428 /**
429 * Test condition in if, for, while.
430 * <br><strong>Method:</strong> {@code boolean testCondition(R y);}.
431 * @since 3.3
432 */
433 CONDITION("?", "testCondition", 1),
434
435 /**
436 * Compare overload as in compare(x, y).
437 * <br><strong>Method:</strong> {@code boolean compare(L x, R y);}.
438 * @since 3.5.0
439 */
440 COMPARE("<>", "compare", 2),
441
442 /**
443 * Not-Contains operator.
444 * <p>Not overridable, calls !(contain(...))</p>
445 */
446 NOT_CONTAINS("!~", null, CONTAINS),
447
448 /**
449 * Not-Starts-With operator.
450 * <p>Not overridable, calls !(startsWith(...))</p>
451 */
452 NOT_STARTSWITH("!^", null, STARTSWITH),
453
454 /**
455 * Not-Ends-With operator.
456 * <p>Not overridable, calls !(endsWith(...))</p>
457 */
458 NOT_ENDSWITH("!$", null, ENDSWITH),;
459
460 /**
461 * The operator symbol.
462 */
463 private final String operator;
464
465 /**
466 * The associated operator method name.
467 */
468 private final String methodName;
469
470 /**
471 * The method arity (ie number of arguments).
472 */
473 private final int arity;
474
475 /**
476 * The base operator.
477 */
478 private final JexlOperator base;
479
480 /**
481 * Creates a base operator.
482 *
483 * @param o the operator name
484 * @param m the method name associated to this operator in a JexlArithmetic
485 * @param argc the number of parameters for the method
486 */
487 JexlOperator(final String o, final String m, final int argc) {
488 this(o, m, null, argc);
489 }
490
491 /**
492 * Creates a side effect operator with arity == 2.
493 *
494 * @param o the operator name
495 * @param m the method name associated to this operator in a JexlArithmetic
496 * @param b the base operator, ie + for +=
497 */
498 JexlOperator(final String o, final String m, final JexlOperator b) {
499 this(o, m, b, 2);
500 }
501
502 /**
503 * Creates a side effect operator.
504 *
505 * @param o the operator name
506 * @param m the method name associated to this operator in a JexlArithmetic
507 * @param b the base operator, ie + for +=
508 * @param a the operator arity
509 */
510 JexlOperator(final String o, final String m, final JexlOperator b, final int a) {
511 this.operator = o;
512 this.methodName = m;
513 this.arity = a;
514 this.base = b;
515 }
516
517 /**
518 * Gets this operator number of parameters.
519 *
520 * @return the method arity
521 */
522 public int getArity() {
523 return arity;
524 }
525
526 /**
527 * Gets the base operator.
528 *
529 * @return the base operator
530 */
531 public final JexlOperator getBaseOperator() {
532 return base;
533 }
534
535 /**
536 * Gets this operator method name in a JexlArithmetic.
537 *
538 * @return the method name
539 */
540 public final String getMethodName() {
541 return methodName;
542 }
543
544 /**
545 * Gets this operator symbol.
546 *
547 * @return the symbol
548 */
549 public final String getOperatorSymbol() {
550 return operator;
551 }
552
553 /**
554 * Uberspect that solves and evaluates JexlOperator overloads.
555 * <p>This is used by the interpreter to find and execute operator overloads implemented in a derived
556 * JexlArithmetic - or in some cases, as methods of the left argument type (contains, size, ...).</p>
557 * <p>This also allows reusing the core logic when extending the applicative type-system; for
558 * instance, implementing a Comparator class that calls compare
559 * (<code>operator.tryOverload(this, JexlOperator.COMPARE, left, right)</code>, etc.</p>
560 * @since 3.5.0
561 */
562 public interface Uberspect extends JexlArithmetic.Uberspect {
563 /**
564 * Try to find the most specific method and evaluate an operator.
565 * <p>This method does not call {@link #overloads(JexlOperator)} and shall not be called with an
566 * assignment operator; use {@link #tryAssignOverload(JexlCache.Reference, JexlOperator, Consumer, Object...)}
567 * in that case.</p>
568 *
569 * @param reference an optional reference caching resolved method or failing signature
570 * @param operator the operator
571 * @param args the arguments
572 * @return TRY_FAILED if no specific method could be found, the evaluation result otherwise
573 */
574 Object tryOverload(JexlCache.Reference reference, JexlOperator operator, Object...args);
575
576 /**
577 * Evaluates an assign operator.
578 * <p>
579 * This takes care of finding and caching the operator method when appropriate.
580 * If an overloads returns a value not-equal to TRY_FAILED, it means the side-effect is complete.
581 * Otherwise, {@code a += b <=> a = a + b}
582 * </p>
583 * @param node an optional reference caching resolved method or failing signature
584 * @param operator the operator
585 * @param assign the actual function that performs the side effect
586 * @param args the arguments, the first one being the target of assignment
587 * @return JexlEngine.TRY_FAILED if no operation was performed,
588 * the value to use as the side effect argument otherwise
589 */
590 Object tryAssignOverload(final JexlCache.Reference node,
591 final JexlOperator operator,
592 final Consumer<Object> assign,
593 final Object... args);
594
595 /**
596 * Calculate the {@code size} of various types:
597 * Collection, Array, Map, String, and anything that has an int size() method.
598 * <p>Seeks an overload or use the default arithmetic implementation.</p>
599 * <p>Note that the result may not be an integer.
600 *
601 * @param node an optional reference caching resolved method or failing signature
602 * @param object the object to get the size of
603 * @return the evaluation result
604 */
605 Object size(final JexlCache.Reference node, final Object object);
606
607 /**
608 * Check for emptiness of various types: Collection, Array, Map, String, and anything that has a boolean isEmpty()
609 * method.
610 * <p>Seeks an overload or use the default arithmetic implementation.</p>
611 * <p>Note that the result may not be a boolean.
612 *
613 * @param node the node holding the object
614 * @param object the object to check the emptiness of
615 * @return the evaluation result
616 */
617 Object empty(final JexlCache.Reference node, final Object object);
618
619 /**
620 * The 'match'/'in' operator implementation.
621 * <p>Seeks an overload or use the default arithmetic implementation.</p>
622 * <p>
623 * Note that 'x in y' or 'x matches y' means 'y contains x' ;
624 * the JEXL operator arguments order syntax is the reverse of this method call.
625 * </p>
626 * @param node an optional reference caching resolved method or failing signature
627 * @param operator the calling operator, =~ or !~
628 * @param right the left operand
629 * @param left the right operand
630 * @return true if left matches right, false otherwise
631 */
632 boolean contains(final JexlCache.Reference node,
633 final JexlOperator operator,
634 final Object left,
635 final Object right);
636
637 /**
638 * The 'startsWith' operator implementation.
639 * <p>Seeks an overload or use the default arithmetic implementation.</p>
640 * @param node an optional reference caching resolved method or failing signature
641 * @param operator the calling operator, $= or $!
642 * @param left the left operand
643 * @param right the right operand
644 * @return true if left starts with right, false otherwise
645 */
646 boolean startsWith(final JexlCache.Reference node,
647 final JexlOperator operator,
648 final Object left,
649 final Object right);
650
651 /**
652 * The 'endsWith' operator implementation.
653 * <p>Seeks an overload or use the default arithmetic implementation.</p>
654 * @param node an optional reference caching resolved method or failing signature
655 * @param operator the calling operator, ^= or ^!
656 * @param left the left operand
657 * @param right the right operand
658 * @return true if left ends with right, false otherwise
659 */
660 boolean endsWith(final JexlCache.Reference node,
661 final JexlOperator operator,
662 final Object left,
663 final Object right);
664 }
665 }