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    /**
054     * Add operator.
055     * <br><strong>Syntax:</strong> {@code x + y}
056     * <br><strong>Method:</strong> {@code T add(L x, R y);}.
057     *
058     * @see JexlArithmetic#add(Object, Object)
059     */
060    ADD("+", "add", 2),
061
062    /**
063     * Subtract operator.
064     * <br><strong>Syntax:</strong> {@code x - y}
065     * <br><strong>Method:</strong> {@code T subtract(L x, R y);}.
066     *
067     * @see JexlArithmetic#subtract(Object, Object)
068     */
069    SUBTRACT("-", "subtract", 2),
070
071    /**
072     * Multiply operator.
073     * <br><strong>Syntax:</strong> {@code x * y}
074     * <br><strong>Method:</strong> {@code T multiply(L x, R y);}.
075     *
076     * @see JexlArithmetic#multiply(Object, Object)
077     */
078    MULTIPLY("*", "multiply", 2),
079
080    /**
081     * Divide operator.
082     * <br><strong>Syntax:</strong> {@code x / y}
083     * <br><strong>Method:</strong> {@code T divide(L x, R y);}.
084     *
085     * @see JexlArithmetic#divide(Object, Object)
086     */
087    DIVIDE("/", "divide", 2),
088
089    /**
090     * Modulo operator.
091     * <br><strong>Syntax:</strong> {@code x % y}
092     * <br><strong>Method:</strong> {@code T mod(L x, R y);}.
093     *
094     * @see JexlArithmetic#mod(Object, Object)
095     */
096    MOD("%", "mod", 2),
097
098    /**
099     * Bitwise-and operator.
100     * <br><strong>Syntax:</strong> {@code x & y}
101     * <br><strong>Method:</strong> {@code T and(L x, R y);}.
102     *
103     * @see JexlArithmetic#and(Object, Object)
104     */
105    AND("&", "and", 2),
106
107    /**
108     * Bitwise-or operator.
109     * <br><strong>Syntax:</strong> {@code x | y}
110     * <br><strong>Method:</strong> {@code T or(L x, R y);}.
111     *
112     * @see JexlArithmetic#or(Object, Object)
113     */
114    OR("|", "or", 2),
115
116    /**
117     * Bitwise-xor operator.
118     * <br><strong>Syntax:</strong> {@code x ^ y}
119     * <br><strong>Method:</strong> {@code T xor(L x, R y);}.
120     *
121     * @see JexlArithmetic#xor(Object, Object)
122     */
123    XOR("^", "xor", 2),
124
125    /**
126     * Bit-pattern right-shift operator.
127     * <br><strong>Syntax:</strong> {@code x >> y}
128     * <br><strong>Method:</strong> {@code T rightShift(L x, R y);}.
129     *
130     * @see JexlArithmetic#shiftRight(Object, Object)
131     */
132    SHIFTRIGHT(">>", "shiftRight", 2),
133
134    /**
135     * Bit-pattern right-shift unsigned operator.
136     * <br><strong>Syntax:</strong> {@code x >>> y}
137     * <br><strong>Method:</strong> {@code T rightShiftUnsigned(L x, R y);}.
138     *
139     * @see JexlArithmetic#shiftRightUnsigned(Object, Object)
140     */
141    SHIFTRIGHTU(">>>", "shiftRightUnsigned", 2),
142
143    /**
144     * Bit-pattern left-shift operator.
145     * <br><strong>Syntax:</strong> {@code x << y}
146     * <br><strong>Method:</strong> {@code T leftShift(L x, R y);}.
147     *
148     * @see JexlArithmetic#shiftLeft(Object, Object)
149     */
150    SHIFTLEFT("<<", "shiftLeft", 2),
151
152    /**
153     * Equals operator.
154     * <br><strong>Syntax:</strong> {@code x == y}
155     * <br><strong>Method:</strong> {@code boolean equals(L x, R y);}.
156     *
157     * @see JexlArithmetic#equals(Object, Object)
158     */
159    EQ("==", "equals", 2),
160
161    /**
162     * Equal-strict operator.
163     * <br><strong>Syntax:</strong> {@code x === y}
164     * <br><strong>Method:</strong> {@code boolean strictEquals(L x, R y);}.
165     *
166     * @see JexlArithmetic#strictEquals(Object, Object)
167     */
168    EQSTRICT("===", "strictEquals", 2),
169
170    /**
171     * Less-than operator.
172     * <br><strong>Syntax:</strong> {@code x < y}
173     * <br><strong>Method:</strong> {@code boolean lessThan(L x, R y);}.
174     *
175     * @see JexlArithmetic#lessThan(Object, Object)
176     */
177    LT("<", "lessThan", 2),
178
179    /**
180     * Less-than-or-equal operator.
181     * <br><strong>Syntax:</strong> {@code x <= y}
182     * <br><strong>Method:</strong> {@code boolean lessThanOrEqual(L x, R y);}.
183     *
184     * @see JexlArithmetic#lessThanOrEqual(Object, Object)
185     */
186    LTE("<=", "lessThanOrEqual", 2),
187
188    /**
189     * Greater-than operator.
190     * <br><strong>Syntax:</strong> {@code x > y}
191     * <br><strong>Method:</strong> {@code boolean greaterThan(L x, R y);}.
192     *
193     * @see JexlArithmetic#greaterThan(Object, Object)
194     */
195    GT(">", "greaterThan", 2),
196
197    /**
198     * Greater-than-or-equal operator.
199     * <br><strong>Syntax:</strong> {@code x >= y}
200     * <br><strong>Method:</strong> {@code boolean greaterThanOrEqual(L x, R y);}.
201     *
202     * @see JexlArithmetic#greaterThanOrEqual(Object, Object)
203     */
204    GTE(">=", "greaterThanOrEqual", 2),
205
206    /**
207     * Contains operator.
208     * <br><strong>Syntax:</strong> {@code x =~ y}
209     * <br><strong>Method:</strong> {@code boolean contains(L x, R y);}.
210     *
211     * @see JexlArithmetic#contains(Object, Object)
212     */
213    CONTAINS("=~", "contains", 2),
214
215    /**
216     * Starts-with operator.
217     * <br><strong>Syntax:</strong> {@code x =^ y}
218     * <br><strong>Method:</strong> {@code boolean startsWith(L x, R y);}.
219     *
220     * @see JexlArithmetic#startsWith(Object, Object)
221     */
222    STARTSWITH("=^", "startsWith", 2),
223
224    /**
225     * Ends-with operator.
226     * <br><strong>Syntax:</strong> {@code x =$ y}
227     * <br><strong>Method:</strong> {@code boolean endsWith(L x, R y);}.
228     *
229     * @see JexlArithmetic#endsWith(Object, Object)
230     */
231    ENDSWITH("=$", "endsWith", 2),
232
233    /**
234     * Not operator.
235     * <br><strong>Syntax:</strong> {@code !x}
236     * <br><strong>Method:</strong> {@code T not(L x);}.
237     *
238     * @see JexlArithmetic#not(Object)
239     */
240    NOT("!", "not", 1),
241
242    /**
243     * Complement operator.
244     * <br><strong>Syntax:</strong> {@code ~x}
245     * <br><strong>Method:</strong> {@code T complement(L x);}.
246     *
247     * @see JexlArithmetic#complement(Object)
248     */
249    COMPLEMENT("~", "complement", 1),
250
251    /**
252     * Negate operator.
253     * <br><strong>Syntax:</strong> {@code -x}
254     * <br><strong>Method:</strong> {@code T negate(L x);}.
255     *
256     * @see JexlArithmetic#negate(Object)
257     */
258    NEGATE("-", "negate", 1),
259
260    /**
261     * Positivize operator.
262     * <br><strong>Syntax:</strong> {@code +x}
263     * <br><strong>Method:</strong> {@code T positivize(L x);}.
264     *
265     * @see JexlArithmetic#positivize(Object)
266     */
267    POSITIVIZE("+", "positivize", 1),
268
269    /**
270     * Empty operator.
271     * <br><strong>Syntax:</strong> {@code empty x} or {@code empty(x)}
272     * <br><strong>Method:</strong> {@code boolean empty(L x);}.
273     *
274     * @see JexlArithmetic#empty(Object)
275     */
276    EMPTY("empty", "empty", 1),
277
278    /**
279     * Size operator.
280     * <br><strong>Syntax:</strong> {@code size x} or {@code size(x)}
281     * <br><strong>Method:</strong> {@code int size(L x);}.
282     *
283     * @see JexlArithmetic#size(Object)
284     */
285    SIZE("size", "size", 1),
286
287    /**
288     * Self-add operator.
289     * <br><strong>Syntax:</strong> {@code x += y}
290     * <br><strong>Method:</strong> {@code T selfAdd(L x, R y);}.
291     */
292    SELF_ADD("+=", "selfAdd", ADD),
293
294    /**
295     * Self-subtract operator.
296     * <br><strong>Syntax:</strong> {@code x -= y}
297     * <br><strong>Method:</strong> {@code T selfSubtract(L x, R y);}.
298     */
299    SELF_SUBTRACT("-=", "selfSubtract", SUBTRACT),
300
301    /**
302     * Self-multiply operator.
303     * <br><strong>Syntax:</strong> {@code x *= y}
304     * <br><strong>Method:</strong> {@code T selfMultiply(L x, R y);}.
305     */
306    SELF_MULTIPLY("*=", "selfMultiply", MULTIPLY),
307
308    /**
309     * Self-divide operator.
310     * <br><strong>Syntax:</strong> {@code x /= y}
311     * <br><strong>Method:</strong> {@code T selfDivide(L x, R y);}.
312     */
313    SELF_DIVIDE("/=", "selfDivide", DIVIDE),
314
315    /**
316     * Self-modulo operator.
317     * <br><strong>Syntax:</strong> {@code x %= y}
318     * <br><strong>Method:</strong> {@code T selfMod(L x, R y);}.
319     */
320    SELF_MOD("%=", "selfMod", MOD),
321
322    /**
323     * Self-and operator.
324     * <br><strong>Syntax:</strong> {@code x &amp;= y}
325     * <br><strong>Method:</strong> {@code T selfAnd(L x, R y);}.
326     */
327    SELF_AND("&=", "selfAnd", AND),
328
329    /**
330     * Self-or operator.
331     * <br><strong>Syntax:</strong> {@code x |= y}
332     * <br><strong>Method:</strong> {@code T selfOr(L x, R y);}.
333     */
334    SELF_OR("|=", "selfOr", OR),
335
336    /**
337     * Self-xor operator.
338     * <br><strong>Syntax:</strong> {@code x ^= y}
339     * <br><strong>Method:</strong> {@code T selfXor(L x, R y);}.
340     */
341    SELF_XOR("^=", "selfXor", XOR),
342
343    /**
344     * Self-right-shift operator.
345     * <br><strong>Syntax:</strong> {@code x >>= y}
346     * <br><strong>Method:</strong> {@code T selfShiftRight(L x, R y);}.
347     */
348    SELF_SHIFTRIGHT(">>=", "selfShiftRight", SHIFTRIGHT),
349
350    /**
351     * Self-right-shift unsigned operator.
352     * <br><strong>Syntax:</strong> {@code x >>> y}
353     * <br><strong>Method:</strong> {@code T selfShiftRightUnsigned(L x, R y);}.
354     */
355    SELF_SHIFTRIGHTU(">>>=", "selfShiftRightUnsigned", SHIFTRIGHTU),
356
357    /**
358     * Self-left-shift operator.
359     * <br><strong>Syntax:</strong> {@code x << y}
360     * <br><strong>Method:</strong> {@code T selfShiftLeft(L x, R y);}.
361     */
362    SELF_SHIFTLEFT("<<=", "selfShiftLeft", SHIFTLEFT),
363
364    /**
365     * Increment pseudo-operator.
366     * <br>No syntax, used as helper for the prefix and postfix versions of {@code ++}.
367     *
368     * @see JexlArithmetic#increment(Object)
369     */
370    INCREMENT("+1", "increment", 1),
371
372    /**
373     * Decrement pseudo-operator.
374     * <br>No syntax, used as helper for the prefix and postfix versions of {@code --}.
375     *
376     * @see JexlArithmetic#decrement(Object)
377     */
378    DECREMENT("-1", "decrement", 1),
379
380    /**
381     * Prefix ++ operator, increments and returns the value after incrementing.
382     * <br><strong>Syntax:</strong> {@code ++x}
383     * <br><strong>Method:</strong> {@code T incrementAndGet(L x);}.
384     */
385    INCREMENT_AND_GET("++.", "incrementAndGet", INCREMENT, 1),
386
387    /**
388     * Postfix ++, increments and returns the value before incrementing.
389     * <br><strong>Syntax:</strong> {@code x++}
390     * <br><strong>Method:</strong> {@code T getAndIncrement(L x);}.
391     */
392    GET_AND_INCREMENT(".++", "getAndIncrement", INCREMENT, 1),
393
394    /**
395     * Prefix --, decrements and returns the value after decrementing.
396     * <br><strong>Syntax:</strong> {@code --x}
397     * <br><strong>Method:</strong> {@code T decrementAndGet(L x);}.
398     */
399    DECREMENT_AND_GET("--.", "decrementAndGet", DECREMENT, 1),
400
401    /**
402     * Postfix --, decrements and returns the value before decrementing.
403     * <br><strong>Syntax:</strong> {@code x--}
404     * <br><strong>Method:</strong> {@code T getAndDecrement(L x);}.
405     */
406    GET_AND_DECREMENT(".--", "getAndDecrement", DECREMENT, 1),
407
408    /**
409     * Marker for side effect.
410     * <p>
411     * Returns this from 'self*' overload method to let the engine know the side effect has been performed and
412     * there is no need to assign the result.
413     * </p>
414     *
415     * @deprecated 3.5.0
416     */
417    @Deprecated
418    ASSIGN("=", null, null),
419
420    /**
421     * Property get operator as in: x.y.
422     * <br><strong>Syntax:</strong> {@code x.y}
423     * <br><strong>Method:</strong> {@code Object propertyGet(L x, R y);}.
424     */
425    PROPERTY_GET(".", "propertyGet", 2),
426
427    /**
428     * Property set operator as in: x.y = z.
429     * <br><strong>Syntax:</strong> {@code x.y = z}
430     * <br><strong>Method:</strong> {@code void propertySet(L x, R y, V z);}.
431     */
432    PROPERTY_SET(".=", "propertySet", 3),
433
434    /**
435     * Array get operator as in: x[y].
436     * <br><strong>Syntax:</strong> {@code x.y}
437     * <br><strong>Method:</strong> {@code Object arrayGet(L x, R y);}.
438     */
439    ARRAY_GET("[]", "arrayGet", 2),
440
441    /**
442     * Array set operator as in: x[y] = z.
443     * <br><strong>Syntax:</strong> {@code x[y] = z}
444     * <br><strong>Method:</strong> {@code void arraySet(L x, R y, V z);}.
445     */
446    ARRAY_SET("[]=", "arraySet", 3),
447
448    /**
449     * Iterator generator as in for(var x : y).
450     * If the returned Iterator is AutoCloseable, close will be called after the last execution of the loop block.
451     * <br><strong>Syntax:</strong> <code>for(var x : y){...}</code>
452     * <br><strong>Method:</strong> {@code Iterator<Object> forEach(R y);}.
453     *
454     * @since 3.1
455     */
456    FOR_EACH("for(...)", "forEach", 1),
457
458    /**
459     * Test condition in if, for, while.
460     * <br><strong>Method:</strong> {@code boolean testCondition(R y);}.
461     *
462     * @since 3.3
463     */
464    CONDITION("?", "testCondition", 1),
465
466    /**
467     * Compare overload as in compare(x, y).
468     * <br><strong>Method:</strong> {@code boolean compare(L x, R y);}.
469     *
470     * @since 3.5.0
471     */
472    COMPARE("<>", "compare", 2),
473
474    /**
475     * Not-Contains operator.
476     * <p>Not overridable, calls !(contain(...))</p>
477     */
478    NOT_CONTAINS("!~", null, CONTAINS),
479
480    /**
481     * Not-Starts-With operator.
482     * <p>Not overridable, calls !(startsWith(...))</p>
483     */
484    NOT_STARTSWITH("!^", null, STARTSWITH),
485
486    /**
487     * Not-Ends-With operator.
488     * <p>Not overridable, calls !(endsWith(...))</p>
489     */
490    NOT_ENDSWITH("!$", null, ENDSWITH),;
491
492    /**
493     * The operator symbol.
494     */
495    private final String operator;
496
497    /**
498     * The associated operator method name.
499     */
500    private final String methodName;
501
502    /**
503     * The method arity (ie number of arguments).
504     */
505    private final int arity;
506
507    /**
508     * The base operator.
509     */
510    private final JexlOperator base;
511
512    /**
513     * Creates a base operator.
514     *
515     * @param o    the operator name
516     * @param m    the method name associated to this operator in a JexlArithmetic
517     * @param argc the number of parameters for the method
518     */
519    JexlOperator(final String o, final String m, final int argc) {
520        this(o, m, null, argc);
521    }
522
523    /**
524     * Creates a side effect operator with arity == 2.
525     *
526     * @param o the operator name
527     * @param m the method name associated to this operator in a JexlArithmetic
528     * @param b the base operator, ie + for +=
529     */
530    JexlOperator(final String o, final String m, final JexlOperator b) {
531        this(o, m, b, 2);
532    }
533
534    /**
535     * Creates a side effect operator.
536     *
537     * @param o the operator name
538     * @param m the method name associated to this operator in a JexlArithmetic
539     * @param b the base operator, ie + for +=
540     * @param a the operator arity
541     */
542    JexlOperator(final String o, final String m, final JexlOperator b, final int a) {
543        this.operator = o;
544        this.methodName = m;
545        this.arity = a;
546        this.base = b;
547    }
548
549    /**
550     * Gets this operator number of parameters.
551     *
552     * @return the method arity
553     */
554    public int getArity() {
555        return arity;
556    }
557
558    /**
559     * Gets the base operator.
560     *
561     * @return the base operator
562     */
563    public final JexlOperator getBaseOperator() {
564        return base;
565    }
566
567    /**
568     * Gets this operator method name in a JexlArithmetic.
569     *
570     * @return the method name
571     */
572    public final String getMethodName() {
573        return methodName;
574    }
575
576    /**
577     * Gets this operator symbol.
578     *
579     * @return the symbol
580     */
581    public final String getOperatorSymbol() {
582        return operator;
583    }
584
585    /**
586     * Uberspect that solves and evaluates JexlOperator overloads.
587     * <p>This is used by the interpreter to find and execute operator overloads implemented in a derived
588     * JexlArithmetic - or in some cases, as methods of the left argument type (contains, size, ...).</p>
589     * <p>This also allows reusing the core logic when extending the applicative type-system; for
590     * instance, implementing a Comparator class that calls compare
591     * (<code>operator.tryOverload(this, JexlOperator.COMPARE, left, right)</code>, etc.</p>
592     *
593     * @since 3.5.0
594     */
595    public interface Uberspect extends JexlArithmetic.Uberspect {
596
597        /**
598         * Try to find the most specific method and evaluate an operator.
599         * <p>This method does not call {@link #overloads(JexlOperator)} and shall not be called with an
600         * assignment operator; use {@link #tryAssignOverload(JexlCache.Reference, JexlOperator, Consumer, Object...)}
601         * in that case.</p>
602         *
603         * @param reference an optional reference caching resolved method or failing signature
604         * @param operator the operator
605         * @param args      the arguments
606         * @return TRY_FAILED if no specific method could be found, the evaluation result otherwise
607         */
608        Object tryOverload(JexlCache.Reference reference, JexlOperator operator, Object...args);
609
610        /**
611         * Evaluates an assign operator.
612         * <p>
613         * This takes care of finding and caching the operator method when appropriate.
614         * If an overloads returns a value not-equal to TRY_FAILED, it means the side-effect is complete.
615         * Otherwise, {@code a += b <=> a = a + b}
616         * </p>
617         *
618         * @param node     an optional reference caching resolved method or failing signature
619         * @param operator the operator
620         * @param assign   the actual function that performs the side effect
621         * @param args     the arguments, the first one being the target of assignment
622         * @return JexlEngine.TRY_FAILED if no operation was performed,
623         *         the value to use as the side effect argument otherwise
624         */
625        Object tryAssignOverload(final JexlCache.Reference node,
626                                 final JexlOperator operator,
627                                 final Consumer<Object> assign,
628                                 final Object... args);
629
630        /**
631         * Calculate the {@code size} of various types:
632         * Collection, Array, Map, String, and anything that has an int size() method.
633         * <p>Seeks an overload or use the default arithmetic implementation.</p>
634         * <p>Note that the result may not be an integer.
635         *
636         * @param node   an optional reference caching resolved method or failing signature
637         * @param object the object to get the size of
638         * @return the evaluation result
639         */
640        Object size(final JexlCache.Reference node, final Object object);
641
642        /**
643         * Check for emptiness of various types: Collection, Array, Map, String, and anything that has a boolean isEmpty()
644         * method.
645         * <p>Seeks an overload or use the default arithmetic implementation.</p>
646         * <p>Note that the result may not be a boolean.
647         *
648         * @param node   the node holding the object
649         * @param object the object to check the emptiness of
650         * @return the evaluation result
651         */
652        Object empty(final JexlCache.Reference node, final Object object);
653
654        /**
655         * The 'match'/'in' operator implementation.
656         * <p>Seeks an overload or use the default arithmetic implementation.</p>
657         * <p>
658         * Note that 'x in y' or 'x matches y' means 'y contains x' ;
659         * the JEXL operator arguments order syntax is the reverse of this method call.
660         * </p>
661         *
662         * @param node  an optional reference caching resolved method or failing signature
663         * @param operator    the calling operator, =~ or !~
664         * @param right the left operand
665         * @param left  the right operand
666         * @return true if left matches right, false otherwise
667         */
668        boolean contains(final JexlCache.Reference node,
669                         final JexlOperator operator,
670                         final Object left,
671                         final Object right);
672
673        /**
674         * The 'startsWith' operator implementation.
675         * <p>Seeks an overload or use the default arithmetic implementation.</p>
676         *
677         * @param node     an optional reference caching resolved method or failing signature
678         * @param operator the calling operator, $= or $!
679         * @param left     the left operand
680         * @param right    the right operand
681         * @return true if left starts with right, false otherwise
682         */
683        boolean startsWith(final JexlCache.Reference node,
684                           final JexlOperator operator,
685                           final Object left,
686                           final Object right);
687
688        /**
689         * The 'endsWith' operator implementation.
690         * <p>Seeks an overload or use the default arithmetic implementation.</p>
691         *
692         * @param node     an optional reference caching resolved method or failing signature
693         * @param operator the calling operator, ^= or ^!
694         * @param left     the left operand
695         * @param right    the right operand
696         * @return true if left ends with right, false otherwise
697         */
698        boolean endsWith(final JexlCache.Reference node,
699                         final JexlOperator operator,
700                         final Object left,
701                         final Object right);
702    }
703}