use of org.apache.commons.jexl3.parser.ASTArguments in project commons-jexl by apache.
the class Interpreter method visit.
/**
* Execute a method call, ie syntactically written as name.call(...).
* @param node the actual method call node
* @param antish non null when name.call is an antish variable
* @param data the context
* @return the method call result
*/
private Object visit(final ASTMethodNode node, final Object antish, final Object data) {
Object object = antish;
// left contains the reference to the method
final JexlNode methodNode = node.jjtGetChild(0);
Object method;
// 1: determine object and method or functor
if (methodNode instanceof ASTIdentifierAccess) {
method = methodNode;
if (object == null) {
object = data;
if (object == null) {
// no object, we fail
return node.isSafeLhs(isSafe()) ? null : unsolvableMethod(methodNode, "<null>.<?>(...)");
}
} else {
// edge case of antish var used as functor
method = object;
}
} else {
method = methodNode.jjtAccept(this, data);
}
Object result = method;
for (int a = 1; a < node.jjtGetNumChildren(); ++a) {
if (result == null) {
// no method, we fail// variable unknown in context and not a local
return node.isSafeLhs(isSafe()) ? null : unsolvableMethod(methodNode, "<?>.<null>(...)");
}
final ASTArguments argNode = (ASTArguments) node.jjtGetChild(a);
result = call(node, object, result, argNode);
object = result;
}
return result;
}
use of org.apache.commons.jexl3.parser.ASTArguments in project commons-jexl by apache.
the class Interpreter method visit.
@Override
protected Object visit(final ASTFunctionNode node, final Object data) {
final ASTIdentifier functionNode = (ASTIdentifier) node.jjtGetChild(0);
final String nsid = functionNode.getNamespace();
final Object namespace = (nsid != null) ? resolveNamespace(nsid, node) : context;
final ASTArguments argNode = (ASTArguments) node.jjtGetChild(1);
return call(node, namespace, functionNode, argNode);
}
use of org.apache.commons.jexl3.parser.ASTArguments in project commons-jexl by apache.
the class TemplateInterpreter method visit.
/**
* Interprets a function node.
* print() and include() must be decoded by this interpreter since delegating to the Uberspect
* may be sandboxing the interpreter itself making it unable to call the function.
* @param node the function node
* @param data the data
* @return the function evaluation result.
*/
@Override
protected Object visit(final ASTFunctionNode node, Object data) {
final int argc = node.jjtGetNumChildren();
if (argc == 2) {
final ASTIdentifier functionNode = (ASTIdentifier) node.jjtGetChild(0);
if ("jexl".equals(functionNode.getNamespace())) {
final String functionName = functionNode.getName();
final ASTArguments argNode = (ASTArguments) node.jjtGetChild(1);
if ("print".equals(functionName)) {
// evaluate the arguments
Object[] argv = visit(argNode, null);
if (argv != null && argv.length > 0 && argv[0] instanceof Number) {
print(((Number) argv[0]).intValue());
return null;
}
}
if ("include".equals(functionName)) {
// evaluate the arguments
Object[] argv = visit(argNode, null);
if (argv != null && argv.length > 0) {
if (argv[0] instanceof TemplateScript) {
TemplateScript script = (TemplateScript) argv[0];
if (argv.length > 1) {
argv = Arrays.copyOfRange(argv, 1, argv.length);
} else {
argv = null;
}
include(script, argv);
return null;
}
}
}
// fail safe
throw new JxltEngine.Exception(node.jexlInfo(), "no callable template function " + functionName, null);
}
}
return super.visit(node, data);
}
use of org.apache.commons.jexl3.parser.ASTArguments in project commons-jexl by apache.
the class Interpreter method call.
/**
* Calls a method (or function).
* <p>
* Method resolution is a follows:
* 1 - attempt to find a method in the target passed as parameter;
* 2 - if this fails, seeks a JexlScript or JexlMethod or a duck-callable* as a property of that target;
* 3 - if this fails, narrow the arguments and try again 1
* 4 - if this fails, seeks a context or arithmetic method with the proper name taking the target as first argument;
* </p>
* *duck-callable: an object where a "call" function exists
*
* @param node the method node
* @param target the target of the method, what it should be invoked upon
* @param funcNode the object carrying the method or function or the method identifier
* @param argNode the node carrying the arguments
* @return the result of the method invocation
*/
protected Object call(final JexlNode node, final Object target, final Object funcNode, final ASTArguments argNode) {
cancelCheck(node);
// evaluate the arguments
final Object[] argv = visit(argNode, null);
final String methodName;
boolean cacheable = cache;
boolean isavar = false;
Object functor = funcNode;
// get the method name if identifier
if (functor instanceof ASTIdentifier) {
// function call, target is context or namespace (if there was one)
final ASTIdentifier methodIdentifier = (ASTIdentifier) functor;
final int symbol = methodIdentifier.getSymbol();
methodName = methodIdentifier.getName();
functor = null;
// is it a global or local variable ?
if (target == context) {
if (frame != null && frame.has(symbol)) {
functor = frame.get(symbol);
isavar = functor != null;
} else if (context.has(methodName)) {
functor = context.get(methodName);
isavar = functor != null;
}
// name is a variable, can't be cached
cacheable &= !isavar;
}
} else if (functor instanceof ASTIdentifierAccess) {
// a method call on target
methodName = ((ASTIdentifierAccess) functor).getName();
functor = null;
cacheable = true;
} else if (functor != null) {
// ...(x)(y)
methodName = null;
cacheable = false;
} else if (!node.isSafeLhs(isSafe())) {
return unsolvableMethod(node, "?(...)");
} else {
// safe lhs
return null;
}
// solving the call site
final CallDispatcher call = new CallDispatcher(node, cacheable);
try {
// do we have a cached version method/function name ?
final Object eval = call.tryEval(target, methodName, argv);
if (JexlEngine.TRY_FAILED != eval) {
return eval;
}
boolean functorp = false;
boolean narrow = false;
// pseudo loop to try acquiring methods without and with argument narrowing
while (true) {
call.narrow = narrow;
// direct function or method call
if (functor == null || functorp) {
// try a method or function from context
if (call.isTargetMethod(target, methodName, argv)) {
return call.eval(methodName);
}
if (target == context) {
// solve 'null' namespace
final Object namespace = resolveNamespace(null, node);
if (namespace != null && namespace != context && call.isTargetMethod(namespace, methodName, argv)) {
return call.eval(methodName);
}
// 10 lines above...; solve as an arithmetic function
if (call.isArithmeticMethod(methodName, argv)) {
return call.eval(methodName);
}
// could not find a method, try as a property of a non-context target (performed once)
} else {
// try prepending target to arguments and look for
// applicable method in context...
final Object[] pargv = functionArguments(target, narrow, argv);
if (call.isContextMethod(methodName, pargv)) {
return call.eval(methodName);
}
// ...or arithmetic
if (call.isArithmeticMethod(methodName, pargv)) {
return call.eval(methodName);
}
// the method may also be a functor stored in a property of the target
if (!narrow) {
final JexlPropertyGet get = uberspect.getPropertyGet(target, methodName);
if (get != null) {
functor = get.tryInvoke(target, methodName);
functorp = functor != null;
}
}
}
}
// or when a var/symbol or antish var is used as a "function" name
if (functor != null) {
// lambda, script or jexl method will do
if (functor instanceof JexlScript) {
return ((JexlScript) functor).execute(context, argv);
}
if (functor instanceof JexlMethod) {
return ((JexlMethod) functor).invoke(target, argv);
}
final String mCALL = "call";
// may be a generic callable, try a 'call' method
if (call.isTargetMethod(functor, mCALL, argv)) {
return call.eval(mCALL);
}
// functor is a var, may be method is a global one ?
if (isavar && target == context) {
if (call.isContextMethod(methodName, argv)) {
return call.eval(methodName);
}
if (call.isArithmeticMethod(methodName, argv)) {
return call.eval(methodName);
}
}
// try prepending functor to arguments and look for
// context or arithmetic function called 'call'
final Object[] pargv = functionArguments(functor, narrow, argv);
if (call.isContextMethod(mCALL, pargv)) {
return call.eval(mCALL);
}
if (call.isArithmeticMethod(mCALL, pargv)) {
return call.eval(mCALL);
}
}
// attempt to narrow the parameters and if this succeeds, try again in next loop
if (narrow || !arithmetic.narrowArguments(argv)) {
break;
}
narrow = true;
// continue;
}
} catch (JexlException.Method xmethod) {
// ignore and handle at end; treat as an inner discover that fails
} catch (final JexlException.TryFailed xany) {
throw invocationException(node, methodName, xany);
} catch (final JexlException xthru) {
throw xthru;
} catch (final Exception xany) {
throw invocationException(node, methodName, xany);
}
// we have either evaluated and returned or no method was found
return node.isSafeLhs(isSafe()) ? null : unsolvableMethod(node, methodName, argv);
}
use of org.apache.commons.jexl3.parser.ASTArguments in project commons-jexl by apache.
the class Interpreter method processAnnotation.
/**
* Processes an annotated statement.
* @param stmt the statement
* @param index the index of the current annotation being processed
* @param data the contextual data
* @return the result of the statement block evaluation
*/
protected Object processAnnotation(final ASTAnnotatedStatement stmt, final int index, final Object data) {
// are we evaluating the block ?
final int last = stmt.jjtGetNumChildren() - 1;
if (index == last) {
final JexlNode cblock = stmt.jjtGetChild(last);
// if the context has changed, might need a new interpreter
final JexlArithmetic jexla = arithmetic.options(context);
if (jexla == arithmetic) {
return cblock.jjtAccept(Interpreter.this, data);
}
if (!arithmetic.getClass().equals(jexla.getClass()) && logger.isWarnEnabled()) {
logger.warn("expected arithmetic to be " + arithmetic.getClass().getSimpleName() + ", got " + jexla.getClass().getSimpleName());
}
final Interpreter ii = new Interpreter(Interpreter.this, jexla);
final Object r = cblock.jjtAccept(ii, data);
if (ii.isCancelled()) {
Interpreter.this.cancel();
}
return r;
}
// tracking whether we processed the annotation
final AnnotatedCall jstmt = new AnnotatedCall(stmt, index + 1, data);
// the annotation node and name
final ASTAnnotation anode = (ASTAnnotation) stmt.jjtGetChild(index);
final String aname = anode.getName();
// evaluate the arguments
final Object[] argv = anode.jjtGetNumChildren() > 0 ? visit((ASTArguments) anode.jjtGetChild(0), null) : null;
// wrap the future, will recurse through annotation processor
Object result;
try {
result = processAnnotation(aname, argv, jstmt);
// not processing an annotation is an error
if (!jstmt.isProcessed()) {
return annotationError(anode, aname, null);
}
} catch (final JexlException xany) {
throw xany;
} catch (final Exception xany) {
return annotationError(anode, aname, xany);
}
// the caller may return a return, break or continue
if (result instanceof JexlException) {
throw (JexlException) result;
}
return result;
}
Aggregations