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 if (nindex instanceof JexlNode.Literal<?>) { 352 object = nindex.jjtAccept(this, object); 353 } else { 354 Object index = nindex.jjtAccept(this, null); 355 object = getAttribute(object, index, nindex); 356 } 357 } 358 359 return object; 360 } 361 362 /** {@inheritDoc} */ 363 public Object visit(ASTArrayLiteral node, Object data) { 364 Object literal = node.getLiteral(); 365 if (literal == null) { 366 int childCount = node.jjtGetNumChildren(); 367 Object[] array = new Object[childCount]; 368 for (int i = 0; i < childCount; i++) { 369 Object entry = node.jjtGetChild(i).jjtAccept(this, data); 370 array[i] = entry; 371 } 372 literal = arithmetic.narrowArrayType(array); 373 node.setLiteral(literal); 374 } 375 return literal; 376 } 377 378 /** {@inheritDoc} */ 379 public Object visit(ASTAssignment node, Object data) { 380 // left contains the reference to assign to 381 JexlNode left = node.jjtGetChild(0); 382 if (!(left instanceof ASTReference)) { 383 throw new JexlException(left, "illegal assignment form"); 384 } 385 // right is the value expression to assign 386 Object right = node.jjtGetChild(1).jjtAccept(this, data); 387 388 // determine initial object & property: 389 JexlNode objectNode = null; 390 Object object = null; 391 JexlNode propertyNode = null; 392 Object property = null; 393 boolean isVariable = true; 394 int v = 0; 395 StringBuilder variableName = null; 396 // 1: follow children till penultimate 397 int last = left.jjtGetNumChildren() - 1; 398 for (int c = 0; c < last; ++c) { 399 objectNode = left.jjtGetChild(c); 400 // evaluate the property within the object 401 object = objectNode.jjtAccept(this, object); 402 if (object != null) { 403 continue; 404 } 405 isVariable &= objectNode instanceof ASTIdentifier; 406 // if we get null back as a result, check for an ant variable 407 if (isVariable) { 408 if (v == 0) { 409 variableName = new StringBuilder(left.jjtGetChild(0).image); 410 v = 1; 411 } 412 for(; v <= c; ++v) { 413 variableName.append('.'); 414 variableName.append(left.jjtGetChild(v).image); 415 } 416 object = context.get(variableName.toString()); 417 // disallow mixing ant & bean with same root; avoid ambiguity 418 if (object != null) { 419 isVariable = false; 420 } 421 } else { 422 throw new JexlException(objectNode, "illegal assignment form"); 423 } 424 } 425 // 2: last objectNode will perform assignement in all cases 426 propertyNode = left.jjtGetChild(last); 427 boolean antVar = false; 428 if (propertyNode instanceof ASTIdentifier) { 429 property = ((ASTIdentifier) propertyNode).image; 430 antVar = true; 431 } else if (propertyNode instanceof ASTIntegerLiteral) { 432 property = ((ASTIntegerLiteral) propertyNode).getLiteral(); 433 antVar = true; 434 } else if (propertyNode instanceof ASTArrayAccess) { 435 // first objectNode is the identifier 436 objectNode = propertyNode; 437 ASTArrayAccess narray = (ASTArrayAccess) objectNode; 438 Object nobject = narray.jjtGetChild(0).jjtAccept(this, object); 439 if (nobject == null) { 440 throw new JexlException(objectNode, "array element is null"); 441 } else { 442 object = nobject; 443 } 444 // can have multiple nodes - either an expression, integer literal or 445 // reference 446 last = narray.jjtGetNumChildren() - 1; 447 for (int i = 1; i < last; i++) { 448 objectNode = narray.jjtGetChild(i); 449 if (objectNode instanceof JexlNode.Literal<?>) { 450 object = objectNode.jjtAccept(this, object); 451 } else { 452 Object index = objectNode.jjtAccept(this, null); 453 object = getAttribute(object, index, objectNode); 454 } 455 } 456 property = narray.jjtGetChild(last).jjtAccept(this, null); 457 } else { 458 throw new JexlException(objectNode, "illegal assignment form"); 459 } 460 // deal with ant variable; set context 461 if (antVar) { 462 if (isVariable && object == null) { 463 if (variableName != null) { 464 if (last > 0) { 465 variableName.append('.'); 466 } 467 variableName.append(property); 468 property = variableName.toString(); 469 } 470 context.set(String.valueOf(property), right); 471 return right; 472 } 473 } 474 if (property == null) { 475 // no property, we fail 476 throw new JexlException(propertyNode, "property is null"); 477 } 478 if (object == null) { 479 // no object, we fail 480 throw new JexlException(objectNode, "bean is null"); 481 } 482 // one before last, assign 483 setAttribute(object, property, right, propertyNode); 484 return right; 485 } 486 487 /** {@inheritDoc} */ 488 public Object visit(ASTBitwiseAndNode node, Object data) { 489 Object left = node.jjtGetChild(0).jjtAccept(this, data); 490 Object right = node.jjtGetChild(1).jjtAccept(this, data); 491 int n = 0; 492 // coerce these two values longs and 'and'. 493 try { 494 long l = arithmetic.toLong(left); 495 n = 1; 496 long r = arithmetic.toLong(right); 497 return Long.valueOf(l & r); 498 } catch (RuntimeException xrt) { 499 throw new JexlException(node.jjtGetChild(n), "long coercion error", xrt); 500 } 501 } 502 503 /** {@inheritDoc} */ 504 public Object visit(ASTBitwiseComplNode node, Object data) { 505 Object left = node.jjtGetChild(0).jjtAccept(this, data); 506 try { 507 long l = arithmetic.toLong(left); 508 return Long.valueOf(~l); 509 } catch (RuntimeException xrt) { 510 throw new JexlException(node.jjtGetChild(0), "long coercion error", xrt); 511 } 512 } 513 514 /** {@inheritDoc} */ 515 public Object visit(ASTBitwiseOrNode node, Object data) { 516 Object left = node.jjtGetChild(0).jjtAccept(this, data); 517 Object right = node.jjtGetChild(1).jjtAccept(this, data); 518 int n = 0; 519 // coerce these two values longs and 'or'. 520 try { 521 long l = arithmetic.toLong(left); 522 n = 1; 523 long r = arithmetic.toLong(right); 524 return Long.valueOf(l | r); 525 } catch (RuntimeException xrt) { 526 throw new JexlException(node.jjtGetChild(n), "long coercion error", xrt); 527 } 528 } 529 530 /** {@inheritDoc} */ 531 public Object visit(ASTBitwiseXorNode node, Object data) { 532 Object left = node.jjtGetChild(0).jjtAccept(this, data); 533 Object right = node.jjtGetChild(1).jjtAccept(this, data); 534 int n = 0; 535 // coerce these two values longs and 'xor'. 536 try { 537 long l = arithmetic.toLong(left); 538 n = 1; 539 long r = arithmetic.toLong(right); 540 return Long.valueOf(l ^ r); 541 } catch (RuntimeException xrt) { 542 throw new JexlException(node.jjtGetChild(n), "long coercion error", xrt); 543 } 544 } 545 546 /** {@inheritDoc} */ 547 public Object visit(ASTBlock node, Object data) { 548 int numChildren = node.jjtGetNumChildren(); 549 Object result = null; 550 for (int i = 0; i < numChildren; i++) { 551 result = node.jjtGetChild(i).jjtAccept(this, data); 552 } 553 return result; 554 } 555 556 /** {@inheritDoc} */ 557 public Object visit(ASTDivNode node, Object data) { 558 Object left = node.jjtGetChild(0).jjtAccept(this, data); 559 Object right = node.jjtGetChild(1).jjtAccept(this, data); 560 try { 561 return arithmetic.divide(left, right); 562 } catch (RuntimeException xrt) { 563 if (!strict && xrt instanceof ArithmeticException) { 564 return new Double(0.0); 565 } 566 JexlNode xnode = findNullOperand(xrt, node, left, right); 567 throw new JexlException(xnode, "divide error", xrt); 568 } 569 } 570 571 /** {@inheritDoc} */ 572 public Object visit(ASTEmptyFunction node, Object data) { 573 Object o = node.jjtGetChild(0).jjtAccept(this, data); 574 if (o == null) { 575 return Boolean.TRUE; 576 } 577 if (o instanceof String && "".equals(o)) { 578 return Boolean.TRUE; 579 } 580 if (o.getClass().isArray() && ((Object[]) o).length == 0) { 581 return Boolean.TRUE; 582 } 583 if (o instanceof Collection<?>) { 584 return ((Collection<?>) o).isEmpty()? Boolean.TRUE : Boolean.FALSE; 585 } 586 // Map isn't a collection 587 if (o instanceof Map<?, ?>) { 588 return ((Map<?,?>) o).isEmpty()? Boolean.TRUE : Boolean.FALSE; 589 } 590 return Boolean.FALSE; 591 } 592 593 /** {@inheritDoc} */ 594 public Object visit(ASTEQNode node, Object data) { 595 Object left = node.jjtGetChild(0).jjtAccept(this, data); 596 Object right = node.jjtGetChild(1).jjtAccept(this, data); 597 try { 598 return arithmetic.equals(left, right) ? Boolean.TRUE : Boolean.FALSE; 599 } catch (RuntimeException xrt) { 600 throw new JexlException(node, "== error", xrt); 601 } 602 } 603 604 /** {@inheritDoc} */ 605 public Object visit(ASTFalseNode node, Object data) { 606 return Boolean.FALSE; 607 } 608 609 /** {@inheritDoc} */ 610 public Object visit(ASTFloatLiteral node, Object data) { 611 if (data != null) { 612 return getAttribute(data, node.getLiteral(), node); 613 } 614 return node.getLiteral(); 615 } 616 617 /** {@inheritDoc} */ 618 public Object visit(ASTForeachStatement node, Object data) { 619 Object result = null; 620 /* first objectNode is the loop variable */ 621 ASTReference loopReference = (ASTReference) node.jjtGetChild(0); 622 ASTIdentifier loopVariable = (ASTIdentifier) loopReference.jjtGetChild(0); 623 /* second objectNode is the variable to iterate */ 624 Object iterableValue = node.jjtGetChild(1).jjtAccept(this, data); 625 // make sure there is a value to iterate on and a statement to execute 626 if (iterableValue != null && node.jjtGetNumChildren() >= 3) { 627 /* third objectNode is the statement to execute */ 628 JexlNode statement = node.jjtGetChild(2); 629 // get an iterator for the collection/array etc via the 630 // introspector. 631 Iterator<?> itemsIterator = getUberspect().getIterator(iterableValue, node); 632 if (itemsIterator != null) { 633 while (itemsIterator.hasNext()) { 634 // set loopVariable to value of iterator 635 Object value = itemsIterator.next(); 636 context.set(loopVariable.image, value); 637 // execute statement 638 result = statement.jjtAccept(this, data); 639 } 640 } 641 } 642 return result; 643 } 644 645 /** {@inheritDoc} */ 646 public Object visit(ASTGENode node, Object data) { 647 Object left = node.jjtGetChild(0).jjtAccept(this, data); 648 Object right = node.jjtGetChild(1).jjtAccept(this, data); 649 try { 650 return arithmetic.greaterThanOrEqual(left, right) ? Boolean.TRUE : Boolean.FALSE; 651 } catch (RuntimeException xrt) { 652 throw new JexlException(node, ">= error", xrt); 653 } 654 } 655 656 /** {@inheritDoc} */ 657 public Object visit(ASTGTNode node, Object data) { 658 Object left = node.jjtGetChild(0).jjtAccept(this, data); 659 Object right = node.jjtGetChild(1).jjtAccept(this, data); 660 try { 661 return arithmetic.greaterThan(left, right) ? Boolean.TRUE : Boolean.FALSE; 662 } catch (RuntimeException xrt) { 663 throw new JexlException(node, "> error", xrt); 664 } 665 } 666 667 /** {@inheritDoc} */ 668 public Object visit(ASTERNode node, Object data) { 669 Object left = node.jjtGetChild(0).jjtAccept(this, data); 670 Object right = node.jjtGetChild(1).jjtAccept(this, data); 671 try { 672 return arithmetic.matches(left, right) ? Boolean.TRUE : Boolean.FALSE; 673 } catch (RuntimeException xrt) { 674 throw new JexlException(node, "=~ error", xrt); 675 } 676 } 677 678 /** {@inheritDoc} */ 679 public Object visit(ASTIdentifier node, Object data) { 680 String name = node.image; 681 if (data == null) { 682 if (registers != null) { 683 return registers[name.charAt(1) - '0']; 684 } 685 Object value = context.get(name); 686 if (value == null 687 && !(node.jjtGetParent() instanceof ASTReference) 688 && !context.has(name)) { 689 JexlException xjexl = new JexlException(node, "undefined variable " + name); 690 return unknownVariable(xjexl); 691 } 692 return value; 693 } else { 694 return getAttribute(data, name, node); 695 } 696 } 697 698 /** {@inheritDoc} */ 699 public Object visit(ASTIfStatement node, Object data) { 700 int n = 0; 701 try { 702 Object result = null; 703 /* first objectNode is the expression */ 704 Object expression = node.jjtGetChild(0).jjtAccept(this, data); 705 if (arithmetic.toBoolean(expression)) { 706 // first objectNode is true statement 707 n = 1; 708 result = node.jjtGetChild(1).jjtAccept(this, data); 709 } else { 710 // if there is a false, execute it. false statement is the second 711 // objectNode 712 if (node.jjtGetNumChildren() == 3) { 713 n = 2; 714 result = node.jjtGetChild(2).jjtAccept(this, data); 715 } 716 } 717 return result; 718 } catch (JexlException error) { 719 throw error; 720 } catch (RuntimeException xrt) { 721 throw new JexlException(node.jjtGetChild(n), "if error", xrt); 722 } 723 } 724 725 /** {@inheritDoc} */ 726 public Object visit(ASTIntegerLiteral node, Object data) { 727 if (data != null) { 728 return getAttribute(data, node.getLiteral(), node); 729 } 730 return node.getLiteral(); 731 } 732 733 /** {@inheritDoc} */ 734 public Object visit(ASTJexlScript node, Object data) { 735 int numChildren = node.jjtGetNumChildren(); 736 Object result = null; 737 for (int i = 0; i < numChildren; i++) { 738 JexlNode child = node.jjtGetChild(i); 739 result = child.jjtAccept(this, data); 740 } 741 return result; 742 } 743 744 /** {@inheritDoc} */ 745 public Object visit(ASTLENode node, Object data) { 746 Object left = node.jjtGetChild(0).jjtAccept(this, data); 747 Object right = node.jjtGetChild(1).jjtAccept(this, data); 748 try { 749 return arithmetic.lessThanOrEqual(left, right) ? Boolean.TRUE : Boolean.FALSE; 750 } catch (RuntimeException xrt) { 751 throw new JexlException(node, "<= error", xrt); 752 } 753 } 754 755 /** {@inheritDoc} */ 756 public Object visit(ASTLTNode node, Object data) { 757 Object left = node.jjtGetChild(0).jjtAccept(this, data); 758 Object right = node.jjtGetChild(1).jjtAccept(this, data); 759 try { 760 return arithmetic.lessThan(left, right) ? Boolean.TRUE : Boolean.FALSE; 761 } catch (RuntimeException xrt) { 762 throw new JexlException(node, "< error", xrt); 763 } 764 } 765 766 /** {@inheritDoc} */ 767 public Object visit(ASTMapEntry node, Object data) { 768 Object key = node.jjtGetChild(0).jjtAccept(this, data); 769 Object value = node.jjtGetChild(1).jjtAccept(this, data); 770 return new Object[]{key, value}; 771 } 772 773 /** {@inheritDoc} */ 774 public Object visit(ASTMapLiteral node, Object data) { 775 int childCount = node.jjtGetNumChildren(); 776 Map<Object, Object> map = new HashMap<Object, Object>(); 777 778 for (int i = 0; i < childCount; i++) { 779 Object[] entry = (Object[]) (node.jjtGetChild(i)).jjtAccept(this, data); 780 map.put(entry[0], entry[1]); 781 } 782 783 return map; 784 } 785 786 /** {@inheritDoc} */ 787 public Object visit(ASTMethodNode node, Object data) { 788 // the object to invoke the method on should be in the data argument 789 if (data == null) { 790 // if the first child of the (ASTReference) parent, 791 // it is considered as calling a 'top level' function 792 if (node.jjtGetParent().jjtGetChild(0) == node) { 793 data = resolveNamespace(null, node); 794 if (data == null) { 795 throw new JexlException(node, "no default function namespace"); 796 } 797 } else { 798 throw new JexlException(node, "attempting to call method on null"); 799 } 800 } 801 // objectNode 0 is the identifier (method name), the others are parameters. 802 String methodName = ((ASTIdentifier) node.jjtGetChild(0)).image; 803 804 // get our arguments 805 int argc = node.jjtGetNumChildren() - 1; 806 Object[] argv = new Object[argc]; 807 for (int i = 0; i < argc; i++) { 808 argv[i] = node.jjtGetChild(i + 1).jjtAccept(this, null); 809 } 810 811 JexlException xjexl = null; 812 try { 813 // attempt to reuse last executor cached in volatile JexlNode.value 814 if (cache) { 815 Object cached = node.jjtGetValue(); 816 if (cached instanceof JexlMethod) { 817 JexlMethod me = (JexlMethod) cached; 818 Object eval = me.tryInvoke(methodName, data, argv); 819 if (!me.tryFailed(eval)) { 820 return eval; 821 } 822 } 823 } 824 JexlMethod vm = uberspect.getMethod(data, methodName, argv, node); 825 // DG: If we can't find an exact match, narrow the parameters and try again! 826 if (vm == null) { 827 if (arithmetic.narrowArguments(argv)) { 828 vm = uberspect.getMethod(data, methodName, argv, node); 829 } 830 if (vm == null) { 831 xjexl = new JexlException(node, "unknown or ambiguous method", null); 832 } 833 } 834 if (xjexl == null) { 835 Object eval = vm.invoke(data, argv); // vm cannot be null if xjexl is null 836 // cache executor in volatile JexlNode.value 837 if (cache && vm.isCacheable()) { 838 node.jjtSetValue(vm); 839 } 840 return eval; 841 } 842 } catch (InvocationTargetException e) { 843 xjexl = new JexlException(node, "method invocation error", e.getCause()); 844 } catch (Exception e) { 845 xjexl = new JexlException(node, "method error", e); 846 } 847 return invocationFailed(xjexl); 848 } 849 850 /** {@inheritDoc} */ 851 public Object visit(ASTConstructorNode node, Object data) { 852 // first child is class or class name 853 Object cobject = node.jjtGetChild(0).jjtAccept(this, data); 854 // get the ctor args 855 int argc = node.jjtGetNumChildren() - 1; 856 Object[] argv = new Object[argc]; 857 for (int i = 0; i < argc; i++) { 858 argv[i] = node.jjtGetChild(i + 1).jjtAccept(this, null); 859 } 860 861 JexlException xjexl = null; 862 try { 863 Constructor<?> ctor = uberspect.getConstructor(cobject, argv, node); 864 // DG: If we can't find an exact match, narrow the parameters and 865 // try again! 866 if (ctor == null) { 867 if (arithmetic.narrowArguments(argv)) { 868 ctor = uberspect.getConstructor(cobject, argv, node); 869 } 870 if (ctor == null) { 871 xjexl = new JexlException(node, "unknown constructor", null); 872 } 873 } 874 if (xjexl == null) { 875 return ctor.newInstance(argv); 876 } 877 } catch (InvocationTargetException e) { 878 xjexl = new JexlException(node, "constructor invocation error", e.getCause()); 879 } catch (Exception e) { 880 xjexl = new JexlException(node, "constructor error", e); 881 } 882 return invocationFailed(xjexl); 883 } 884 885 /** {@inheritDoc} */ 886 public Object visit(ASTFunctionNode node, Object data) { 887 // objectNode 0 is the prefix 888 String prefix = ((ASTIdentifier) node.jjtGetChild(0)).image; 889 Object namespace = resolveNamespace(prefix, node); 890 // objectNode 1 is the identifier , the others are parameters. 891 String function = ((ASTIdentifier) node.jjtGetChild(1)).image; 892 893 // get our args 894 int argc = node.jjtGetNumChildren() - 2; 895 Object[] argv = new Object[argc]; 896 for (int i = 0; i < argc; i++) { 897 argv[i] = node.jjtGetChild(i + 2).jjtAccept(this, null); 898 } 899 900 JexlException xjexl = null; 901 try { 902 // attempt to reuse last executor cached in volatile JexlNode.value 903 if (cache) { 904 Object cached = node.jjtGetValue(); 905 if (cached instanceof JexlMethod) { 906 JexlMethod me = (JexlMethod) cached; 907 Object eval = me.tryInvoke(function, namespace, argv); 908 if (!me.tryFailed(eval)) { 909 return eval; 910 } 911 } 912 } 913 JexlMethod vm = uberspect.getMethod(namespace, function, argv, node); 914 // DG: If we can't find an exact match, narrow the parameters and 915 // try again! 916 if (vm == null) { 917 // replace all numbers with the smallest type that will fit 918 if (arithmetic.narrowArguments(argv)) { 919 vm = uberspect.getMethod(namespace, function, argv, node); 920 } 921 if (vm == null) { 922 xjexl = new JexlException(node, "unknown function", null); 923 } 924 } 925 if (xjexl == null) { 926 Object eval = vm.invoke(namespace, argv); // vm cannot be null if xjexl is null 927 // cache executor in volatile JexlNode.value 928 if (cache && vm.isCacheable()) { 929 node.jjtSetValue(vm); 930 } 931 return eval; 932 } 933 } catch (InvocationTargetException e) { 934 xjexl = new JexlException(node, "function invocation error", e.getCause()); 935 } catch (Exception e) { 936 xjexl = new JexlException(node, "function error", e); 937 } 938 return invocationFailed(xjexl); 939 } 940 941 /** {@inheritDoc} */ 942 public Object visit(ASTModNode node, Object data) { 943 Object left = node.jjtGetChild(0).jjtAccept(this, data); 944 Object right = node.jjtGetChild(1).jjtAccept(this, data); 945 try { 946 return arithmetic.mod(left, right); 947 } catch (RuntimeException xrt) { 948 if (!strict && xrt instanceof ArithmeticException) { 949 return new Double(0.0); 950 } 951 JexlNode xnode = findNullOperand(xrt, node, left, right); 952 throw new JexlException(xnode, "% error", xrt); 953 } 954 } 955 956 /** {@inheritDoc} */ 957 public Object visit(ASTMulNode node, Object data) { 958 Object left = node.jjtGetChild(0).jjtAccept(this, data); 959 Object right = node.jjtGetChild(1).jjtAccept(this, data); 960 try { 961 return arithmetic.multiply(left, right); 962 } catch (RuntimeException xrt) { 963 JexlNode xnode = findNullOperand(xrt, node, left, right); 964 throw new JexlException(xnode, "* error", xrt); 965 } 966 } 967 968 /** {@inheritDoc} */ 969 public Object visit(ASTNENode node, Object data) { 970 Object left = node.jjtGetChild(0).jjtAccept(this, data); 971 Object right = node.jjtGetChild(1).jjtAccept(this, data); 972 try { 973 return arithmetic.equals(left, right) ? Boolean.FALSE : Boolean.TRUE; 974 } catch (RuntimeException xrt) { 975 JexlNode xnode = findNullOperand(xrt, node, left, right); 976 throw new JexlException(xnode, "!= error", xrt); 977 } 978 } 979 980 /** {@inheritDoc} */ 981 public Object visit(ASTNRNode node, Object data) { 982 Object left = node.jjtGetChild(0).jjtAccept(this, data); 983 Object right = node.jjtGetChild(1).jjtAccept(this, data); 984 try { 985 return arithmetic.matches(left, right) ? Boolean.FALSE : Boolean.TRUE; 986 } catch (RuntimeException xrt) { 987 throw new JexlException(node, "!~ error", xrt); 988 } 989 } 990 991 /** {@inheritDoc} */ 992 public Object visit(ASTNotNode node, Object data) { 993 Object val = node.jjtGetChild(0).jjtAccept(this, data); 994 return arithmetic.toBoolean(val) ? Boolean.FALSE : Boolean.TRUE; 995 } 996 997 /** {@inheritDoc} */ 998 public Object visit(ASTNullLiteral node, Object data) { 999 return null; 1000 } 1001 1002 /** {@inheritDoc} */ 1003 public Object visit(ASTOrNode node, Object data) { 1004 Object left = node.jjtGetChild(0).jjtAccept(this, data); 1005 try { 1006 boolean leftValue = arithmetic.toBoolean(left); 1007 if (leftValue) { 1008 return Boolean.TRUE; 1009 } 1010 } catch (RuntimeException xrt) { 1011 throw new JexlException(node.jjtGetChild(0), "boolean coercion error", xrt); 1012 } 1013 Object right = node.jjtGetChild(1).jjtAccept(this, data); 1014 try { 1015 boolean rightValue = arithmetic.toBoolean(right); 1016 if (rightValue) { 1017 return Boolean.TRUE; 1018 } 1019 } catch (RuntimeException xrt) { 1020 throw new JexlException(node.jjtGetChild(1), "boolean coercion error", xrt); 1021 } 1022 return Boolean.FALSE; 1023 } 1024 1025 /** {@inheritDoc} */ 1026 public Object visit(ASTReference node, Object data) { 1027 // could be array access, identifier or map literal 1028 // followed by zero or more ("." and array access, method, size, 1029 // identifier or integer literal) 1030 1031 int numChildren = node.jjtGetNumChildren(); 1032 1033 // pass first piece of data in and loop through children 1034 Object result = null; 1035 StringBuilder variableName = null; 1036 boolean isVariable = true; 1037 int v = 0; 1038 for (int c = 0; c < numChildren; c++) { 1039 JexlNode theNode = node.jjtGetChild(c); 1040 // integer literals may be part of an antish var name only if no bean was found so far 1041 if (result == null && theNode instanceof ASTIntegerLiteral) { 1042 isVariable &= v > 0; 1043 } else { 1044 isVariable &= (theNode instanceof ASTIdentifier); 1045 result = theNode.jjtAccept(this, result); 1046 } 1047 // if we get null back a result, check for an ant variable 1048 if (result == null && isVariable) { 1049 if (v == 0) { 1050 variableName = new StringBuilder(node.jjtGetChild(0).image); 1051 v = 1; 1052 } 1053 for (; v <= c; ++v) { 1054 variableName.append('.'); 1055 variableName.append(node.jjtGetChild(v).image); 1056 } 1057 result = context.get(variableName.toString()); 1058 } 1059 } 1060 if (result == null) { 1061 if (isVariable 1062 && !(node.jjtGetParent() instanceof ASTTernaryNode) 1063 && !context.has(variableName.toString())) { 1064 JexlException xjexl = new JexlException(node, "undefined variable " + variableName.toString()); 1065 return unknownVariable(xjexl); 1066 } 1067 } 1068 return result; 1069 } 1070 1071 /** {@inheritDoc} */ 1072 public Object visit(ASTSizeFunction node, Object data) { 1073 Object val = node.jjtGetChild(0).jjtAccept(this, data); 1074 1075 if (val == null) { 1076 throw new JexlException(node, "size() : argument is null", null); 1077 } 1078 1079 return Integer.valueOf(sizeOf(node, val)); 1080 } 1081 1082 /** {@inheritDoc} */ 1083 public Object visit(ASTSizeMethod node, Object data) { 1084 return Integer.valueOf(sizeOf(node, data)); 1085 } 1086 1087 /** {@inheritDoc} */ 1088 public Object visit(ASTStringLiteral node, Object data) { 1089 if (data != null) { 1090 return getAttribute(data, node.getLiteral(), node); 1091 } 1092 return node.image; 1093 } 1094 1095 /** {@inheritDoc} */ 1096 public Object visit(ASTTernaryNode node, Object data) { 1097 Object condition = node.jjtGetChild(0).jjtAccept(this, data); 1098 if (node.jjtGetNumChildren() == 3) { 1099 if (condition != null && arithmetic.toBoolean(condition)) { 1100 return node.jjtGetChild(1).jjtAccept(this, data); 1101 } else { 1102 return node.jjtGetChild(2).jjtAccept(this, data); 1103 } 1104 } 1105 if (condition != null && !Boolean.FALSE.equals(condition)) { 1106 return condition; 1107 } else { 1108 return node.jjtGetChild(1).jjtAccept(this, data); 1109 } 1110 } 1111 1112 /** {@inheritDoc} */ 1113 public Object visit(ASTTrueNode node, Object data) { 1114 return Boolean.TRUE; 1115 } 1116 1117 /** {@inheritDoc} */ 1118 public Object visit(ASTUnaryMinusNode node, Object data) { 1119 JexlNode valNode = node.jjtGetChild(0); 1120 Object val = valNode.jjtAccept(this, data); 1121 if (val instanceof Byte) { 1122 byte valueAsByte = ((Byte) val).byteValue(); 1123 return Byte.valueOf((byte) -valueAsByte); 1124 } else if (val instanceof Short) { 1125 short valueAsShort = ((Short) val).shortValue(); 1126 return Short.valueOf((short) -valueAsShort); 1127 } else if (val instanceof Integer) { 1128 int valueAsInt = ((Integer) val).intValue(); 1129 return Integer.valueOf(-valueAsInt); 1130 } else if (val instanceof Long) { 1131 long valueAsLong = ((Long) val).longValue(); 1132 return Long.valueOf(-valueAsLong); 1133 } else if (val instanceof Float) { 1134 float valueAsFloat = ((Float) val).floatValue(); 1135 return new Float(-valueAsFloat); 1136 } else if (val instanceof Double) { 1137 double valueAsDouble = ((Double) val).doubleValue(); 1138 return new Double(-valueAsDouble); 1139 } else if (val instanceof BigDecimal) { 1140 BigDecimal valueAsBigD = (BigDecimal) val; 1141 return valueAsBigD.negate(); 1142 } else if (val instanceof BigInteger) { 1143 BigInteger valueAsBigI = (BigInteger) val; 1144 return valueAsBigI.negate(); 1145 } 1146 throw new JexlException(valNode, "not a number"); 1147 } 1148 1149 /** {@inheritDoc} */ 1150 public Object visit(ASTWhileStatement node, Object data) { 1151 Object result = null; 1152 /* first objectNode is the expression */ 1153 Node expressionNode = node.jjtGetChild(0); 1154 while (arithmetic.toBoolean(expressionNode.jjtAccept(this, data))) { 1155 // execute statement 1156 result = node.jjtGetChild(1).jjtAccept(this, data); 1157 } 1158 1159 return result; 1160 } 1161 1162 /** 1163 * Calculate the <code>size</code> of various types: Collection, Array, 1164 * Map, String, and anything that has a int size() method. 1165 * @param node the node that gave the value to size 1166 * @param val the object to get the size of. 1167 * @return the size of val 1168 */ 1169 private int sizeOf(JexlNode node, Object val) { 1170 if (val instanceof Collection<?>) { 1171 return ((Collection<?>) val).size(); 1172 } else if (val.getClass().isArray()) { 1173 return Array.getLength(val); 1174 } else if (val instanceof Map<?, ?>) { 1175 return ((Map<?, ?>) val).size(); 1176 } else if (val instanceof String) { 1177 return ((String) val).length(); 1178 } else { 1179 // check if there is a size method on the object that returns an 1180 // integer and if so, just use it 1181 Object[] params = new Object[0]; 1182 JexlMethod vm = uberspect.getMethod(val, "size", EMPTY_PARAMS, node); 1183 if (vm != null && vm.getReturnType() == Integer.TYPE) { 1184 Integer result; 1185 try { 1186 result = (Integer) vm.invoke(val, params); 1187 } catch (Exception e) { 1188 throw new JexlException(node, "size() : error executing", e); 1189 } 1190 return result.intValue(); 1191 } 1192 throw new JexlException(node, "size() : unsupported type : " + val.getClass(), null); 1193 } 1194 } 1195 1196 /** 1197 * Gets an attribute of an object. 1198 * 1199 * @param object to retrieve value from 1200 * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or 1201 * key for a map 1202 * @return the attribute value 1203 */ 1204 public Object getAttribute(Object object, Object attribute) { 1205 return getAttribute(object, attribute, null); 1206 } 1207 1208 /** 1209 * Gets an attribute of an object. 1210 * 1211 * @param object to retrieve value from 1212 * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or 1213 * key for a map 1214 * @param node the node that evaluated as the object 1215 * @return the attribute value 1216 */ 1217 protected Object getAttribute(Object object, Object attribute, JexlNode node) { 1218 if (object == null) { 1219 throw new JexlException(node, "object is null"); 1220 } 1221 // attempt to reuse last executor cached in volatile JexlNode.value 1222 if (node != null && cache) { 1223 Object cached = node.jjtGetValue(); 1224 if (cached instanceof JexlPropertyGet) { 1225 JexlPropertyGet vg = (JexlPropertyGet) cached; 1226 Object value = vg.tryInvoke(object, attribute); 1227 if (!vg.tryFailed(value)) { 1228 return value; 1229 } 1230 } 1231 } 1232 JexlPropertyGet vg = uberspect.getPropertyGet(object, attribute, node); 1233 if (vg != null) { 1234 try { 1235 Object value = vg.invoke(object); 1236 // cache executor in volatile JexlNode.value 1237 if (node != null && cache && vg.isCacheable()) { 1238 node.jjtSetValue(vg); 1239 } 1240 return value; 1241 } catch (Exception xany) { 1242 if (node == null) { 1243 throw new RuntimeException(xany); 1244 } else { 1245 JexlException xjexl = new JexlException(node, "get object property error", xany); 1246 if (strict) { 1247 throw xjexl; 1248 } 1249 if (!silent) { 1250 logger.warn(xjexl.getMessage()); 1251 } 1252 } 1253 } 1254 } 1255 1256 return null; 1257 } 1258 1259 /** 1260 * Sets an attribute of an object. 1261 * 1262 * @param object to set the value to 1263 * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or 1264 * key for a map 1265 * @param value the value to assign to the object's attribute 1266 */ 1267 public void setAttribute(Object object, Object attribute, Object value) { 1268 setAttribute(object, attribute, value, null); 1269 } 1270 1271 /** 1272 * Sets an attribute of an object. 1273 * 1274 * @param object to set the value to 1275 * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or 1276 * key for a map 1277 * @param value the value to assign to the object's attribute 1278 * @param node the node that evaluated as the object 1279 */ 1280 protected void setAttribute(Object object, Object attribute, Object value, JexlNode node) { 1281 // attempt to reuse last executor cached in volatile JexlNode.value 1282 if (node != null && cache) { 1283 Object cached = node.jjtGetValue(); 1284 if (cached instanceof JexlPropertySet) { 1285 JexlPropertySet setter = (JexlPropertySet) cached; 1286 Object eval = setter.tryInvoke(object, attribute, value); 1287 if (!setter.tryFailed(eval)) { 1288 return; 1289 } 1290 } 1291 } 1292 JexlException xjexl = null; 1293 JexlPropertySet vs = uberspect.getPropertySet(object, attribute, value, node); 1294 if (vs != null) { 1295 try { 1296 // cache executor in volatile JexlNode.value 1297 vs.invoke(object, value); 1298 if (node != null && cache && vs.isCacheable()) { 1299 node.jjtSetValue(vs); 1300 } 1301 return; 1302 } catch (RuntimeException xrt) { 1303 if (node == null) { 1304 throw xrt; 1305 } 1306 xjexl = new JexlException(node, "set object property error", xrt); 1307 } catch (Exception xany) { 1308 if (node == null) { 1309 throw new RuntimeException(xany); 1310 } 1311 xjexl = new JexlException(node, "set object property error", xany); 1312 } 1313 } 1314 if (xjexl == null) { 1315 String error = "unable to set object property" 1316 + ", class: " + object.getClass().getName() 1317 + ", property: " + attribute; 1318 if (node == null) { 1319 throw new UnsupportedOperationException(error); 1320 } 1321 xjexl = new JexlException(node, error, null); 1322 } 1323 if (strict) { 1324 throw xjexl; 1325 } 1326 if (!silent) { 1327 logger.warn(xjexl.getMessage()); 1328 } 1329 } 1330 1331 /** 1332 * Unused, satisfy ParserVisitor interface. 1333 * @param node a node 1334 * @param data the data 1335 * @return does not return 1336 */ 1337 public Object visit(SimpleNode node, Object data) { 1338 throw new UnsupportedOperationException("Not supported yet."); 1339 } 1340 1341 /** 1342 * Unused, should throw in Parser. 1343 * @param node a node 1344 * @param data the data 1345 * @return does not return 1346 */ 1347 public Object visit(ASTAmbiguous node, Object data) { 1348 throw new UnsupportedOperationException("unexpected type of node"); 1349 } 1350 }