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 }