protected Object executeAssign()

in src/main/java/org/apache/commons/jexl3/internal/Interpreter.java [510:692]


    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);
        final ASTIdentifier variable;
        Object object = null;
        final int symbol;
        // check var decl with assign is ok
        if (left instanceof ASTIdentifier) {
            variable = (ASTIdentifier) left;
            symbol = variable.getSymbol();
            if (symbol >= 0) {
                if  (variable.isLexical() || options.isLexical()) {
                    if (variable instanceof ASTVar) {
                        if (!defineVariable((ASTVar) variable, block)) {
                            return redefinedVariable(variable, variable.getName());
                        }
                    } else if (variable.isShaded() && (variable.isLexical() || options.isLexicalShade())) {
                        return undefinedVariable(variable, variable.getName());
                    }
                }
                if (variable.isCaptured() && options.isConstCapture()) {
                    return constVariable(variable, variable.getName());
                }
            }
        } else {
            variable = null;
            symbol = -1;
        }
        boolean antish = options.isAntish();
        // 0: determine initial object & property:
        final int last = left.jjtGetNumChildren() - 1;
        // right is the value expression to assign
       final  Object right = node.jjtGetNumChildren() < 2? null: node.jjtGetChild(1).jjtAccept(this, data);
        // actual value to return, right in most cases
        Object actual = right;
        // a (var?) v = ... expression
        if (variable != null) {
            if (symbol >= 0) {
                // check we are not assigning a symbol itself
                if (last < 0) {
                    if (assignop == null) {
                        // make the closure accessible to itself, ie capture the currently set variable after frame creation
                        if (right instanceof Closure) {
                            final Closure closure = (Closure) right;
                            // the variable scope must be the parent of the lambdas
                            closure.captureSelfIfRecursive(frame, symbol);
                        }
                        frame.set(symbol, right);
                    } else {
                        // go through potential overload
                        final Object self = getVariable(frame, block, variable);
                        final Consumer<Object> f = r -> frame.set(symbol, r);
                        actual = operators.tryAssignOverload(node, assignop, f, self, right);
                    }
                    return actual; // 1
                }
                object = getVariable(frame, block, variable);
                // top level is a symbol, cannot be an antish var
                antish = false;
            } else {
                // check we are not assigning direct global
                final String name = variable.getName();
                if (last < 0) {
                    if (assignop == null) {
                        setContextVariable(node, name, right);
                    } else {
                        // go through potential overload
                        final Object self = context.get(name);
                        final Consumer<Object> f = r ->  setContextVariable(node, name, r);
                        actual = operators.tryAssignOverload(node, assignop, f, self, right);
                    }
                    return actual; // 2
                }
                object = context.get(name);
                // top level accesses object, cannot 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 assignment 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/assigning antish variable
            if (antish && ant != null && object == null && !propertyId.isSafe() && !propertyId.isExpression()) {
                ant.append('.');
                ant.append(propertyId.getName());
                final String name = ant.toString();
                if (assignop == null) {
                    setContextVariable(propertyNode, name, right);
                } else {
                    final Object self = context.get(ant.toString());
                    final JexlNode pnode = propertyNode;
                    final Consumer<Object> assign = r -> setContextVariable(pnode, name, r);
                    actual = operators.tryAssignOverload(node, assignop, assign, self, right);
                }
                return actual; // 3
            }
            // 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 may have a null property as in map[null], no check needed.
        // we cannot *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) {
            setAttribute(object, property, right, propertyNode);
        } else {
            final Object self = getAttribute(object, property, propertyNode);
            final Object o = object;
            final JexlNode n = propertyNode;
            final Consumer<Object> assign = r ->  setAttribute(o, property, r, n);
            actual = operators.tryAssignOverload(node, assignop, assign, self, right);
        }
        return actual;
    }