Search in sources :

Example 1 with ASTArguments

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;
}
Also used : JexlNode(org.apache.commons.jexl3.parser.JexlNode) ASTArguments(org.apache.commons.jexl3.parser.ASTArguments) ASTIdentifierAccess(org.apache.commons.jexl3.parser.ASTIdentifierAccess)

Example 2 with ASTArguments

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);
}
Also used : ASTIdentifier(org.apache.commons.jexl3.parser.ASTIdentifier) ASTArguments(org.apache.commons.jexl3.parser.ASTArguments)

Example 3 with ASTArguments

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);
}
Also used : ASTIdentifier(org.apache.commons.jexl3.parser.ASTIdentifier) ASTArguments(org.apache.commons.jexl3.parser.ASTArguments)

Example 4 with ASTArguments

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);
}
Also used : JexlMethod(org.apache.commons.jexl3.introspection.JexlMethod) JexlException(org.apache.commons.jexl3.JexlException) ASTIdentifier(org.apache.commons.jexl3.parser.ASTIdentifier) ASTJexlScript(org.apache.commons.jexl3.parser.ASTJexlScript) JexlScript(org.apache.commons.jexl3.JexlScript) JexlPropertyGet(org.apache.commons.jexl3.introspection.JexlPropertyGet) JexlException(org.apache.commons.jexl3.JexlException) ASTIdentifierAccess(org.apache.commons.jexl3.parser.ASTIdentifierAccess)

Example 5 with ASTArguments

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;
}
Also used : JexlException(org.apache.commons.jexl3.JexlException) ASTAnnotation(org.apache.commons.jexl3.parser.ASTAnnotation) JexlNode(org.apache.commons.jexl3.parser.JexlNode) JexlArithmetic(org.apache.commons.jexl3.JexlArithmetic) ASTArguments(org.apache.commons.jexl3.parser.ASTArguments) JexlException(org.apache.commons.jexl3.JexlException)

Aggregations

ASTArguments (org.apache.commons.jexl3.parser.ASTArguments)5 ASTIdentifier (org.apache.commons.jexl3.parser.ASTIdentifier)4 JexlNode (org.apache.commons.jexl3.parser.JexlNode)3 JexlException (org.apache.commons.jexl3.JexlException)2 ASTIdentifierAccess (org.apache.commons.jexl3.parser.ASTIdentifierAccess)2 JexlArithmetic (org.apache.commons.jexl3.JexlArithmetic)1 JexlScript (org.apache.commons.jexl3.JexlScript)1 JexlMethod (org.apache.commons.jexl3.introspection.JexlMethod)1 JexlPropertyGet (org.apache.commons.jexl3.introspection.JexlPropertyGet)1 ASTAnnotation (org.apache.commons.jexl3.parser.ASTAnnotation)1 ASTFunctionNode (org.apache.commons.jexl3.parser.ASTFunctionNode)1 ASTJexlScript (org.apache.commons.jexl3.parser.ASTJexlScript)1 ASTNumberLiteral (org.apache.commons.jexl3.parser.ASTNumberLiteral)1