Search in sources :

Example 6 with JexlMethod

use of org.apache.commons.jexl3.introspection.JexlMethod in project commons-jexl by apache.

the class Operators method tryAssignOverload.

/**
 * Evaluates an assign operator.
 * <p>
 * This takes care of finding and caching the operator method when appropriate.
 * If an overloads returns Operator.ASSIGN, it means the side-effect is complete.
 * Otherwise, a += b &lt;=&gt; a = a + b
 * </p>
 * @param node     the syntactic node
 * @param operator the operator
 * @param args     the arguments, the first one being the target of assignment
 * @return JexlOperator.ASSIGN if operation assignment has been performed,
 *         JexlEngine.TRY_FAILED if no operation was performed,
 *         the value to use as the side effect argument otherwise
 */
protected Object tryAssignOverload(final JexlNode node, final JexlOperator operator, final Object... args) {
    final JexlArithmetic arithmetic = interpreter.arithmetic;
    if (args.length != operator.getArity()) {
        return JexlEngine.TRY_FAILED;
    }
    // try to call overload with side effect
    Object result = tryOverload(node, operator, args);
    if (result != JexlEngine.TRY_FAILED) {
        return result;
    }
    // call base operator
    final JexlOperator base = operator.getBaseOperator();
    if (base == null) {
        throw new IllegalArgumentException("must be called with a side-effect operator");
    }
    if (operators != null && operators.overloads(base)) {
        // in case there is an overload on the base operator
        try {
            final JexlMethod vm = operators.getOperator(base, args);
            if (vm != null) {
                result = vm.invoke(arithmetic, args);
                if (result != JexlEngine.TRY_FAILED) {
                    return result;
                }
            }
        } catch (final Exception xany) {
            interpreter.operatorError(node, base, xany);
        }
    }
    // base eval
    try {
        switch(operator) {
            case SELF_ADD:
                return arithmetic.add(args[0], args[1]);
            case SELF_SUBTRACT:
                return arithmetic.subtract(args[0], args[1]);
            case SELF_MULTIPLY:
                return arithmetic.multiply(args[0], args[1]);
            case SELF_DIVIDE:
                return arithmetic.divide(args[0], args[1]);
            case SELF_MOD:
                return arithmetic.mod(args[0], args[1]);
            case SELF_AND:
                return arithmetic.and(args[0], args[1]);
            case SELF_OR:
                return arithmetic.or(args[0], args[1]);
            case SELF_XOR:
                return arithmetic.xor(args[0], args[1]);
            default:
                // unexpected, new operator added?
                throw new UnsupportedOperationException(operator.getOperatorSymbol());
        }
    } catch (final Exception xany) {
        interpreter.operatorError(node, base, xany);
    }
    return JexlEngine.TRY_FAILED;
}
Also used : JexlMethod(org.apache.commons.jexl3.introspection.JexlMethod) JexlOperator(org.apache.commons.jexl3.JexlOperator) JexlArithmetic(org.apache.commons.jexl3.JexlArithmetic) JexlException(org.apache.commons.jexl3.JexlException)

Example 7 with JexlMethod

use of org.apache.commons.jexl3.introspection.JexlMethod in project commons-jexl by apache.

the class ReferenceUberspect method discoverFind.

/**
 * Discovers a an optional getter.
 * <p>The method to be found should be named "{find}{P,p}property and return an Optional&lt;?&gt;.</p>
 *
 * @param is the uberspector
 * @param clazz the class to find the get method from
 * @param property the property name to find
 * @return the executor if found, null otherwise
 */
private static JexlPropertyGet discoverFind(final JexlUberspect is, final Class<?> clazz, final String property) {
    if (property == null || property.isEmpty()) {
        return null;
    }
    // this is gross and linear, but it keeps it straightforward.
    JexlMethod method;
    // "find".length() == 4
    final int start = 4;
    // start with get<Property>
    final StringBuilder sb = new StringBuilder("find");
    sb.append(property);
    // uppercase nth char
    final char c = sb.charAt(start);
    sb.setCharAt(start, Character.toUpperCase(c));
    method = is.getMethod(clazz, sb.toString(), EMPTY_PARAMS);
    // lowercase nth char
    if (method == null) {
        sb.setCharAt(start, Character.toLowerCase(c));
        method = is.getMethod(clazz, sb.toString(), EMPTY_PARAMS);
    }
    if (method != null && Optional.class.equals(method.getReturnType())) {
        final JexlMethod getter = method;
        final String name = sb.toString();
        return new JexlPropertyGet() {

            @Override
            public Object invoke(Object obj) throws Exception {
                return getter.invoke(obj);
            }

            @Override
            public Object tryInvoke(Object obj, Object key) throws JexlException.TryFailed {
                return !Objects.equals(property, key) ? JexlEngine.TRY_FAILED : getter.tryInvoke(name, obj);
            }

            @Override
            public boolean tryFailed(Object rval) {
                return rval == JexlEngine.TRY_FAILED;
            }

            @Override
            public boolean isCacheable() {
                return getter.isCacheable();
            }
        };
    }
    return null;
}
Also used : JexlMethod(org.apache.commons.jexl3.introspection.JexlMethod) Optional(java.util.Optional) JexlException(org.apache.commons.jexl3.JexlException) JexlPropertyGet(org.apache.commons.jexl3.introspection.JexlPropertyGet)

Example 8 with JexlMethod

use of org.apache.commons.jexl3.introspection.JexlMethod in project commons-jexl by apache.

the class MethodTest method testTryFailed.

@Test
public void testTryFailed() throws Exception {
    // JEXL-257
    final Functor func = new Functor();
    final JexlContext ctxt = new MapContext();
    ctxt.set("func", func);
    Object result;
    final JexlUberspect uber = JEXL.getUberspect();
    // tryInvoke
    final JexlMethod method = uber.getMethod(func, "over", "foo", 42);
    Assert.assertNotNull(method);
    // tryInvoke succeeds
    result = method.tryInvoke("over", func, "foo", 42);
    Assert.assertEquals("foo + 42", result);
    // tryInvoke fails
    func.setKill(true);
    try {
        /*result = */
        method.tryInvoke("over", func, "foo", 42);
        Assert.fail("should throw TryFailed");
    } catch (final JexlException.TryFailed xfail) {
        Assert.assertEquals(UnsupportedOperationException.class, xfail.getCause().getClass());
    }
    func.setKill(false);
    final JexlPropertySet setter = uber.getPropertySet(func, "under", "42");
    result = setter.tryInvoke(func, "under", "42");
    Assert.assertFalse(setter.tryFailed(result));
    Assert.assertEquals("42", result);
    final JexlPropertyGet getter = uber.getPropertyGet(func, "under");
    result = getter.tryInvoke(func, "under");
    Assert.assertFalse(getter.tryFailed(result));
    Assert.assertEquals("42", result);
    func.setKill(true);
    try {
        /*result = */
        setter.tryInvoke(func, "under", "42");
        Assert.fail("should throw TryFailed");
    } catch (final JexlException.TryFailed xfail) {
        Assert.assertEquals(UnsupportedOperationException.class, xfail.getCause().getClass());
    }
    func.setKill(false);
    result = setter.tryInvoke(func, "under", "-42");
    Assert.assertEquals("-42", result);
    func.setKill(true);
    try {
        /*result = */
        getter.tryInvoke(func, "under");
        Assert.fail("should throw TryFailed");
    } catch (final JexlException.TryFailed xfail) {
        Assert.assertEquals(UnsupportedOperationException.class, xfail.getCause().getClass());
    }
    func.setKill(false);
    result = getter.tryInvoke(func, "under");
    Assert.assertFalse(getter.tryFailed(result));
    Assert.assertEquals("-42", result);
}
Also used : JexlPropertySet(org.apache.commons.jexl3.introspection.JexlPropertySet) JexlMethod(org.apache.commons.jexl3.introspection.JexlMethod) JexlUberspect(org.apache.commons.jexl3.introspection.JexlUberspect) JexlPropertyGet(org.apache.commons.jexl3.introspection.JexlPropertyGet) Test(org.junit.Test)

Example 9 with JexlMethod

use of org.apache.commons.jexl3.introspection.JexlMethod 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 10 with JexlMethod

use of org.apache.commons.jexl3.introspection.JexlMethod in project commons-jexl by apache.

the class Interpreter method visit.

@Override
protected Object visit(final ASTUnaryMinusNode node, final Object data) {
    // use cached value if literal
    final Object value = node.jjtGetValue();
    if (value != null && !(value instanceof JexlMethod)) {
        return value;
    }
    final JexlNode valNode = node.jjtGetChild(0);
    final Object val = valNode.jjtAccept(this, data);
    try {
        final Object result = operators.tryOverload(node, JexlOperator.NEGATE, val);
        if (result != JexlEngine.TRY_FAILED) {
            return result;
        }
        Object number = arithmetic.negate(val);
        // cache if number literal and negate is idempotent
        if (number instanceof Number && valNode instanceof ASTNumberLiteral) {
            number = arithmetic.narrowNumber((Number) number, ((ASTNumberLiteral) valNode).getLiteralClass());
            if (arithmetic.isNegateStable()) {
                node.jjtSetValue(number);
            }
        }
        return number;
    } catch (final ArithmeticException xrt) {
        throw new JexlException(valNode, "- error", xrt);
    }
}
Also used : JexlMethod(org.apache.commons.jexl3.introspection.JexlMethod) JexlException(org.apache.commons.jexl3.JexlException) JexlNode(org.apache.commons.jexl3.parser.JexlNode) ASTNumberLiteral(org.apache.commons.jexl3.parser.ASTNumberLiteral)

Aggregations

JexlMethod (org.apache.commons.jexl3.introspection.JexlMethod)18 JexlException (org.apache.commons.jexl3.JexlException)14 JexlArithmetic (org.apache.commons.jexl3.JexlArithmetic)7 JexlUberspect (org.apache.commons.jexl3.introspection.JexlUberspect)7 Test (org.junit.Test)4 JexlPropertyGet (org.apache.commons.jexl3.introspection.JexlPropertyGet)3 JexlInfo (org.apache.commons.jexl3.JexlInfo)2 ASTNumberLiteral (org.apache.commons.jexl3.parser.ASTNumberLiteral)2 JexlNode (org.apache.commons.jexl3.parser.JexlNode)2 HashMap (java.util.HashMap)1 Map (java.util.Map)1 Optional (java.util.Optional)1 JexlOperator (org.apache.commons.jexl3.JexlOperator)1 JexlScript (org.apache.commons.jexl3.JexlScript)1 JexlPropertySet (org.apache.commons.jexl3.introspection.JexlPropertySet)1 ASTIdentifier (org.apache.commons.jexl3.parser.ASTIdentifier)1 ASTIdentifierAccess (org.apache.commons.jexl3.parser.ASTIdentifierAccess)1 ASTJexlScript (org.apache.commons.jexl3.parser.ASTJexlScript)1