org.apache.commons.jexl2.Interpreter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of commons-jexl Show documentation
Show all versions of commons-jexl Show documentation
Jexl is an implementation of the JSTL Expression Language with extensions.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.jexl2;
import java.lang.reflect.Constructor;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.jexl2.parser.SimpleNode;
import org.apache.commons.logging.Log;
import org.apache.commons.jexl2.parser.JexlNode;
import org.apache.commons.jexl2.parser.ASTAdditiveNode;
import org.apache.commons.jexl2.parser.ASTAdditiveOperator;
import org.apache.commons.jexl2.parser.ASTAndNode;
import org.apache.commons.jexl2.parser.ASTAmbiguous;
import org.apache.commons.jexl2.parser.ASTArrayAccess;
import org.apache.commons.jexl2.parser.ASTArrayLiteral;
import org.apache.commons.jexl2.parser.ASTAssignment;
import org.apache.commons.jexl2.parser.ASTBitwiseAndNode;
import org.apache.commons.jexl2.parser.ASTBitwiseComplNode;
import org.apache.commons.jexl2.parser.ASTBitwiseOrNode;
import org.apache.commons.jexl2.parser.ASTBitwiseXorNode;
import org.apache.commons.jexl2.parser.ASTBlock;
import org.apache.commons.jexl2.parser.ASTConstructorNode;
import org.apache.commons.jexl2.parser.ASTDivNode;
import org.apache.commons.jexl2.parser.ASTEQNode;
import org.apache.commons.jexl2.parser.ASTERNode;
import org.apache.commons.jexl2.parser.ASTEmptyFunction;
import org.apache.commons.jexl2.parser.ASTFalseNode;
import org.apache.commons.jexl2.parser.ASTFunctionNode;
import org.apache.commons.jexl2.parser.ASTFloatLiteral;
import org.apache.commons.jexl2.parser.ASTForeachStatement;
import org.apache.commons.jexl2.parser.ASTGENode;
import org.apache.commons.jexl2.parser.ASTGTNode;
import org.apache.commons.jexl2.parser.ASTIdentifier;
import org.apache.commons.jexl2.parser.ASTIfStatement;
import org.apache.commons.jexl2.parser.ASTIntegerLiteral;
import org.apache.commons.jexl2.parser.ASTJexlScript;
import org.apache.commons.jexl2.parser.ASTLENode;
import org.apache.commons.jexl2.parser.ASTLTNode;
import org.apache.commons.jexl2.parser.ASTMapEntry;
import org.apache.commons.jexl2.parser.ASTMapLiteral;
import org.apache.commons.jexl2.parser.ASTMethodNode;
import org.apache.commons.jexl2.parser.ASTModNode;
import org.apache.commons.jexl2.parser.ASTMulNode;
import org.apache.commons.jexl2.parser.ASTNENode;
import org.apache.commons.jexl2.parser.ASTNRNode;
import org.apache.commons.jexl2.parser.ASTNotNode;
import org.apache.commons.jexl2.parser.ASTNullLiteral;
import org.apache.commons.jexl2.parser.ASTOrNode;
import org.apache.commons.jexl2.parser.ASTReference;
import org.apache.commons.jexl2.parser.ASTSizeFunction;
import org.apache.commons.jexl2.parser.ASTSizeMethod;
import org.apache.commons.jexl2.parser.ASTStringLiteral;
import org.apache.commons.jexl2.parser.ASTTernaryNode;
import org.apache.commons.jexl2.parser.ASTTrueNode;
import org.apache.commons.jexl2.parser.ASTUnaryMinusNode;
import org.apache.commons.jexl2.parser.ASTWhileStatement;
import org.apache.commons.jexl2.parser.Node;
import org.apache.commons.jexl2.parser.ParserVisitor;
import org.apache.commons.jexl2.introspection.Uberspect;
import org.apache.commons.jexl2.introspection.JexlMethod;
import org.apache.commons.jexl2.introspection.JexlPropertyGet;
import org.apache.commons.jexl2.introspection.JexlPropertySet;
/**
* An interpreter of JEXL syntax.
*
* @since 2.0
*/
public class Interpreter implements ParserVisitor {
/** The logger. */
protected final Log logger;
/** The uberspect. */
protected final Uberspect uberspect;
/** The arithmetic handler. */
protected final JexlArithmetic arithmetic;
/** The map of registered functions. */
protected final Map functions;
/** The map of registered functions. */
protected Map functors;
/** The context to store/retrieve variables. */
protected final JexlContext context;
/** Strict interpreter flag. */
protected final boolean strict;
/** Silent intepreter flag. */
protected boolean silent;
/** Cache executors. */
protected final boolean cache;
/** Registers made of 2 pairs of {register-name, value}. */
protected Object[] registers = null;
/** Empty parameters for method matching. */
protected static final Object[] EMPTY_PARAMS = new Object[0];
/**
* Creates an interpreter.
* @param jexl the engine creating this interpreter
* @param aContext the context to evaluate expression
*/
public Interpreter(JexlEngine jexl, JexlContext aContext) {
this.logger = jexl.logger;
this.uberspect = jexl.uberspect;
this.arithmetic = jexl.arithmetic;
this.functions = jexl.functions;
this.strict = !this.arithmetic.isLenient();
this.silent = jexl.silent;
this.cache = jexl.cache != null;
this.context = aContext;
this.functors = null;
}
/**
* Sets whether this interpreter throws JexlException during evaluation.
* @param flag true means no JexlException will be thrown but will be logged
* as info through the Jexl engine logger, false allows them to be thrown.
*/
public void setSilent(boolean flag) {
this.silent = flag;
}
/**
* Checks whether this interpreter throws JexlException during evaluation.
* @return true if silent, false otherwise
*/
public boolean isSilent() {
return this.silent;
}
/**
* Interpret the given script/expression.
*
* If the underlying JEXL engine is silent, errors will be logged through its logger as info.
*
* @param node the script or expression to interpret.
* @return the result of the interpretation.
* @throws JexlException if any error occurs during interpretation.
*/
public Object interpret(JexlNode node) {
try {
return node.jjtAccept(this, null);
} catch (JexlException xjexl) {
if (silent) {
logger.warn(xjexl.getMessage(), xjexl.getCause());
return null;
}
throw xjexl;
}
}
/**
* Gets the uberspect.
* @return an {@link Uberspect}
*/
protected Uberspect getUberspect() {
return uberspect;
}
/**
* Sets this interpreter registers for bean access/assign expressions.
* @param theRegisters the array of registers
*/
protected void setRegisters(Object[] theRegisters) {
this.registers = theRegisters;
}
/**
* Finds the node causing a NPE for diadic operators.
* @param xrt the RuntimeException
* @param node the parent node
* @param left the left argument
* @param right the right argument
* @return the left, right or parent node
*/
protected JexlNode findNullOperand(RuntimeException xrt, JexlNode node, Object left, Object right) {
if (xrt instanceof NullPointerException
&& JexlException.NULL_OPERAND == xrt.getMessage()) {
if (left == null) {
return node.jjtGetChild(0);
}
if (right == null) {
return node.jjtGetChild(1);
}
}
return node;
}
/**
* Triggered when variable can not be resolved.
* @param xjexl the JexlException ("undefined variable " + variable)
* @return throws JexlException if strict, null otherwise
*/
protected Object unknownVariable(JexlException xjexl) {
if (strict) {
throw xjexl;
}
if (!silent) {
logger.warn(xjexl.getMessage());
}
return null;
}
/**
* Triggered when method, function or constructor invocation fails.
* @param xjexl the JexlException wrapping the original error
* @return throws JexlException if strict, null otherwise
*/
protected Object invocationFailed(JexlException xjexl) {
if (strict) {
throw xjexl;
}
if (!silent) {
logger.warn(xjexl.getMessage(), xjexl.getCause());
}
return null;
}
/**
* Resolves a namespace, eventually allocating an instance using context as constructor argument.
* The lifetime of such instances span the current expression or script evaluation.
*
* @param prefix the prefix name (may be null for global namespace)
* @param node the AST node
* @return the namespace instance
*/
protected Object resolveNamespace(String prefix, JexlNode node) {
Object namespace;
// check whether this namespace is a functor
if (functors != null) {
namespace = functors.get(prefix);
if (namespace != null) {
return namespace;
}
}
namespace = functions.get(prefix);
if (namespace == null) {
throw new JexlException(node, "no such function namespace " + prefix);
}
// allow namespace to be instantiated as functor with context
if (namespace instanceof Class>) {
Object[] args = new Object[]{context};
Constructor> ctor = uberspect.getConstructor(namespace,args, node);
if (ctor != null) {
try {
namespace = ctor.newInstance(args);
if (functors == null) {
functors = new HashMap();
}
functors.put(prefix, namespace);
} catch (Exception xinst) {
throw new JexlException(node, "unable to instantiate namespace " + prefix, xinst);
}
}
}
return namespace;
}
/** {@inheritDoc} */
public Object visit(ASTAdditiveNode node, Object data) {
/**
* The pattern for exception mgmt is to let the child*.jjtAccept
* out of the try/catch loop so that if one fails, the ex will
* traverse up to the interpreter.
* In cases where this is not convenient/possible, JexlException must
* be caught explicitly and rethrown.
*/
Object left = node.jjtGetChild(0).jjtAccept(this, data);
for(int c = 2, size = node.jjtGetNumChildren(); c < size; c += 2) {
Object right = node.jjtGetChild(c).jjtAccept(this, data);
try {
JexlNode op = node.jjtGetChild(c - 1);
if (op instanceof ASTAdditiveOperator) {
String which = ((ASTAdditiveOperator) op).image;
if ("+".equals(which)) {
left = arithmetic.add(left, right);
continue;
}
if ("-".equals(which)) {
left = arithmetic.subtract(left, right);
continue;
}
throw new UnsupportedOperationException("unknown operator " + which);
}
throw new IllegalArgumentException("unknown operator " + op);
} catch (RuntimeException xrt) {
JexlNode xnode = findNullOperand(xrt, node, left, right);
throw new JexlException(xnode, "+/- error", xrt);
}
}
return left;
}
/** {@inheritDoc} */
public Object visit(ASTAdditiveOperator node, Object data) {
throw new UnsupportedOperationException("Shoud not be called.");
}
/** {@inheritDoc} */
public Object visit(ASTAndNode node, Object data) {
Object left = node.jjtGetChild(0).jjtAccept(this, data);
try {
boolean leftValue = arithmetic.toBoolean(left);
if (!leftValue) {
return Boolean.FALSE;
}
} catch (RuntimeException xrt) {
throw new JexlException(node.jjtGetChild(0), "boolean coercion error", xrt);
}
Object right = node.jjtGetChild(1).jjtAccept(this, data);
try {
boolean rightValue = arithmetic.toBoolean(right);
if (!rightValue) {
return Boolean.FALSE;
}
} catch (RuntimeException xrt) {
throw new JexlException(node.jjtGetChild(1), "boolean coercion error", xrt);
}
return Boolean.TRUE;
}
/** {@inheritDoc} */
public Object visit(ASTArrayAccess node, Object data) {
// first objectNode is the identifier
Object object = node.jjtGetChild(0).jjtAccept(this, data);
// can have multiple nodes - either an expression, integer literal or
// reference
int numChildren = node.jjtGetNumChildren();
for (int i = 1; i < numChildren; i++) {
JexlNode nindex = node.jjtGetChild(i);
Object index = nindex.jjtAccept(this, null);
object = getAttribute(object, index, nindex);
}
return object;
}
/** {@inheritDoc} */
public Object visit(ASTArrayLiteral node, Object data) {
int childCount = node.jjtGetNumChildren();
Object[] array = new Object[childCount];
for (int i = 0; i < childCount; i++) {
Object entry = node.jjtGetChild(i).jjtAccept(this, data);
array[i] = entry;
}
return arithmetic.narrowArrayType(array);
}
/** {@inheritDoc} */
public Object visit(ASTAssignment node, Object data) {
// left contains the reference to assign to
JexlNode left = node.jjtGetChild(0);
if (!(left instanceof ASTReference)) {
throw new JexlException(left, "illegal assignment form");
}
// right is the value expression to assign
Object right = node.jjtGetChild(1).jjtAccept(this, data);
// determine initial object & property:
JexlNode objectNode = null;
Object object = null;
JexlNode propertyNode = null;
Object property = null;
boolean isVariable = true;
int v = 0;
StringBuilder variableName = null;
// 1: follow children till penultimate
int last = left.jjtGetNumChildren() - 1;
for (int c = 0; c < last; ++c) {
objectNode = left.jjtGetChild(c);
// evaluate the property within the object
object = objectNode.jjtAccept(this, object);
if (object != null) {
continue;
}
isVariable &= objectNode instanceof ASTIdentifier;
// if we get null back as a result, check for an ant variable
if (isVariable) {
if (v == 0) {
variableName = new StringBuilder(left.jjtGetChild(0).image);
v = 1;
}
for(; v <= c; ++v) {
variableName.append('.');
variableName.append(left.jjtGetChild(v).image);
}
object = context.get(variableName.toString());
// disallow mixing ant & bean with same root; avoid ambiguity
if (object != null) {
isVariable = false;
}
} else {
throw new JexlException(objectNode, "illegal assignment form");
}
}
// 2: last objectNode will perform assignement in all cases
propertyNode = left.jjtGetChild(last);
if (propertyNode instanceof ASTIdentifier) {
property = ((ASTIdentifier) propertyNode).image;
// deal with ant variable
if (isVariable && object == null) {
if (variableName != null) {
if (last > 0) {
variableName.append('.');
}
variableName.append(property);
property = variableName.toString();
}
context.set(String.valueOf(property), right);
return right;
}
} else if (propertyNode instanceof ASTIntegerLiteral) {
property = visit((ASTIntegerLiteral) propertyNode, null);
// deal with ant variable
if (isVariable && object == null) {
if (variableName != null) {
if (last > 0) {
variableName.append('.');
}
variableName.append(property);
property = variableName.toString();
}
context.set(String.valueOf(property), right);
return right;
}
} else if (propertyNode instanceof ASTArrayAccess) {
// first objectNode is the identifier
objectNode = propertyNode;
ASTArrayAccess narray = (ASTArrayAccess) objectNode;
Object nobject = narray.jjtGetChild(0).jjtAccept(this, object);
if (nobject == null) {
throw new JexlException(objectNode, "array element is null");
} else {
object = nobject;
}
// can have multiple nodes - either an expression, integer literal or
// reference
last = narray.jjtGetNumChildren() - 1;
for (int i = 1; i < last; i++) {
objectNode = narray.jjtGetChild(i);
Object index = objectNode.jjtAccept(this, null);
object = getAttribute(object, index, objectNode);
}
property = narray.jjtGetChild(last).jjtAccept(this, null);
} else {
throw new JexlException(objectNode, "illegal assignment form");
}
if (property == null) {
// no property, we fail
throw new JexlException(propertyNode, "property is null");
}
if (object == null) {
// no object, we fail
throw new JexlException(objectNode, "bean is null");
}
// one before last, assign
setAttribute(object, property, right, propertyNode);
return right;
}
/** {@inheritDoc} */
public Object visit(ASTBitwiseAndNode node, Object data) {
Object left = node.jjtGetChild(0).jjtAccept(this, data);
Object right = node.jjtGetChild(1).jjtAccept(this, data);
int n = 0;
// coerce these two values longs and 'and'.
try {
long l = arithmetic.toLong(left);
n = 1;
long r = arithmetic.toLong(right);
return Long.valueOf(l & r);
} catch (RuntimeException xrt) {
throw new JexlException(node.jjtGetChild(n), "long coercion error", xrt);
}
}
/** {@inheritDoc} */
public Object visit(ASTBitwiseComplNode node, Object data) {
Object left = node.jjtGetChild(0).jjtAccept(this, data);
try {
long l = arithmetic.toLong(left);
return Long.valueOf(~l);
} catch (RuntimeException xrt) {
throw new JexlException(node.jjtGetChild(0), "long coercion error", xrt);
}
}
/** {@inheritDoc} */
public Object visit(ASTBitwiseOrNode node, Object data) {
Object left = node.jjtGetChild(0).jjtAccept(this, data);
Object right = node.jjtGetChild(1).jjtAccept(this, data);
int n = 0;
// coerce these two values longs and 'or'.
try {
long l = arithmetic.toLong(left);
n = 1;
long r = arithmetic.toLong(right);
return Long.valueOf(l | r);
} catch (RuntimeException xrt) {
throw new JexlException(node.jjtGetChild(n), "long coercion error", xrt);
}
}
/** {@inheritDoc} */
public Object visit(ASTBitwiseXorNode node, Object data) {
Object left = node.jjtGetChild(0).jjtAccept(this, data);
Object right = node.jjtGetChild(1).jjtAccept(this, data);
int n = 0;
// coerce these two values longs and 'xor'.
try {
long l = arithmetic.toLong(left);
n = 1;
long r = arithmetic.toLong(right);
return Long.valueOf(l ^ r);
} catch (RuntimeException xrt) {
throw new JexlException(node.jjtGetChild(n), "long coercion error", xrt);
}
}
/** {@inheritDoc} */
public Object visit(ASTBlock node, Object data) {
int numChildren = node.jjtGetNumChildren();
Object result = null;
for (int i = 0; i < numChildren; i++) {
result = node.jjtGetChild(i).jjtAccept(this, data);
}
return result;
}
/** {@inheritDoc} */
public Object visit(ASTDivNode node, Object data) {
Object left = node.jjtGetChild(0).jjtAccept(this, data);
Object right = node.jjtGetChild(1).jjtAccept(this, data);
try {
return arithmetic.divide(left, right);
} catch (RuntimeException xrt) {
if (!strict && xrt instanceof ArithmeticException) {
return new Double(0.0);
}
JexlNode xnode = findNullOperand(xrt, node, left, right);
throw new JexlException(xnode, "divide error", xrt);
}
}
/** {@inheritDoc} */
public Object visit(ASTEmptyFunction node, Object data) {
Object o = node.jjtGetChild(0).jjtAccept(this, data);
if (o == null) {
return Boolean.TRUE;
}
if (o instanceof String && "".equals(o)) {
return Boolean.TRUE;
}
if (o.getClass().isArray() && ((Object[]) o).length == 0) {
return Boolean.TRUE;
}
if (o instanceof Collection> && ((Collection>) o).isEmpty()) {
return Boolean.TRUE;
}
// Map isn't a collection
if (o instanceof Map, ?> && ((Map, ?>) o).isEmpty()) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
/** {@inheritDoc} */
public Object visit(ASTEQNode node, Object data) {
Object left = node.jjtGetChild(0).jjtAccept(this, data);
Object right = node.jjtGetChild(1).jjtAccept(this, data);
try {
return arithmetic.equals(left, right) ? Boolean.TRUE : Boolean.FALSE;
} catch (RuntimeException xrt) {
throw new JexlException(node, "== error", xrt);
}
}
/** {@inheritDoc} */
public Object visit(ASTFalseNode node, Object data) {
return Boolean.FALSE;
}
/** {@inheritDoc} */
public Object visit(ASTFloatLiteral node, Object data) {
Float value = (Float) node.jjtGetValue();
if (value == null) {
value = Float.valueOf(node.image);
node.jjtSetValue(value);
}
return value;
}
/** {@inheritDoc} */
public Object visit(ASTForeachStatement node, Object data) {
Object result = null;
/* first objectNode is the loop variable */
ASTReference loopReference = (ASTReference) node.jjtGetChild(0);
ASTIdentifier loopVariable = (ASTIdentifier) loopReference.jjtGetChild(0);
/* second objectNode is the variable to iterate */
Object iterableValue = node.jjtGetChild(1).jjtAccept(this, data);
// make sure there is a value to iterate on and a statement to execute
if (iterableValue != null && node.jjtGetNumChildren() >= 3) {
/* third objectNode is the statement to execute */
JexlNode statement = node.jjtGetChild(2);
// get an iterator for the collection/array etc via the
// introspector.
Iterator> itemsIterator = getUberspect().getIterator(iterableValue, node);
if (itemsIterator != null) {
while (itemsIterator.hasNext()) {
// set loopVariable to value of iterator
Object value = itemsIterator.next();
context.set(loopVariable.image, value);
// execute statement
result = statement.jjtAccept(this, data);
}
}
}
return result;
}
/** {@inheritDoc} */
public Object visit(ASTGENode node, Object data) {
Object left = node.jjtGetChild(0).jjtAccept(this, data);
Object right = node.jjtGetChild(1).jjtAccept(this, data);
try {
return arithmetic.greaterThanOrEqual(left, right) ? Boolean.TRUE : Boolean.FALSE;
} catch (RuntimeException xrt) {
throw new JexlException(node, ">= error", xrt);
}
}
/** {@inheritDoc} */
public Object visit(ASTGTNode node, Object data) {
Object left = node.jjtGetChild(0).jjtAccept(this, data);
Object right = node.jjtGetChild(1).jjtAccept(this, data);
try {
return arithmetic.greaterThan(left, right) ? Boolean.TRUE : Boolean.FALSE;
} catch (RuntimeException xrt) {
throw new JexlException(node, "> error", xrt);
}
}
/** {@inheritDoc} */
public Object visit(ASTERNode node, Object data) {
Object left = node.jjtGetChild(0).jjtAccept(this, data);
Object right = node.jjtGetChild(1).jjtAccept(this, data);
try {
return arithmetic.matches(left, right) ? Boolean.TRUE : Boolean.FALSE;
} catch (RuntimeException xrt) {
throw new JexlException(node, "=~ error", xrt);
}
}
/** {@inheritDoc} */
public Object visit(ASTIdentifier node, Object data) {
String name = node.image;
if (data == null) {
if (registers != null) {
if (registers[0].equals(name)) {
return registers[1];
}
if (registers[2].equals(name)) {
return registers[3];
}
}
Object value = context.get(name);
if (value == null
&& !(node.jjtGetParent() instanceof ASTReference)
&& !context.has(name)) {
JexlException xjexl = new JexlException(node, "undefined variable " + name);
return unknownVariable(xjexl);
}
return value;
} else {
return getAttribute(data, name, node);
}
}
/** {@inheritDoc} */
public Object visit(ASTIfStatement node, Object data) {
int n = 0;
try {
Object result = null;
/* first objectNode is the expression */
Object expression = node.jjtGetChild(0).jjtAccept(this, data);
if (arithmetic.toBoolean(expression)) {
// first objectNode is true statement
n = 1;
result = node.jjtGetChild(1).jjtAccept(this, data);
} else {
// if there is a false, execute it. false statement is the second
// objectNode
if (node.jjtGetNumChildren() == 3) {
n = 2;
result = node.jjtGetChild(2).jjtAccept(this, data);
}
}
return result;
} catch (JexlException error) {
throw error;
} catch (RuntimeException xrt) {
throw new JexlException(node.jjtGetChild(n), "if error", xrt);
}
}
/** {@inheritDoc} */
public Object visit(ASTIntegerLiteral node, Object data) {
if (data != null) {
Integer value = Integer.valueOf(node.image);
return getAttribute(data, value, node);
}
Integer value = (Integer) node.jjtGetValue();
if (value == null) {
value = Integer.valueOf(node.image);
node.jjtSetValue(value);
}
return value;
}
/** {@inheritDoc} */
public Object visit(ASTJexlScript node, Object data) {
int numChildren = node.jjtGetNumChildren();
Object result = null;
for (int i = 0; i < numChildren; i++) {
JexlNode child = node.jjtGetChild(i);
result = child.jjtAccept(this, data);
}
return result;
}
/** {@inheritDoc} */
public Object visit(ASTLENode node, Object data) {
Object left = node.jjtGetChild(0).jjtAccept(this, data);
Object right = node.jjtGetChild(1).jjtAccept(this, data);
try {
return arithmetic.lessThanOrEqual(left, right) ? Boolean.TRUE : Boolean.FALSE;
} catch (RuntimeException xrt) {
throw new JexlException(node, "<= error", xrt);
}
}
/** {@inheritDoc} */
public Object visit(ASTLTNode node, Object data) {
Object left = node.jjtGetChild(0).jjtAccept(this, data);
Object right = node.jjtGetChild(1).jjtAccept(this, data);
try {
return arithmetic.lessThan(left, right) ? Boolean.TRUE : Boolean.FALSE;
} catch (RuntimeException xrt) {
throw new JexlException(node, "< error", xrt);
}
}
/** {@inheritDoc} */
public Object visit(ASTMapEntry node, Object data) {
Object key = node.jjtGetChild(0).jjtAccept(this, data);
Object value = node.jjtGetChild(1).jjtAccept(this, data);
return new Object[]{key, value};
}
/** {@inheritDoc} */
public Object visit(ASTMapLiteral node, Object data) {
int childCount = node.jjtGetNumChildren();
Map