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