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 * http://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 package org.apache.commons.jexl2; 018 019 import java.lang.reflect.Constructor; 020 import java.lang.reflect.Array; 021 import java.lang.reflect.InvocationTargetException; 022 import java.math.BigDecimal; 023 import java.math.BigInteger; 024 import java.util.Collection; 025 import java.util.HashMap; 026 import java.util.Iterator; 027 import java.util.Map; 028 029 import org.apache.commons.jexl2.parser.SimpleNode; 030 import org.apache.commons.logging.Log; 031 032 import org.apache.commons.jexl2.parser.JexlNode; 033 import org.apache.commons.jexl2.parser.ASTAdditiveNode; 034 import org.apache.commons.jexl2.parser.ASTAdditiveOperator; 035 import org.apache.commons.jexl2.parser.ASTAndNode; 036 import org.apache.commons.jexl2.parser.ASTAmbiguous; 037 import org.apache.commons.jexl2.parser.ASTArrayAccess; 038 import org.apache.commons.jexl2.parser.ASTArrayLiteral; 039 import org.apache.commons.jexl2.parser.ASTAssignment; 040 import org.apache.commons.jexl2.parser.ASTBitwiseAndNode; 041 import org.apache.commons.jexl2.parser.ASTBitwiseComplNode; 042 import org.apache.commons.jexl2.parser.ASTBitwiseOrNode; 043 import org.apache.commons.jexl2.parser.ASTBitwiseXorNode; 044 import org.apache.commons.jexl2.parser.ASTBlock; 045 import org.apache.commons.jexl2.parser.ASTConstructorNode; 046 import org.apache.commons.jexl2.parser.ASTDivNode; 047 import org.apache.commons.jexl2.parser.ASTEQNode; 048 import org.apache.commons.jexl2.parser.ASTERNode; 049 import org.apache.commons.jexl2.parser.ASTEmptyFunction; 050 import org.apache.commons.jexl2.parser.ASTFalseNode; 051 import org.apache.commons.jexl2.parser.ASTFunctionNode; 052 import org.apache.commons.jexl2.parser.ASTFloatLiteral; 053 import org.apache.commons.jexl2.parser.ASTForeachStatement; 054 import org.apache.commons.jexl2.parser.ASTGENode; 055 import org.apache.commons.jexl2.parser.ASTGTNode; 056 import org.apache.commons.jexl2.parser.ASTIdentifier; 057 import org.apache.commons.jexl2.parser.ASTIfStatement; 058 import org.apache.commons.jexl2.parser.ASTIntegerLiteral; 059 import org.apache.commons.jexl2.parser.ASTJexlScript; 060 import org.apache.commons.jexl2.parser.ASTLENode; 061 import org.apache.commons.jexl2.parser.ASTLTNode; 062 import org.apache.commons.jexl2.parser.ASTMapEntry; 063 import org.apache.commons.jexl2.parser.ASTMapLiteral; 064 import org.apache.commons.jexl2.parser.ASTMethodNode; 065 import org.apache.commons.jexl2.parser.ASTModNode; 066 import org.apache.commons.jexl2.parser.ASTMulNode; 067 import org.apache.commons.jexl2.parser.ASTNENode; 068 import org.apache.commons.jexl2.parser.ASTNRNode; 069 import org.apache.commons.jexl2.parser.ASTNotNode; 070 import org.apache.commons.jexl2.parser.ASTNullLiteral; 071 import org.apache.commons.jexl2.parser.ASTOrNode; 072 import org.apache.commons.jexl2.parser.ASTReference; 073 import org.apache.commons.jexl2.parser.ASTSizeFunction; 074 import org.apache.commons.jexl2.parser.ASTSizeMethod; 075 import org.apache.commons.jexl2.parser.ASTStringLiteral; 076 import org.apache.commons.jexl2.parser.ASTTernaryNode; 077 import org.apache.commons.jexl2.parser.ASTTrueNode; 078 import org.apache.commons.jexl2.parser.ASTUnaryMinusNode; 079 import org.apache.commons.jexl2.parser.ASTWhileStatement; 080 import org.apache.commons.jexl2.parser.Node; 081 import org.apache.commons.jexl2.parser.ParserVisitor; 082 083 import org.apache.commons.jexl2.introspection.Uberspect; 084 import org.apache.commons.jexl2.introspection.JexlMethod; 085 import org.apache.commons.jexl2.introspection.JexlPropertyGet; 086 import org.apache.commons.jexl2.introspection.JexlPropertySet; 087 088 /** 089 * An interpreter of JEXL syntax. 090 * 091 * @since 2.0 092 */ 093 public class Interpreter implements ParserVisitor { 094 /** The logger. */ 095 protected final Log logger; 096 /** The uberspect. */ 097 protected final Uberspect uberspect; 098 /** The arithmetic handler. */ 099 protected final JexlArithmetic arithmetic; 100 /** The map of registered functions. */ 101 protected final Map<String, Object> functions; 102 /** The map of registered functions. */ 103 protected Map<String, Object> functors; 104 /** The context to store/retrieve variables. */ 105 protected final JexlContext context; 106 /** Strict interpreter flag. */ 107 protected final boolean strict; 108 /** Silent intepreter flag. */ 109 protected boolean silent; 110 /** Cache executors. */ 111 protected final boolean cache; 112 /** Registers made of 2 pairs of {register-name, value}. */ 113 protected Object[] registers = null; 114 /** Empty parameters for method matching. */ 115 protected static final Object[] EMPTY_PARAMS = new Object[0]; 116 117 /** 118 * Creates an interpreter. 119 * @param jexl the engine creating this interpreter 120 * @param aContext the context to evaluate expression 121 */ 122 public Interpreter(JexlEngine jexl, JexlContext aContext) { 123 this.logger = jexl.logger; 124 this.uberspect = jexl.uberspect; 125 this.arithmetic = jexl.arithmetic; 126 this.functions = jexl.functions; 127 this.strict = !this.arithmetic.isLenient(); 128 this.silent = jexl.silent; 129 this.cache = jexl.cache != null; 130 this.context = aContext; 131 this.functors = null; 132 } 133 134 /** 135 * Sets whether this interpreter throws JexlException during evaluation. 136 * @param flag true means no JexlException will be thrown but will be logged 137 * as info through the Jexl engine logger, false allows them to be thrown. 138 */ 139 public void setSilent(boolean flag) { 140 this.silent = flag; 141 } 142 143 /** 144 * Checks whether this interpreter throws JexlException during evaluation. 145 * @return true if silent, false otherwise 146 */ 147 public boolean isSilent() { 148 return this.silent; 149 } 150 151 /** 152 * Interpret the given script/expression. 153 * <p> 154 * If the underlying JEXL engine is silent, errors will be logged through its logger as info. 155 * </p> 156 * @param node the script or expression to interpret. 157 * @return the result of the interpretation. 158 * @throws JexlException if any error occurs during interpretation. 159 */ 160 public Object interpret(JexlNode node) { 161 try { 162 return node.jjtAccept(this, null); 163 } catch (JexlException xjexl) { 164 if (silent) { 165 logger.warn(xjexl.getMessage(), xjexl.getCause()); 166 return null; 167 } 168 throw xjexl; 169 } 170 } 171 172 /** 173 * Gets the uberspect. 174 * @return an {@link Uberspect} 175 */ 176 protected Uberspect getUberspect() { 177 return uberspect; 178 } 179 180 /** 181 * Sets this interpreter registers for bean access/assign expressions. 182 * @param theRegisters the array of registers 183 */ 184 protected void setRegisters(Object[] theRegisters) { 185 this.registers = theRegisters; 186 } 187 188 /** 189 * Finds the node causing a NPE for diadic operators. 190 * @param xrt the RuntimeException 191 * @param node the parent node 192 * @param left the left argument 193 * @param right the right argument 194 * @return the left, right or parent node 195 */ 196 protected JexlNode findNullOperand(RuntimeException xrt, JexlNode node, Object left, Object right) { 197 if (xrt instanceof NullPointerException 198 && JexlException.NULL_OPERAND == xrt.getMessage()) { 199 if (left == null) { 200 return node.jjtGetChild(0); 201 } 202 if (right == null) { 203 return node.jjtGetChild(1); 204 } 205 } 206 return node; 207 } 208 209 /** 210 * Triggered when variable can not be resolved. 211 * @param xjexl the JexlException ("undefined variable " + variable) 212 * @return throws JexlException if strict, null otherwise 213 */ 214 protected Object unknownVariable(JexlException xjexl) { 215 if (strict) { 216 throw xjexl; 217 } 218 if (!silent) { 219 logger.warn(xjexl.getMessage()); 220 } 221 return null; 222 } 223 224 /** 225 * Triggered when method, function or constructor invocation fails. 226 * @param xjexl the JexlException wrapping the original error 227 * @return throws JexlException if strict, null otherwise 228 */ 229 protected Object invocationFailed(JexlException xjexl) { 230 if (strict) { 231 throw xjexl; 232 } 233 if (!silent) { 234 logger.warn(xjexl.getMessage(), xjexl.getCause()); 235 } 236 return null; 237 } 238 239 /** 240 * Resolves a namespace, eventually allocating an instance using context as constructor argument. 241 * The lifetime of such instances span the current expression or script evaluation. 242 * 243 * @param prefix the prefix name (may be null for global namespace) 244 * @param node the AST node 245 * @return the namespace instance 246 */ 247 protected Object resolveNamespace(String prefix, JexlNode node) { 248 Object namespace; 249 // check whether this namespace is a functor 250 if (functors != null) { 251 namespace = functors.get(prefix); 252 if (namespace != null) { 253 return namespace; 254 } 255 } 256 namespace = functions.get(prefix); 257 if (namespace == null) { 258 throw new JexlException(node, "no such function namespace " + prefix); 259 } 260 // allow namespace to be instantiated as functor with context 261 if (namespace instanceof Class<?>) { 262 Object[] args = new Object[]{context}; 263 Constructor<?> ctor = uberspect.getConstructor(namespace,args, node); 264 if (ctor != null) { 265 try { 266 namespace = ctor.newInstance(args); 267 if (functors == null) { 268 functors = new HashMap<String, Object>(); 269 } 270 functors.put(prefix, namespace); 271 } catch (Exception xinst) { 272 throw new JexlException(node, "unable to instantiate namespace " + prefix, xinst); 273 } 274 } 275 } 276 return namespace; 277 } 278 279 /** {@inheritDoc} */ 280 public Object visit(ASTAdditiveNode node, Object data) { 281 /** 282 * The pattern for exception mgmt is to let the child*.jjtAccept 283 * out of the try/catch loop so that if one fails, the ex will 284 * traverse up to the interpreter. 285 * In cases where this is not convenient/possible, JexlException must 286 * be caught explicitly and rethrown. 287 */ 288 Object left = node.jjtGetChild(0).jjtAccept(this, data); 289 for(int c = 2, size = node.jjtGetNumChildren(); c < size; c += 2) { 290 Object right = node.jjtGetChild(c).jjtAccept(this, data); 291 try { 292 JexlNode op = node.jjtGetChild(c - 1); 293 if (op instanceof ASTAdditiveOperator) { 294 String which = ((ASTAdditiveOperator) op).image; 295 if ("+".equals(which)) { 296 left = arithmetic.add(left, right); 297 continue; 298 } 299 if ("-".equals(which)) { 300 left = arithmetic.subtract(left, right); 301 continue; 302 } 303 throw new UnsupportedOperationException("unknown operator " + which); 304 } 305 throw new IllegalArgumentException("unknown operator " + op); 306 } catch (RuntimeException xrt) { 307 JexlNode xnode = findNullOperand(xrt, node, left, right); 308 throw new JexlException(xnode, "+/- error", xrt); 309 } 310 } 311 return left; 312 } 313 314 /** {@inheritDoc} */ 315 public Object visit(ASTAdditiveOperator node, Object data) { 316 throw new UnsupportedOperationException("Shoud not be called."); 317 } 318 319 /** {@inheritDoc} */ 320 public Object visit(ASTAndNode node, Object data) { 321 Object left = node.jjtGetChild(0).jjtAccept(this, data); 322 try { 323 boolean leftValue = arithmetic.toBoolean(left); 324 if (!leftValue) { 325 return Boolean.FALSE; 326 } 327 } catch (RuntimeException xrt) { 328 throw new JexlException(node.jjtGetChild(0), "boolean coercion error", xrt); 329 } 330 Object right = node.jjtGetChild(1).jjtAccept(this, data); 331 try { 332 boolean rightValue = arithmetic.toBoolean(right); 333 if (!rightValue) { 334 return Boolean.FALSE; 335 } 336 } catch (RuntimeException xrt) { 337 throw new JexlException(node.jjtGetChild(1), "boolean coercion error", xrt); 338 } 339 return Boolean.TRUE; 340 } 341 342 /** {@inheritDoc} */ 343 public Object visit(ASTArrayAccess node, Object data) { 344 // first objectNode is the identifier 345 Object object = node.jjtGetChild(0).jjtAccept(this, data); 346 // can have multiple nodes - either an expression, integer literal or 347 // reference 348 int numChildren = node.jjtGetNumChildren(); 349 for (int i = 1; i < numChildren; i++) { 350 JexlNode nindex = node.jjtGetChild(i); 351 Object index = nindex.jjtAccept(this, null); 352 object = getAttribute(object, index, nindex); 353 } 354 355 return object; 356 } 357 358 /** {@inheritDoc} */ 359 public Object visit(ASTArrayLiteral node, Object data) { 360 int childCount = node.jjtGetNumChildren(); 361 Object[] array = new Object[childCount]; 362 for (int i = 0; i < childCount; i++) { 363 Object entry = node.jjtGetChild(i).jjtAccept(this, data); 364 array[i] = entry; 365 } 366 return arithmetic.narrowArrayType(array); 367 } 368 369 /** {@inheritDoc} */ 370 public Object visit(ASTAssignment node, Object data) { 371 // left contains the reference to assign to 372 JexlNode left = node.jjtGetChild(0); 373 if (!(left instanceof ASTReference)) { 374 throw new JexlException(left, "illegal assignment form"); 375 } 376 // right is the value expression to assign 377 Object right = node.jjtGetChild(1).jjtAccept(this, data); 378 379 // determine initial object & property: 380 JexlNode objectNode = null; 381 Object object = null; 382 JexlNode propertyNode = null; 383 Object property = null; 384 boolean isVariable = true; 385 int v = 0; 386 StringBuilder variableName = null; 387 // 1: follow children till penultimate 388 int last = left.jjtGetNumChildren() - 1; 389 for (int c = 0; c < last; ++c) { 390 objectNode = left.jjtGetChild(c); 391 // evaluate the property within the object 392 object = objectNode.jjtAccept(this, object); 393 if (object != null) { 394 continue; 395 } 396 isVariable &= objectNode instanceof ASTIdentifier; 397 // if we get null back as a result, check for an ant variable 398 if (isVariable) { 399 if (v == 0) { 400 variableName = new StringBuilder(left.jjtGetChild(0).image); 401 v = 1; 402 } 403 for(; v <= c; ++v) { 404 variableName.append('.'); 405 variableName.append(left.jjtGetChild(v).image); 406 } 407 object = context.get(variableName.toString()); 408 // disallow mixing ant & bean with same root; avoid ambiguity 409 if (object != null) { 410 isVariable = false; 411 } 412 } else { 413 throw new JexlException(objectNode, "illegal assignment form"); 414 } 415 } 416 // 2: last objectNode will perform assignement in all cases 417 propertyNode = left.jjtGetChild(last); 418 if (propertyNode instanceof ASTIdentifier) { 419 property = ((ASTIdentifier) propertyNode).image; 420 // deal with ant variable 421 if (isVariable && object == null) { 422 if (variableName != null) { 423 if (last > 0) { 424 variableName.append('.'); 425 } 426 variableName.append(property); 427 property = variableName.toString(); 428 } 429 context.set(String.valueOf(property), right); 430 return right; 431 } 432 } else if (propertyNode instanceof ASTIntegerLiteral) { 433 property = visit((ASTIntegerLiteral) propertyNode, null); 434 // deal with ant variable 435 if (isVariable && object == null) { 436 if (variableName != null) { 437 if (last > 0) { 438 variableName.append('.'); 439 } 440 variableName.append(property); 441 property = variableName.toString(); 442 } 443 context.set(String.valueOf(property), right); 444 return right; 445 } 446 } else if (propertyNode instanceof ASTArrayAccess) { 447 // first objectNode is the identifier 448 objectNode = propertyNode; 449 ASTArrayAccess narray = (ASTArrayAccess) objectNode; 450 Object nobject = narray.jjtGetChild(0).jjtAccept(this, object); 451 if (nobject == null) { 452 throw new JexlException(objectNode, "array element is null"); 453 } else { 454 object = nobject; 455 } 456 // can have multiple nodes - either an expression, integer literal or 457 // reference 458 last = narray.jjtGetNumChildren() - 1; 459 for (int i = 1; i < last; i++) { 460 objectNode = narray.jjtGetChild(i); 461 Object index = objectNode.jjtAccept(this, null); 462 object = getAttribute(object, index, objectNode); 463 } 464 property = narray.jjtGetChild(last).jjtAccept(this, null); 465 } else { 466 throw new JexlException(objectNode, "illegal assignment form"); 467 } 468 if (property == null) { 469 // no property, we fail 470 throw new JexlException(propertyNode, "property is null"); 471 } 472 if (object == null) { 473 // no object, we fail 474 throw new JexlException(objectNode, "bean is null"); 475 } 476 // one before last, assign 477 setAttribute(object, property, right, propertyNode); 478 return right; 479 } 480 481 /** {@inheritDoc} */ 482 public Object visit(ASTBitwiseAndNode node, Object data) { 483 Object left = node.jjtGetChild(0).jjtAccept(this, data); 484 Object right = node.jjtGetChild(1).jjtAccept(this, data); 485 int n = 0; 486 // coerce these two values longs and 'and'. 487 try { 488 long l = arithmetic.toLong(left); 489 n = 1; 490 long r = arithmetic.toLong(right); 491 return Long.valueOf(l & r); 492 } catch (RuntimeException xrt) { 493 throw new JexlException(node.jjtGetChild(n), "long coercion error", xrt); 494 } 495 } 496 497 /** {@inheritDoc} */ 498 public Object visit(ASTBitwiseComplNode node, Object data) { 499 Object left = node.jjtGetChild(0).jjtAccept(this, data); 500 try { 501 long l = arithmetic.toLong(left); 502 return Long.valueOf(~l); 503 } catch (RuntimeException xrt) { 504 throw new JexlException(node.jjtGetChild(0), "long coercion error", xrt); 505 } 506 } 507 508 /** {@inheritDoc} */ 509 public Object visit(ASTBitwiseOrNode node, Object data) { 510 Object left = node.jjtGetChild(0).jjtAccept(this, data); 511 Object right = node.jjtGetChild(1).jjtAccept(this, data); 512 int n = 0; 513 // coerce these two values longs and 'or'. 514 try { 515 long l = arithmetic.toLong(left); 516 n = 1; 517 long r = arithmetic.toLong(right); 518 return Long.valueOf(l | r); 519 } catch (RuntimeException xrt) { 520 throw new JexlException(node.jjtGetChild(n), "long coercion error", xrt); 521 } 522 } 523 524 /** {@inheritDoc} */ 525 public Object visit(ASTBitwiseXorNode node, Object data) { 526 Object left = node.jjtGetChild(0).jjtAccept(this, data); 527 Object right = node.jjtGetChild(1).jjtAccept(this, data); 528 int n = 0; 529 // coerce these two values longs and 'xor'. 530 try { 531 long l = arithmetic.toLong(left); 532 n = 1; 533 long r = arithmetic.toLong(right); 534 return Long.valueOf(l ^ r); 535 } catch (RuntimeException xrt) { 536 throw new JexlException(node.jjtGetChild(n), "long coercion error", xrt); 537 } 538 } 539 540 /** {@inheritDoc} */ 541 public Object visit(ASTBlock node, Object data) { 542 int numChildren = node.jjtGetNumChildren(); 543 Object result = null; 544 for (int i = 0; i < numChildren; i++) { 545 result = node.jjtGetChild(i).jjtAccept(this, data); 546 } 547 return result; 548 } 549 550 /** {@inheritDoc} */ 551 public Object visit(ASTDivNode node, Object data) { 552 Object left = node.jjtGetChild(0).jjtAccept(this, data); 553 Object right = node.jjtGetChild(1).jjtAccept(this, data); 554 try { 555 return arithmetic.divide(left, right); 556 } catch (RuntimeException xrt) { 557 if (!strict && xrt instanceof ArithmeticException) { 558 return new Double(0.0); 559 } 560 JexlNode xnode = findNullOperand(xrt, node, left, right); 561 throw new JexlException(xnode, "divide error", xrt); 562 } 563 } 564 565 /** {@inheritDoc} */ 566 public Object visit(ASTEmptyFunction node, Object data) { 567 Object o = node.jjtGetChild(0).jjtAccept(this, data); 568 if (o == null) { 569 return Boolean.TRUE; 570 } 571 if (o instanceof String && "".equals(o)) { 572 return Boolean.TRUE; 573 } 574 if (o.getClass().isArray() && ((Object[]) o).length == 0) { 575 return Boolean.TRUE; 576 } 577 if (o instanceof Collection<?> && ((Collection<?>) o).isEmpty()) { 578 return Boolean.TRUE; 579 } 580 // Map isn't a collection 581 if (o instanceof Map<?, ?> && ((Map<?, ?>) o).isEmpty()) { 582 return Boolean.TRUE; 583 } 584 return Boolean.FALSE; 585 } 586 587 /** {@inheritDoc} */ 588 public Object visit(ASTEQNode node, Object data) { 589 Object left = node.jjtGetChild(0).jjtAccept(this, data); 590 Object right = node.jjtGetChild(1).jjtAccept(this, data); 591 try { 592 return arithmetic.equals(left, right) ? Boolean.TRUE : Boolean.FALSE; 593 } catch (RuntimeException xrt) { 594 throw new JexlException(node, "== error", xrt); 595 } 596 } 597 598 /** {@inheritDoc} */ 599 public Object visit(ASTFalseNode node, Object data) { 600 return Boolean.FALSE; 601 } 602 603 /** {@inheritDoc} */ 604 public Object visit(ASTFloatLiteral node, Object data) { 605 Float value = (Float) node.jjtGetValue(); 606 if (value == null) { 607 value = Float.valueOf(node.image); 608 node.jjtSetValue(value); 609 } 610 return value; 611 } 612 613 /** {@inheritDoc} */ 614 public Object visit(ASTForeachStatement node, Object data) { 615 Object result = null; 616 /* first objectNode is the loop variable */ 617 ASTReference loopReference = (ASTReference) node.jjtGetChild(0); 618 ASTIdentifier loopVariable = (ASTIdentifier) loopReference.jjtGetChild(0); 619 /* second objectNode is the variable to iterate */ 620 Object iterableValue = node.jjtGetChild(1).jjtAccept(this, data); 621 // make sure there is a value to iterate on and a statement to execute 622 if (iterableValue != null && node.jjtGetNumChildren() >= 3) { 623 /* third objectNode is the statement to execute */ 624 JexlNode statement = node.jjtGetChild(2); 625 // get an iterator for the collection/array etc via the 626 // introspector. 627 Iterator<?> itemsIterator = getUberspect().getIterator(iterableValue, node); 628 if (itemsIterator != null) { 629 while (itemsIterator.hasNext()) { 630 // set loopVariable to value of iterator 631 Object value = itemsIterator.next(); 632 context.set(loopVariable.image, value); 633 // execute statement 634 result = statement.jjtAccept(this, data); 635 } 636 } 637 } 638 return result; 639 } 640 641 /** {@inheritDoc} */ 642 public Object visit(ASTGENode node, Object data) { 643 Object left = node.jjtGetChild(0).jjtAccept(this, data); 644 Object right = node.jjtGetChild(1).jjtAccept(this, data); 645 try { 646 return arithmetic.greaterThanOrEqual(left, right) ? Boolean.TRUE : Boolean.FALSE; 647 } catch (RuntimeException xrt) { 648 throw new JexlException(node, ">= error", xrt); 649 } 650 } 651 652 /** {@inheritDoc} */ 653 public Object visit(ASTGTNode node, Object data) { 654 Object left = node.jjtGetChild(0).jjtAccept(this, data); 655 Object right = node.jjtGetChild(1).jjtAccept(this, data); 656 try { 657 return arithmetic.greaterThan(left, right) ? Boolean.TRUE : Boolean.FALSE; 658 } catch (RuntimeException xrt) { 659 throw new JexlException(node, "> error", xrt); 660 } 661 } 662 663 /** {@inheritDoc} */ 664 public Object visit(ASTERNode node, Object data) { 665 Object left = node.jjtGetChild(0).jjtAccept(this, data); 666 Object right = node.jjtGetChild(1).jjtAccept(this, data); 667 try { 668 return arithmetic.matches(left, right) ? Boolean.TRUE : Boolean.FALSE; 669 } catch (RuntimeException xrt) { 670 throw new JexlException(node, "=~ error", xrt); 671 } 672 } 673 674 /** {@inheritDoc} */ 675 public Object visit(ASTIdentifier node, Object data) { 676 String name = node.image; 677 if (data == null) { 678 if (registers != null) { 679 if (registers[0].equals(name)) { 680 return registers[1]; 681 } 682 if (registers[2].equals(name)) { 683 return registers[3]; 684 } 685 } 686 Object value = context.get(name); 687 if (value == null 688 && !(node.jjtGetParent() instanceof ASTReference) 689 && !context.has(name)) { 690 JexlException xjexl = new JexlException(node, "undefined variable " + name); 691 return unknownVariable(xjexl); 692 } 693 return value; 694 } else { 695 return getAttribute(data, name, node); 696 } 697 } 698 699 /** {@inheritDoc} */ 700 public Object visit(ASTIfStatement node, Object data) { 701 int n = 0; 702 try { 703 Object result = null; 704 /* first objectNode is the expression */ 705 Object expression = node.jjtGetChild(0).jjtAccept(this, data); 706 if (arithmetic.toBoolean(expression)) { 707 // first objectNode is true statement 708 n = 1; 709 result = node.jjtGetChild(1).jjtAccept(this, data); 710 } else { 711 // if there is a false, execute it. false statement is the second 712 // objectNode 713 if (node.jjtGetNumChildren() == 3) { 714 n = 2; 715 result = node.jjtGetChild(2).jjtAccept(this, data); 716 } 717 } 718 return result; 719 } catch (JexlException error) { 720 throw error; 721 } catch (RuntimeException xrt) { 722 throw new JexlException(node.jjtGetChild(n), "if error", xrt); 723 } 724 } 725 726 /** {@inheritDoc} */ 727 public Object visit(ASTIntegerLiteral node, Object data) { 728 if (data != null) { 729 Integer value = Integer.valueOf(node.image); 730 return getAttribute(data, value, node); 731 } 732 Integer value = (Integer) node.jjtGetValue(); 733 if (value == null) { 734 value = Integer.valueOf(node.image); 735 node.jjtSetValue(value); 736 } 737 return value; 738 } 739 740 /** {@inheritDoc} */ 741 public Object visit(ASTJexlScript node, Object data) { 742 int numChildren = node.jjtGetNumChildren(); 743 Object result = null; 744 for (int i = 0; i < numChildren; i++) { 745 JexlNode child = node.jjtGetChild(i); 746 result = child.jjtAccept(this, data); 747 } 748 return result; 749 } 750 751 /** {@inheritDoc} */ 752 public Object visit(ASTLENode node, Object data) { 753 Object left = node.jjtGetChild(0).jjtAccept(this, data); 754 Object right = node.jjtGetChild(1).jjtAccept(this, data); 755 try { 756 return arithmetic.lessThanOrEqual(left, right) ? Boolean.TRUE : Boolean.FALSE; 757 } catch (RuntimeException xrt) { 758 throw new JexlException(node, "<= error", xrt); 759 } 760 } 761 762 /** {@inheritDoc} */ 763 public Object visit(ASTLTNode node, Object data) { 764 Object left = node.jjtGetChild(0).jjtAccept(this, data); 765 Object right = node.jjtGetChild(1).jjtAccept(this, data); 766 try { 767 return arithmetic.lessThan(left, right) ? Boolean.TRUE : Boolean.FALSE; 768 } catch (RuntimeException xrt) { 769 throw new JexlException(node, "< error", xrt); 770 } 771 } 772 773 /** {@inheritDoc} */ 774 public Object visit(ASTMapEntry node, Object data) { 775 Object key = node.jjtGetChild(0).jjtAccept(this, data); 776 Object value = node.jjtGetChild(1).jjtAccept(this, data); 777 return new Object[]{key, value}; 778 } 779 780 /** {@inheritDoc} */ 781 public Object visit(ASTMapLiteral node, Object data) { 782 int childCount = node.jjtGetNumChildren(); 783 Map<Object, Object> map = new HashMap<Object, Object>(); 784 785 for (int i = 0; i < childCount; i++) { 786 Object[] entry = (Object[]) (node.jjtGetChild(i)).jjtAccept(this, data); 787 map.put(entry[0], entry[1]); 788 } 789 790 return map; 791 } 792 793 /** {@inheritDoc} */ 794 public Object visit(ASTMethodNode node, Object data) { 795 // the object to invoke the method on should be in the data argument 796 if (data == null) { 797 // if the first child of the (ASTReference) parent, 798 // it is considered as calling a 'top level' function 799 if (node.jjtGetParent().jjtGetChild(0) == node) { 800 data = resolveNamespace(null, node); 801 if (data == null) { 802 throw new JexlException(node, "no default function namespace"); 803 } 804 } else { 805 throw new JexlException(node, "attempting to call method on null"); 806 } 807 } 808 // objectNode 0 is the identifier (method name), the others are parameters. 809 String methodName = ((ASTIdentifier) node.jjtGetChild(0)).image; 810 811 // get our arguments 812 int argc = node.jjtGetNumChildren() - 1; 813 Object[] argv = new Object[argc]; 814 for (int i = 0; i < argc; i++) { 815 argv[i] = node.jjtGetChild(i + 1).jjtAccept(this, null); 816 } 817 818 JexlException xjexl = null; 819 try { 820 // attempt to reuse last executor cached in volatile JexlNode.value 821 if (cache) { 822 Object cached = node.jjtGetValue(); 823 if (cached instanceof JexlMethod) { 824 JexlMethod me = (JexlMethod) cached; 825 Object eval = me.tryInvoke(methodName, data, argv); 826 if (!me.tryFailed(eval)) { 827 return eval; 828 } 829 } 830 } 831 JexlMethod vm = uberspect.getMethod(data, methodName, argv, node); 832 // DG: If we can't find an exact match, narrow the parameters and try again! 833 if (vm == null) { 834 if (arithmetic.narrowArguments(argv)) { 835 vm = uberspect.getMethod(data, methodName, argv, node); 836 } 837 if (vm == null) { 838 xjexl = new JexlException(node, "unknown or ambiguous method", null); 839 } 840 } 841 if (xjexl == null) { 842 Object eval = vm.invoke(data, argv); // vm cannot be null if xjexl is null 843 // cache executor in volatile JexlNode.value 844 if (cache && vm.isCacheable()) { 845 node.jjtSetValue(vm); 846 } 847 return eval; 848 } 849 } catch (InvocationTargetException e) { 850 xjexl = new JexlException(node, "method invocation error", e.getCause()); 851 } catch (Exception e) { 852 xjexl = new JexlException(node, "method error", e); 853 } 854 return invocationFailed(xjexl); 855 } 856 857 /** {@inheritDoc} */ 858 public Object visit(ASTConstructorNode node, Object data) { 859 // first child is class or class name 860 Object cobject = node.jjtGetChild(0).jjtAccept(this, data); 861 // get the ctor args 862 int argc = node.jjtGetNumChildren() - 1; 863 Object[] argv = new Object[argc]; 864 for (int i = 0; i < argc; i++) { 865 argv[i] = node.jjtGetChild(i + 1).jjtAccept(this, null); 866 } 867 868 JexlException xjexl = null; 869 try { 870 Constructor<?> ctor = uberspect.getConstructor(cobject, argv, node); 871 // DG: If we can't find an exact match, narrow the parameters and 872 // try again! 873 if (ctor == null) { 874 if (arithmetic.narrowArguments(argv)) { 875 ctor = uberspect.getConstructor(cobject, argv, node); 876 } 877 if (ctor == null) { 878 xjexl = new JexlException(node, "unknown constructor", null); 879 } 880 } 881 if (xjexl == null) { 882 return ctor.newInstance(argv); 883 } 884 } catch (InvocationTargetException e) { 885 xjexl = new JexlException(node, "constructor invocation error", e.getCause()); 886 } catch (Exception e) { 887 xjexl = new JexlException(node, "constructor error", e); 888 } 889 return invocationFailed(xjexl); 890 } 891 892 /** {@inheritDoc} */ 893 public Object visit(ASTFunctionNode node, Object data) { 894 // objectNode 0 is the prefix 895 String prefix = ((ASTIdentifier) node.jjtGetChild(0)).image; 896 Object namespace = resolveNamespace(prefix, node); 897 // objectNode 1 is the identifier , the others are parameters. 898 String function = ((ASTIdentifier) node.jjtGetChild(1)).image; 899 900 // get our args 901 int argc = node.jjtGetNumChildren() - 2; 902 Object[] argv = new Object[argc]; 903 for (int i = 0; i < argc; i++) { 904 argv[i] = node.jjtGetChild(i + 2).jjtAccept(this, null); 905 } 906 907 JexlException xjexl = null; 908 try { 909 // attempt to reuse last executor cached in volatile JexlNode.value 910 if (cache) { 911 Object cached = node.jjtGetValue(); 912 if (cached instanceof JexlMethod) { 913 JexlMethod me = (JexlMethod) cached; 914 Object eval = me.tryInvoke(function, namespace, argv); 915 if (!me.tryFailed(eval)) { 916 return eval; 917 } 918 } 919 } 920 JexlMethod vm = uberspect.getMethod(namespace, function, argv, node); 921 // DG: If we can't find an exact match, narrow the parameters and 922 // try again! 923 if (vm == null) { 924 // replace all numbers with the smallest type that will fit 925 if (arithmetic.narrowArguments(argv)) { 926 vm = uberspect.getMethod(namespace, function, argv, node); 927 } 928 if (vm == null) { 929 xjexl = new JexlException(node, "unknown function", null); 930 } 931 } 932 if (xjexl == null) { 933 Object eval = vm.invoke(namespace, argv); // vm cannot be null if xjexl is null 934 // cache executor in volatile JexlNode.value 935 if (cache && vm.isCacheable()) { 936 node.jjtSetValue(vm); 937 } 938 return eval; 939 } 940 } catch (InvocationTargetException e) { 941 xjexl = new JexlException(node, "function invocation error", e.getCause()); 942 } catch (Exception e) { 943 xjexl = new JexlException(node, "function error", e); 944 } 945 return invocationFailed(xjexl); 946 } 947 948 /** {@inheritDoc} */ 949 public Object visit(ASTModNode node, Object data) { 950 Object left = node.jjtGetChild(0).jjtAccept(this, data); 951 Object right = node.jjtGetChild(1).jjtAccept(this, data); 952 try { 953 return arithmetic.mod(left, right); 954 } catch (RuntimeException xrt) { 955 if (!strict && xrt instanceof ArithmeticException) { 956 return new Double(0.0); 957 } 958 JexlNode xnode = findNullOperand(xrt, node, left, right); 959 throw new JexlException(xnode, "% error", xrt); 960 } 961 } 962 963 /** {@inheritDoc} */ 964 public Object visit(ASTMulNode node, Object data) { 965 Object left = node.jjtGetChild(0).jjtAccept(this, data); 966 Object right = node.jjtGetChild(1).jjtAccept(this, data); 967 try { 968 return arithmetic.multiply(left, right); 969 } catch (RuntimeException xrt) { 970 JexlNode xnode = findNullOperand(xrt, node, left, right); 971 throw new JexlException(xnode, "* error", xrt); 972 } 973 } 974 975 /** {@inheritDoc} */ 976 public Object visit(ASTNENode node, Object data) { 977 Object left = node.jjtGetChild(0).jjtAccept(this, data); 978 Object right = node.jjtGetChild(1).jjtAccept(this, data); 979 try { 980 return arithmetic.equals(left, right) ? Boolean.FALSE : Boolean.TRUE; 981 } catch (RuntimeException xrt) { 982 JexlNode xnode = findNullOperand(xrt, node, left, right); 983 throw new JexlException(xnode, "!= error", xrt); 984 } 985 } 986 987 /** {@inheritDoc} */ 988 public Object visit(ASTNRNode node, Object data) { 989 Object left = node.jjtGetChild(0).jjtAccept(this, data); 990 Object right = node.jjtGetChild(1).jjtAccept(this, data); 991 try { 992 return arithmetic.matches(left, right) ? Boolean.FALSE : Boolean.TRUE; 993 } catch (RuntimeException xrt) { 994 throw new JexlException(node, "!~ error", xrt); 995 } 996 } 997 998 /** {@inheritDoc} */ 999 public Object visit(ASTNotNode node, Object data) { 1000 Object val = node.jjtGetChild(0).jjtAccept(this, data); 1001 return arithmetic.toBoolean(val) ? Boolean.FALSE : Boolean.TRUE; 1002 } 1003 1004 /** {@inheritDoc} */ 1005 public Object visit(ASTNullLiteral node, Object data) { 1006 return null; 1007 } 1008 1009 /** {@inheritDoc} */ 1010 public Object visit(ASTOrNode node, Object data) { 1011 Object left = node.jjtGetChild(0).jjtAccept(this, data); 1012 try { 1013 boolean leftValue = arithmetic.toBoolean(left); 1014 if (leftValue) { 1015 return Boolean.TRUE; 1016 } 1017 } catch (RuntimeException xrt) { 1018 throw new JexlException(node.jjtGetChild(0), "boolean coercion error", xrt); 1019 } 1020 Object right = node.jjtGetChild(1).jjtAccept(this, data); 1021 try { 1022 boolean rightValue = arithmetic.toBoolean(right); 1023 if (rightValue) { 1024 return Boolean.TRUE; 1025 } 1026 } catch (RuntimeException xrt) { 1027 throw new JexlException(node.jjtGetChild(1), "boolean coercion error", xrt); 1028 } 1029 return Boolean.FALSE; 1030 } 1031 1032 /** {@inheritDoc} */ 1033 public Object visit(ASTReference node, Object data) { 1034 // could be array access, identifier or map literal 1035 // followed by zero or more ("." and array access, method, size, 1036 // identifier or integer literal) 1037 1038 int numChildren = node.jjtGetNumChildren(); 1039 1040 // pass first piece of data in and loop through children 1041 Object result = null; 1042 StringBuilder variableName = null; 1043 boolean isVariable = true; 1044 int v = 0; 1045 for (int c = 0; c < numChildren; c++) { 1046 JexlNode theNode = node.jjtGetChild(c); 1047 isVariable &= (theNode instanceof ASTIdentifier); 1048 result = theNode.jjtAccept(this, result); 1049 // if we get null back a result, check for an ant variable 1050 if (result == null && isVariable) { 1051 if (v == 0) { 1052 variableName = new StringBuilder(node.jjtGetChild(0).image); 1053 v = 1; 1054 } 1055 for(; v <= c; ++v) { 1056 variableName.append('.'); 1057 variableName.append(node.jjtGetChild(v).image); 1058 } 1059 result = context.get(variableName.toString()); 1060 } 1061 } 1062 if (result == null) { 1063 if (isVariable 1064 && !(node.jjtGetParent() instanceof ASTTernaryNode) 1065 && !context.has(variableName.toString())) { 1066 JexlException xjexl = new JexlException(node, "undefined variable " + variableName.toString()); 1067 return unknownVariable(xjexl); 1068 } 1069 } 1070 return result; 1071 } 1072 1073 /** {@inheritDoc} */ 1074 public Object visit(ASTSizeFunction node, Object data) { 1075 Object val = node.jjtGetChild(0).jjtAccept(this, data); 1076 1077 if (val == null) { 1078 throw new JexlException(node, "size() : argument is null", null); 1079 } 1080 1081 return Integer.valueOf(sizeOf(node, val)); 1082 } 1083 1084 /** {@inheritDoc} */ 1085 public Object visit(ASTSizeMethod node, Object data) { 1086 return Integer.valueOf(sizeOf(node, data)); 1087 } 1088 1089 /** {@inheritDoc} */ 1090 public Object visit(ASTStringLiteral node, Object data) { 1091 return node.image; 1092 } 1093 1094 /** {@inheritDoc} */ 1095 public Object visit(ASTTernaryNode node, Object data) { 1096 Object condition = node.jjtGetChild(0).jjtAccept(this, data); 1097 if (node.jjtGetNumChildren() == 3) { 1098 if (condition != null && arithmetic.toBoolean(condition)) { 1099 return node.jjtGetChild(1).jjtAccept(this, data); 1100 } else { 1101 return node.jjtGetChild(2).jjtAccept(this, data); 1102 } 1103 } 1104 if (condition != null && !Boolean.FALSE.equals(condition)) { 1105 return condition; 1106 } else { 1107 return node.jjtGetChild(1).jjtAccept(this, data); 1108 } 1109 } 1110 1111 /** {@inheritDoc} */ 1112 public Object visit(ASTTrueNode node, Object data) { 1113 return Boolean.TRUE; 1114 } 1115 1116 /** {@inheritDoc} */ 1117 public Object visit(ASTUnaryMinusNode node, Object data) { 1118 JexlNode valNode = node.jjtGetChild(0); 1119 Object val = valNode.jjtAccept(this, data); 1120 if (val instanceof Byte) { 1121 byte valueAsByte = ((Byte) val).byteValue(); 1122 return Byte.valueOf((byte) -valueAsByte); 1123 } else if (val instanceof Short) { 1124 short valueAsShort = ((Short) val).shortValue(); 1125 return Short.valueOf((short) -valueAsShort); 1126 } else if (val instanceof Integer) { 1127 int valueAsInt = ((Integer) val).intValue(); 1128 return Integer.valueOf(-valueAsInt); 1129 } else if (val instanceof Long) { 1130 long valueAsLong = ((Long) val).longValue(); 1131 return Long.valueOf(-valueAsLong); 1132 } else if (val instanceof Float) { 1133 float valueAsFloat = ((Float) val).floatValue(); 1134 return new Float(-valueAsFloat); 1135 } else if (val instanceof Double) { 1136 double valueAsDouble = ((Double) val).doubleValue(); 1137 return new Double(-valueAsDouble); 1138 } else if (val instanceof BigDecimal) { 1139 BigDecimal valueAsBigD = (BigDecimal) val; 1140 return valueAsBigD.negate(); 1141 } else if (val instanceof BigInteger) { 1142 BigInteger valueAsBigI = (BigInteger) val; 1143 return valueAsBigI.negate(); 1144 } 1145 throw new JexlException(valNode, "not a number"); 1146 } 1147 1148 /** {@inheritDoc} */ 1149 public Object visit(ASTWhileStatement node, Object data) { 1150 Object result = null; 1151 /* first objectNode is the expression */ 1152 Node expressionNode = node.jjtGetChild(0); 1153 while (arithmetic.toBoolean(expressionNode.jjtAccept(this, data))) { 1154 // execute statement 1155 result = node.jjtGetChild(1).jjtAccept(this, data); 1156 } 1157 1158 return result; 1159 } 1160 1161 /** 1162 * Calculate the <code>size</code> of various types: Collection, Array, 1163 * Map, String, and anything that has a int size() method. 1164 * @param node the node that gave the value to size 1165 * @param val the object to get the size of. 1166 * @return the size of val 1167 */ 1168 private int sizeOf(JexlNode node, Object val) { 1169 if (val instanceof Collection<?>) { 1170 return ((Collection<?>) val).size(); 1171 } else if (val.getClass().isArray()) { 1172 return Array.getLength(val); 1173 } else if (val instanceof Map<?, ?>) { 1174 return ((Map<?, ?>) val).size(); 1175 } else if (val instanceof String) { 1176 return ((String) val).length(); 1177 } else { 1178 // check if there is a size method on the object that returns an 1179 // integer and if so, just use it 1180 Object[] params = new Object[0]; 1181 JexlMethod vm = uberspect.getMethod(val, "size", EMPTY_PARAMS, node); 1182 if (vm != null && vm.getReturnType() == Integer.TYPE) { 1183 Integer result; 1184 try { 1185 result = (Integer) vm.invoke(val, params); 1186 } catch (Exception e) { 1187 throw new JexlException(node, "size() : error executing", e); 1188 } 1189 return result.intValue(); 1190 } 1191 throw new JexlException(node, "size() : unsupported type : " + val.getClass(), null); 1192 } 1193 } 1194 1195 /** 1196 * Gets an attribute of an object. 1197 * 1198 * @param object to retrieve value from 1199 * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or 1200 * key for a map 1201 * @return the attribute value 1202 */ 1203 public Object getAttribute(Object object, Object attribute) { 1204 return getAttribute(object, attribute, null); 1205 } 1206 1207 /** 1208 * Gets an attribute of an object. 1209 * 1210 * @param object to retrieve value from 1211 * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or 1212 * key for a map 1213 * @param node the node that evaluated as the object 1214 * @return the attribute value 1215 */ 1216 protected Object getAttribute(Object object, Object attribute, JexlNode node) { 1217 if (object == null) { 1218 throw new JexlException(node, "object is null"); 1219 } 1220 // attempt to reuse last executor cached in volatile JexlNode.value 1221 if (node != null && cache) { 1222 Object cached = node.jjtGetValue(); 1223 if (cached instanceof JexlPropertyGet) { 1224 JexlPropertyGet vg = (JexlPropertyGet) cached; 1225 Object value = vg.tryInvoke(object, attribute); 1226 if (!vg.tryFailed(value)) { 1227 return value; 1228 } 1229 } 1230 } 1231 JexlPropertyGet vg = uberspect.getPropertyGet(object, attribute, node); 1232 if (vg != null) { 1233 try { 1234 Object value = vg.invoke(object); 1235 // cache executor in volatile JexlNode.value 1236 if (node != null && cache && vg.isCacheable()) { 1237 node.jjtSetValue(vg); 1238 } 1239 return value; 1240 } catch (Exception xany) { 1241 if (node == null) { 1242 throw new RuntimeException(xany); 1243 } else { 1244 JexlException xjexl = new JexlException(node, "get object property error", xany); 1245 if (strict) { 1246 throw xjexl; 1247 } 1248 if (!silent) { 1249 logger.warn(xjexl.getMessage()); 1250 } 1251 } 1252 } 1253 } 1254 1255 return null; 1256 } 1257 1258 /** 1259 * Sets an attribute of an object. 1260 * 1261 * @param object to set the value to 1262 * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or 1263 * key for a map 1264 * @param value the value to assign to the object's attribute 1265 */ 1266 public void setAttribute(Object object, Object attribute, Object value) { 1267 setAttribute(object, attribute, value, null); 1268 } 1269 1270 /** 1271 * Sets an attribute of an object. 1272 * 1273 * @param object to set the value to 1274 * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or 1275 * key for a map 1276 * @param value the value to assign to the object's attribute 1277 * @param node the node that evaluated as the object 1278 */ 1279 protected void setAttribute(Object object, Object attribute, Object value, JexlNode node) { 1280 // attempt to reuse last executor cached in volatile JexlNode.value 1281 if (node != null && cache) { 1282 Object cached = node.jjtGetValue(); 1283 if (cached instanceof JexlPropertySet) { 1284 JexlPropertySet setter = (JexlPropertySet) cached; 1285 Object eval = setter.tryInvoke(object, attribute, value); 1286 if (!setter.tryFailed(eval)) { 1287 return; 1288 } 1289 } 1290 } 1291 JexlException xjexl = null; 1292 JexlPropertySet vs = uberspect.getPropertySet(object, attribute, value, node); 1293 if (vs != null) { 1294 try { 1295 // cache executor in volatile JexlNode.value 1296 vs.invoke(object, value); 1297 if (node != null && cache && vs.isCacheable()) { 1298 node.jjtSetValue(vs); 1299 } 1300 return; 1301 } catch (RuntimeException xrt) { 1302 if (node == null) { 1303 throw xrt; 1304 } 1305 xjexl = new JexlException(node, "set object property error", xrt); 1306 } catch (Exception xany) { 1307 if (node == null) { 1308 throw new RuntimeException(xany); 1309 } 1310 xjexl = new JexlException(node, "set object property error", xany); 1311 } 1312 } 1313 if (xjexl == null) { 1314 String error = "unable to set object property" 1315 + ", class: " + object.getClass().getName() 1316 + ", property: " + attribute; 1317 if (node == null) { 1318 throw new UnsupportedOperationException(error); 1319 } 1320 xjexl = new JexlException(node, error, null); 1321 } 1322 if (strict) { 1323 throw xjexl; 1324 } 1325 if (!silent) { 1326 logger.warn(xjexl.getMessage()); 1327 } 1328 } 1329 1330 /** 1331 * Unused, satisfy ParserVisitor interface. 1332 * @param node a node 1333 * @param data the data 1334 * @return does not return 1335 */ 1336 public Object visit(SimpleNode node, Object data) { 1337 throw new UnsupportedOperationException("Not supported yet."); 1338 } 1339 1340 /** 1341 * Unused, should throw in Parser. 1342 * @param node a node 1343 * @param data the data 1344 * @return does not return 1345 */ 1346 public Object visit(ASTAmbiguous node, Object data) { 1347 throw new UnsupportedOperationException("unexpected type of node"); 1348 } 1349 }