Search in sources :

Example 1 with JexlOperator

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

the class InterpreterBase method setAttribute.

/**
 * Sets an attribute of an object.
 *
 * @param object    to set the value to
 * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or key for a map
 * @param value     the value to assign to the object's attribute
 * @param node      the node that evaluated as the object
 */
protected void setAttribute(final Object object, final Object attribute, final Object value, final JexlNode node) {
    cancelCheck(node);
    final JexlOperator operator = node != null && node.jjtGetParent() instanceof ASTArrayAccess ? JexlOperator.ARRAY_SET : JexlOperator.PROPERTY_SET;
    final Object result = operators.tryOverload(node, operator, object, attribute, value);
    if (result != JexlEngine.TRY_FAILED) {
        return;
    }
    Exception xcause = null;
    try {
        // attempt to reuse last executor cached in volatile JexlNode.value
        if (node != null && cache) {
            final Object cached = node.jjtGetValue();
            if (cached instanceof JexlPropertySet) {
                final JexlPropertySet setter = (JexlPropertySet) cached;
                final Object eval = setter.tryInvoke(object, attribute, value);
                if (!setter.tryFailed(eval)) {
                    return;
                }
            }
        }
        final List<JexlUberspect.PropertyResolver> resolvers = uberspect.getResolvers(operator, object);
        JexlPropertySet vs = uberspect.getPropertySet(resolvers, object, attribute, value);
        // if we can't find an exact match, narrow the value argument and try again
        if (vs == null) {
            // replace all numbers with the smallest type that will fit
            final Object[] narrow = { value };
            if (arithmetic.narrowArguments(narrow)) {
                vs = uberspect.getPropertySet(resolvers, object, attribute, narrow[0]);
            }
        }
        if (vs != null) {
            // cache executor in volatile JexlNode.value
            vs.invoke(object, value);
            if (node != null && cache && vs.isCacheable()) {
                node.jjtSetValue(vs);
            }
            return;
        }
    } catch (final Exception xany) {
        xcause = xany;
    }
    // lets fail
    if (node == null) {
        // direct call
        final String error = "unable to set object property" + ", class: " + object.getClass().getName() + ", property: " + attribute + ", argument: " + value.getClass().getSimpleName();
        throw new UnsupportedOperationException(error, xcause);
    }
    final String attrStr = attribute != null ? attribute.toString() : null;
    unsolvableProperty(node, attrStr, true, xcause);
}
Also used : JexlPropertySet(org.apache.commons.jexl3.introspection.JexlPropertySet) JexlOperator(org.apache.commons.jexl3.JexlOperator) ASTArrayAccess(org.apache.commons.jexl3.parser.ASTArrayAccess) JexlException(org.apache.commons.jexl3.JexlException)

Example 2 with JexlOperator

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

the class Operators method tryOverload.

/**
 * Attempts to call an operator.
 * <p>
 * This takes care of finding and caching the operator method when appropriate
 * @param node     the syntactic node
 * @param operator the operator
 * @param args     the arguments
 * @return the result of the operator evaluation or TRY_FAILED
 */
protected Object tryOverload(final JexlNode node, final JexlOperator operator, final Object... args) {
    if (operators != null && operators.overloads(operator)) {
        final JexlArithmetic arithmetic = interpreter.arithmetic;
        final boolean cache = interpreter.cache;
        try {
            if (cache) {
                final Object cached = node.jjtGetValue();
                if (cached instanceof JexlMethod) {
                    final JexlMethod me = (JexlMethod) cached;
                    final Object eval = me.tryInvoke(operator.getMethodName(), arithmetic, args);
                    if (!me.tryFailed(eval)) {
                        return eval;
                    }
                }
            }
            final JexlMethod vm = operators.getOperator(operator, args);
            if (vm != null && !isArithmetic(vm)) {
                final Object result = vm.invoke(arithmetic, args);
                if (cache) {
                    node.jjtSetValue(vm);
                }
                return result;
            }
        } catch (final Exception xany) {
            return interpreter.operatorError(node, operator, xany);
        }
    }
    return JexlEngine.TRY_FAILED;
}
Also used : JexlMethod(org.apache.commons.jexl3.introspection.JexlMethod) JexlArithmetic(org.apache.commons.jexl3.JexlArithmetic) JexlException(org.apache.commons.jexl3.JexlException)

Example 3 with JexlOperator

use of org.apache.commons.jexl3.JexlOperator 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 4 with JexlOperator

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

the class InterpreterBase method getAttribute.

/**
 * Gets an attribute of an object.
 *
 * @param object    to retrieve value from
 * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or key for a map
 * @param node      the node that evaluated as the object
 * @return the attribute value
 */
protected Object getAttribute(final Object object, final Object attribute, final JexlNode node) {
    if (object == null) {
        throw new JexlException(node, "object is null");
    }
    cancelCheck(node);
    final JexlOperator operator = node != null && node.jjtGetParent() instanceof ASTArrayAccess ? JexlOperator.ARRAY_GET : JexlOperator.PROPERTY_GET;
    final Object result = operators.tryOverload(node, operator, object, attribute);
    if (result != JexlEngine.TRY_FAILED) {
        return result;
    }
    Exception xcause = null;
    try {
        // attempt to reuse last executor cached in volatile JexlNode.value
        if (node != null && cache) {
            final Object cached = node.jjtGetValue();
            if (cached instanceof JexlPropertyGet) {
                final JexlPropertyGet vg = (JexlPropertyGet) cached;
                final Object value = vg.tryInvoke(object, attribute);
                if (!vg.tryFailed(value)) {
                    return value;
                }
            }
        }
        // resolve that property
        final List<JexlUberspect.PropertyResolver> resolvers = uberspect.getResolvers(operator, object);
        final JexlPropertyGet vg = uberspect.getPropertyGet(resolvers, object, attribute);
        if (vg != null) {
            final Object value = vg.invoke(object);
            // cache executor in volatile JexlNode.value
            if (node != null && cache && vg.isCacheable()) {
                node.jjtSetValue(vg);
            }
            return value;
        }
    } catch (final Exception xany) {
        xcause = xany;
    }
    // lets fail
    if (node == null) {
        // direct call
        final String error = "unable to get object property" + ", class: " + object.getClass().getName() + ", property: " + attribute;
        throw new UnsupportedOperationException(error, xcause);
    }
    final boolean safe = (node instanceof ASTIdentifierAccess) && ((ASTIdentifierAccess) node).isSafe();
    if (safe) {
        return null;
    }
    final String attrStr = attribute != null ? attribute.toString() : null;
    return unsolvableProperty(node, attrStr, true, xcause);
}
Also used : JexlException(org.apache.commons.jexl3.JexlException) JexlOperator(org.apache.commons.jexl3.JexlOperator) JexlPropertyGet(org.apache.commons.jexl3.introspection.JexlPropertyGet) ASTArrayAccess(org.apache.commons.jexl3.parser.ASTArrayAccess) JexlException(org.apache.commons.jexl3.JexlException) ASTIdentifierAccess(org.apache.commons.jexl3.parser.ASTIdentifierAccess)

Example 5 with JexlOperator

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

the class Interpreter method executeAssign.

/**
 * Executes an assignment with an optional side-effect operator.
 * @param node     the node
 * @param assignop the assignment operator or null if simply assignment
 * @param data     the data
 * @return the left hand side
 */
protected Object executeAssign(final JexlNode node, final JexlOperator assignop, final Object data) {
    // CSOFF: MethodLength
    cancelCheck(node);
    // left contains the reference to assign to
    final JexlNode left = node.jjtGetChild(0);
    ASTIdentifier var = null;
    Object object = null;
    int symbol = -1;
    // check var decl with assign is ok
    if (left instanceof ASTIdentifier) {
        var = (ASTIdentifier) left;
        symbol = var.getSymbol();
        if (symbol >= 0 && options.isLexical()) {
            if (var instanceof ASTVar) {
                if (!defineVariable((ASTVar) var, block)) {
                    return redefinedVariable(var, var.getName());
                }
            } else if (options.isLexicalShade() && var.isShaded()) {
                return undefinedVariable(var, var.getName());
            }
        }
    }
    boolean antish = options.isAntish();
    // 0: determine initial object & property:
    final int last = left.jjtGetNumChildren() - 1;
    // right is the value expression to assign
    Object right = node.jjtGetChild(1).jjtAccept(this, data);
    // a (var?) v = ... expression
    if (var != null) {
        if (symbol >= 0) {
            // check we are not assigning a symbol itself
            if (last < 0) {
                if (assignop != null) {
                    final Object self = getVariable(frame, block, var);
                    right = operators.tryAssignOverload(node, assignop, self, right);
                    if (right == JexlOperator.ASSIGN) {
                        return self;
                    }
                }
                frame.set(symbol, right);
                // make the closure accessible to itself, ie capture the currently set variable after frame creation
                if (right instanceof Closure) {
                    ((Closure) right).setCaptured(symbol, right);
                }
                // 1
                return right;
            }
            object = getVariable(frame, block, var);
            // top level is a symbol, can not be an antish var
            antish = false;
        } else {
            // check we are not assigning direct global
            if (last < 0) {
                if (assignop != null) {
                    final Object self = context.get(var.getName());
                    right = operators.tryAssignOverload(node, assignop, self, right);
                    if (right == JexlOperator.ASSIGN) {
                        return self;
                    }
                }
                setContextVariable(node, var.getName(), right);
                // 2
                return right;
            }
            object = context.get(var.getName());
            // top level accesses object, can not be an antish var
            if (object != null) {
                antish = false;
            }
        }
    } else if (!(left instanceof ASTReference)) {
        throw new JexlException(left, "illegal assignment form 0");
    }
    // 1: follow children till penultimate, resolve dot/array
    JexlNode objectNode = null;
    StringBuilder ant = null;
    int v = 1;
    // start at 1 if symbol
    main: for (int c = symbol >= 0 ? 1 : 0; c < last; ++c) {
        objectNode = left.jjtGetChild(c);
        object = objectNode.jjtAccept(this, object);
        if (object != null) {
            // disallow mixing antish variable & bean with same root; avoid ambiguity
            antish = false;
        } else if (antish) {
            // initialize if first time
            if (ant == null) {
                final JexlNode first = left.jjtGetChild(0);
                final ASTIdentifier firstId = first instanceof ASTIdentifier ? (ASTIdentifier) first : null;
                if ((firstId == null) || (firstId.getSymbol() >= 0)) {
                    // ant remains null, object is null, stop solving
                    antish = false;
                    break main;
                }
                ant = new StringBuilder(firstId.getName());
            }
            // catch up to current child
            for (; v <= c; ++v) {
                final JexlNode child = left.jjtGetChild(v);
                final ASTIdentifierAccess aid = child instanceof ASTIdentifierAccess ? (ASTIdentifierAccess) child : null;
                // remain antish only if unsafe navigation
                if ((aid == null) || aid.isSafe() || aid.isExpression()) {
                    antish = false;
                    break main;
                }
                ant.append('.');
                ant.append(aid.getName());
            }
            // solve antish
            object = context.get(ant.toString());
        } else {
            throw new JexlException(objectNode, "illegal assignment form");
        }
    }
    // 2: last objectNode will perform assignement in all cases
    JexlNode propertyNode = left.jjtGetChild(last);
    final ASTIdentifierAccess propertyId = propertyNode instanceof ASTIdentifierAccess ? (ASTIdentifierAccess) propertyNode : null;
    final Object property;
    if (propertyId != null) {
        // deal with creating/assignining antish variable
        if (antish && ant != null && object == null && !propertyId.isSafe() && !propertyId.isExpression()) {
            if (last > 0) {
                ant.append('.');
            }
            ant.append(propertyId.getName());
            if (assignop != null) {
                final Object self = context.get(ant.toString());
                right = operators.tryAssignOverload(node, assignop, self, right);
                if (right == JexlOperator.ASSIGN) {
                    return self;
                }
            }
            setContextVariable(propertyNode, ant.toString(), right);
            // 3
            return right;
        }
        // property of an object ?
        property = evalIdentifier(propertyId);
    } else if (propertyNode instanceof ASTArrayAccess) {
        // can have multiple nodes - either an expression, integer literal or reference
        final int numChildren = propertyNode.jjtGetNumChildren() - 1;
        for (int i = 0; i < numChildren; i++) {
            final JexlNode nindex = propertyNode.jjtGetChild(i);
            final Object index = nindex.jjtAccept(this, null);
            object = getAttribute(object, index, nindex);
        }
        propertyNode = propertyNode.jjtGetChild(numChildren);
        property = propertyNode.jjtAccept(this, null);
    } else {
        throw new JexlException(objectNode, "illegal assignment form");
    }
    // we can not *have* a null object though.
    if (object == null) {
        // no object, we fail
        return unsolvableProperty(objectNode, "<null>.<?>", true, null);
    }
    // 3: one before last, assign
    if (assignop != null) {
        final Object self = getAttribute(object, property, propertyNode);
        right = operators.tryAssignOverload(node, assignop, self, right);
        if (right == JexlOperator.ASSIGN) {
            return self;
        }
    }
    setAttribute(object, property, right, propertyNode);
    // 4
    return right;
}
Also used : ASTVar(org.apache.commons.jexl3.parser.ASTVar) JexlException(org.apache.commons.jexl3.JexlException) JexlNode(org.apache.commons.jexl3.parser.JexlNode) ASTIdentifier(org.apache.commons.jexl3.parser.ASTIdentifier) ASTReference(org.apache.commons.jexl3.parser.ASTReference) ASTIdentifierAccess(org.apache.commons.jexl3.parser.ASTIdentifierAccess) ASTArrayAccess(org.apache.commons.jexl3.parser.ASTArrayAccess)

Aggregations

JexlException (org.apache.commons.jexl3.JexlException)5 JexlOperator (org.apache.commons.jexl3.JexlOperator)4 JexlArithmetic (org.apache.commons.jexl3.JexlArithmetic)3 JexlMethod (org.apache.commons.jexl3.introspection.JexlMethod)3 ASTArrayAccess (org.apache.commons.jexl3.parser.ASTArrayAccess)3 ASTIdentifierAccess (org.apache.commons.jexl3.parser.ASTIdentifierAccess)2 Method (java.lang.reflect.Method)1 JexlPropertyGet (org.apache.commons.jexl3.introspection.JexlPropertyGet)1 JexlPropertySet (org.apache.commons.jexl3.introspection.JexlPropertySet)1 ASTIdentifier (org.apache.commons.jexl3.parser.ASTIdentifier)1 ASTReference (org.apache.commons.jexl3.parser.ASTReference)1 ASTVar (org.apache.commons.jexl3.parser.ASTVar)1 JexlNode (org.apache.commons.jexl3.parser.JexlNode)1