View Javadoc
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      /**
54       * Add operator.
55       * <br><strong>Syntax:</strong> {@code x + y}
56       * <br><strong>Method:</strong> {@code T add(L x, R y);}.
57       *
58       * @see JexlArithmetic#add(Object, Object)
59       */
60      ADD("+", "add", 2),
61  
62      /**
63       * Subtract operator.
64       * <br><strong>Syntax:</strong> {@code x - y}
65       * <br><strong>Method:</strong> {@code T subtract(L x, R y);}.
66       *
67       * @see JexlArithmetic#subtract(Object, Object)
68       */
69      SUBTRACT("-", "subtract", 2),
70  
71      /**
72       * Multiply operator.
73       * <br><strong>Syntax:</strong> {@code x * y}
74       * <br><strong>Method:</strong> {@code T multiply(L x, R y);}.
75       *
76       * @see JexlArithmetic#multiply(Object, Object)
77       */
78      MULTIPLY("*", "multiply", 2),
79  
80      /**
81       * Divide operator.
82       * <br><strong>Syntax:</strong> {@code x / y}
83       * <br><strong>Method:</strong> {@code T divide(L x, R y);}.
84       *
85       * @see JexlArithmetic#divide(Object, Object)
86       */
87      DIVIDE("/", "divide", 2),
88  
89      /**
90       * Modulo operator.
91       * <br><strong>Syntax:</strong> {@code x % y}
92       * <br><strong>Method:</strong> {@code T mod(L x, R y);}.
93       *
94       * @see JexlArithmetic#mod(Object, Object)
95       */
96      MOD("%", "mod", 2),
97  
98      /**
99       * 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 }