001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.jexl3;
019
020import java.util.function.Consumer;
021
022/**
023 * The JEXL operators.
024 *
025 * These are the operators that are executed by JexlArithmetic methods.
026 *
027 * <p>Each of them  associates a symbol to a method signature.
028 * For instance, '+' is associated to 'T add(L x, R y)'.</p>
029 *
030 * <p>The default JexlArithmetic implements generic versions of these methods using Object as arguments.
031 * You can use your own derived JexlArithmetic that override and/or overload those operator methods.
032 * Note that these are overloads by convention, not actual Java overloads.
033 * The following rules apply to all operator methods:</p>
034 * <ul>
035 * <li>Operator methods should be public</li>
036 * <li>Operators return type should be respected when primitive (int, boolean,...)</li>
037 * <li>Operators may be overloaded multiple times with different signatures</li>
038 * <li>Operators may return JexlEngine.TRY_AGAIN to fallback on default JEXL implementation</li>
039 * </ul>
040 *
041 * For side effect operators, operators that modify the left-hand size value (+=, -=, etc.), the user implemented
042 * overload methods may return:
043 * <ul>
044 *     <li>JexlEngine.TRY_FAIL to let the default fallback behavior be executed.</li>
045 *     <li>Any other value will be used as the new value to be assigned to the left-hand-side.</li>
046 * </ul>
047 * Note that side effect operators always return the left-hand side value (with an exception for postfix ++ and --).
048 *
049 * @since 3.0
050 */
051public enum JexlOperator {
052    /**
053     * Add operator.
054     * <br><strong>Syntax:</strong> {@code x + y}
055     * <br><strong>Method:</strong> {@code T add(L x, R y);}.
056     * @see JexlArithmetic#add(Object, Object)
057     */
058    ADD("+", "add", 2),
059
060    /**
061     * Subtract operator.
062     * <br><strong>Syntax:</strong> {@code x - y}
063     * <br><strong>Method:</strong> {@code T subtract(L x, R y);}.
064     * @see JexlArithmetic#subtract(Object, Object)
065     */
066    SUBTRACT("-", "subtract", 2),
067
068    /**
069     * Multiply operator.
070     * <br><strong>Syntax:</strong> {@code x * y}
071     * <br><strong>Method:</strong> {@code T multiply(L x, R y);}.
072     * @see JexlArithmetic#multiply(Object, Object)
073     */
074    MULTIPLY("*", "multiply", 2),
075
076    /**
077     * Divide operator.
078     * <br><strong>Syntax:</strong> {@code x / y}
079     * <br><strong>Method:</strong> {@code T divide(L x, R y);}.
080     * @see JexlArithmetic#divide(Object, Object)
081     */
082    DIVIDE("/", "divide", 2),
083
084    /**
085     * Modulo operator.
086     * <br><strong>Syntax:</strong> {@code x % y}
087     * <br><strong>Method:</strong> {@code T mod(L x, R y);}.
088     * @see JexlArithmetic#mod(Object, Object)
089     */
090    MOD("%", "mod", 2),
091
092    /**
093     * Bitwise-and operator.
094     * <br><strong>Syntax:</strong> {@code x & y}
095     * <br><strong>Method:</strong> {@code T and(L x, R y);}.
096     * @see JexlArithmetic#and(Object, Object)
097     */
098    AND("&", "and", 2),
099
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 &amp;= 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}