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 &= 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}