ASTNode.java :  » Scripting » mvel » org » mvel » Java Open Source

Java Open Source » Scripting » mvel 
mvel » org » mvel » ASTNode.java
/**
 * MVEL (The MVFLEX Expression Language)
 *
 * Copyright (C) 2007 Christopher Brock, MVFLEX/Valhalla Project and the Codehaus
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.mvel;

import static org.mvel.PropertyAccessor.get;
import org.mvel.integration.VariableResolverFactory;
import org.mvel.optimizers.AccessorOptimizer;
import org.mvel.optimizers.OptimizationNotSupported;
import static org.mvel.optimizers.OptimizerFactory.*;
import static org.mvel.util.ArrayTools.findFirst;
import static org.mvel.util.ParseTools.handleEscapeSequence;
import static org.mvel.util.PropertyTools.handleNumericConversion;
import static org.mvel.util.PropertyTools.isNumber;
import org.mvel.util.ThisLiteral;

import java.io.Serializable;
import static java.lang.Class.forName;
import static java.lang.System.arraycopy;
import java.lang.reflect.Method;

public class ASTNode implements Cloneable, Serializable {
    public static final int LITERAL = 1;
    public static final int DEEP_PROPERTY = 1 << 1;
    public static final int OPERATOR = 1 << 2;
    public static final int IDENTIFIER = 1 << 3;
    public static final int COMPILE_IMMEDIATE = 1 << 4;
    public static final int NUMERIC = 1 << 5;
    public static final int NEGATION = 1 << 6;
    public static final int INVERT = 1 << 8;
    public static final int FOLD = 1 << 9;
    public static final int METHOD = 1 << 10;
    public static final int ASSIGN = 1 << 11;
    public static final int LOOKAHEAD = 1 << 12;
    public static final int COLLECTION = 1 << 13;
    public static final int THISREF = 1 << 14;
    public static final int INLINE_COLLECTION = 1 << 15;
    public static final int STR_LITERAL = 1 << 16;
    public static final int BLOCK = 1 << 17;
    public static final int BLOCK_IF = 1 << 18;
    public static final int BLOCK_FOREACH = 1 << 19;
    public static final int BLOCK_WITH = 1 << 20;
    public static final int TYPED = 1 << 21;
    public static final int RETURN = 1 << 22;
    public static final int INTEGER32 = 1 << 23;

    protected int firstUnion;
    protected int endOfName;

    protected int fields = 0;

    protected Class egressType;
    protected char[] name;
    protected String nameCache;

    protected Object literal;
    protected Accessor accessor;

    protected int cursorPosition;
    public ASTNode nextASTNode;
    protected boolean discard;

    private int intRegister;

    public ASTNode() {
    }

    public ASTNode(char[] expr, int start, int end, int fields) {
        this.cursorPosition = start;
        this.fields = fields;


        char[] name = new char[end - start];
        arraycopy(expr, start, name, 0, end - start);
        setName(name);
    }

    public ASTNode(char[] expr, int fields) {
        this.fields = fields;
        this.name = expr;
    }

    public ASTNode(int fields, Object literalValue) {
        this.fields = fields;
        this.literal = literalValue;
    }


    protected String getAbsoluteRootElement() {
        if ((fields & (DEEP_PROPERTY | COLLECTION)) != 0) {
            return new String(name, 0, getAbsoluteFirstPart());
        }
        return null;
    }

    public Class getEgressType() {
        return egressType;
    }

    public void setEgressType(Class egressType) {
        this.egressType = egressType;
    }

    protected String getAbsoluteRemainder() {
        return (fields & COLLECTION) != 0 ? new String(name, endOfName, name.length - endOfName)
                : ((fields & DEEP_PROPERTY) != 0 ? new String(name, firstUnion + 1, name.length - firstUnion - 1) : null);
    }

    public char[] getNameAsArray() {
        return name;
    }

    private int getAbsoluteFirstPart() {
        if ((fields & COLLECTION) != 0) {
            if (firstUnion < 0 || endOfName < firstUnion) return endOfName;
            else return firstUnion;
        }
        else if ((fields & DEEP_PROPERTY) != 0) {
            return firstUnion;
        }
        else {
            return -1;
        }

    }

    public String getAbsoluteName() {
        if ((fields & (COLLECTION | DEEP_PROPERTY)) != 0) {
            return new String(name, 0, getAbsoluteFirstPart());
        }
        else {
            return getName();
        }
    }

    public String getName() {
        if (nameCache != null) return nameCache;
        else if (name != null) return nameCache = new String(name);
        return "";
    }

    public Object getLiteralValue() {
        return literal;
    }

    public void setLiteralValue(Object literal) {
        this.literal = literal;
    }

    public Object getReducedValueAccelerated(Object ctx, Object thisValue, VariableResolverFactory factory) {
        if ((fields & (LITERAL)) != 0) {
//            if ((fields & THISREF) != 0)
//                return thisValue;
//            else
                return literal;
        }
        try {
            return valRet(accessor.getValue(ctx, thisValue, factory));
        }
        catch (NullPointerException e) {
            //todo: FIX JIT, so we don't have to force safe reflective mode.
            AccessorOptimizer optimizer;
            Object retVal = null;

            if ((fields & FOLD) != 0) {
                optimizer = getAccessorCompiler(SAFE_REFLECTIVE);
                accessor = optimizer.optimizeFold(name, ctx, thisValue, factory);
                retVal = accessor.getValue(ctx, thisValue, factory);
            }
            else {
                try {
                    accessor = (optimizer = getThreadAccessorOptimizer()).optimizeAccessor(name, ctx, thisValue, factory, true);
                }
                catch (OptimizationNotSupported ne) {
                    accessor = (optimizer = getAccessorCompiler(SAFE_REFLECTIVE)).optimizeAccessor(name, ctx, thisValue, factory, true);
                }
            }

            if (accessor == null)
                throw new OptimizationFailure("failed optimization", e);

            if (retVal == null) {
                retVal = optimizer.getResultOptPass();
            }

            if (egressType == null) {
                egressType = optimizer.getEgressType();
            }

            return valRet(retVal);
        }
    }

    public Object getReducedValue(Object ctx, Object thisValue, VariableResolverFactory factory) {
        String s;
        if ((fields & (LITERAL)) != 0) {
            if ((fields & THISREF) != 0) {
                return thisValue;
            }
            else {
                return literal;
            }
        }
        else if ((fields & FOLD) != 0) {
            if (accessor == null) {
                AccessorOptimizer optimizer = getAccessorCompiler(SAFE_REFLECTIVE);
                accessor = optimizer.optimizeFold(name, ctx, thisValue, factory);

                return optimizer.getResultOptPass();
            }
        }

        if ((fields & DEEP_PROPERTY) != 0) {
            /**
             * The token is a DEEP PROPERTY (meaning it contains unions) in which case we need to traverse an object
             * graph.
             */
            if (AbstractParser.LITERALS.containsKey(s = getAbsoluteRootElement())) {
                /**
                 * The root of the DEEP PROPERTY is a literal.
                 */
                Object literal = AbstractParser.LITERALS.get(s);
                if (literal == ThisLiteral.class) literal = thisValue;

                return valRet(get(getAbsoluteRemainder(), literal, factory, thisValue));
            }
            else if (factory != null && factory.isResolveable(s)) {
                /**
                 * The root of the DEEP PROPERTY is a local or global var.
                 */
                return valRet(get(name, ctx, factory, thisValue));

            }
            else if (ctx != null) {
                /**
                 * We didn't resolve the root, yet, so we assume that if we have a VROOT then the property must be
                 * accessible as a field of the VROOT.
                 */

                try {
                    return valRet(get(name, ctx, factory, thisValue));
                }
                catch (PropertyAccessException e) {
                    /**
                     * No luck. Make a last-ditch effort to resolve this as a static-class reference.
                     */
                    Object sa = tryStaticAccess(ctx, factory);
                    if (sa == null) throw e;

                    /**
                     * Since this clearly is a class literal, we change the nature of theis node to
                     * make it a literal to prevent re-evaluation.
                     */
                    literal = valRet(sa);
                    fields |= LITERAL;

                    return literal;
                }
            }
        }
        else {
            if (factory != null && factory.isResolveable(s = getAbsoluteName())) {
                /**
                 * The token is a local or global var.
                 */

                if (isCollection()) {
                    return valRet(get(new String(name, endOfName, name.length - endOfName),
                            factory.getVariableResolver(s).getValue(), factory, thisValue));
                }

                return valRet(factory.getVariableResolver(s).getValue());
            }
            else if (ctx != null) {
                /**
                 * Check to see if the var exists in the VROOT.
                 */
                try {
                    return valRet(get(name, ctx, factory, thisValue));
                }
                catch (RuntimeException e) {
                    e.printStackTrace();
                    throw new UnresolveablePropertyException(this);
                }
            }
            else {
                if (isOperator()) {
                    throw new CompileException("incomplete statement");
                }
                else {
                    int mBegin = findFirst('(', name);
                    if (mBegin != -1) {
                        if (factory.isResolveable(s = new String(name, 0, mBegin))) {
                            Method m = (Method) factory.getVariableResolver(s).getValue();

                            return valRet(get(m.getName() + new String(name, mBegin, name.length - mBegin),
                                    m.getDeclaringClass(), factory, thisValue));
                        }
                    }
                }

                throw new UnresolveablePropertyException(this);
            }
        }


        Object sa = tryStaticAccess(ctx, factory);
        if (sa == null) throw new UnresolveablePropertyException(this);
        return valRet(sa);

    }


    protected Object valRet(final Object value) {
        if ((fields & NEGATION) != 0) {
            try {
                return !((Boolean) value);
            }
            catch (Exception e) {
                throw new CompileException("illegal negation of non-boolean value");
            }
        }
        else if ((fields & INVERT) != 0) {
            try {
                return ~((Integer) value);
            }
            catch (Exception e) {
                throw new CompileException("bitwise (~) operator can only be applied to integers");
            }
        }


        return value;
    }

    protected Object tryStaticAccess(Object thisRef, VariableResolverFactory factory) {
        try {
            /**
             * Try to resolve this *smartly* as a static class reference.
             *
             * This starts at the end of the token and starts to step backwards to figure out whether
             * or not this may be a static class reference.  We search for method calls simply by
             * inspecting for ()'s.  The first union area we come to where no brackets are present is our
             * test-point for a class reference.  If we find a class, we pass the reference to the
             * property accessor along  with trailing methods (if any).
             *
             */
            boolean meth = false;
            int depth = 0;
            int last = name.length;
            for (int i = last - 1; i > 0; i--) {
                switch (name[i]) {
                    case'.':
                        if (!meth) {
                            try {
                                return get(new String(name, last, name.length - last), forName(new String(name, 0, last)), factory, thisRef);
                            }
                            catch (ClassNotFoundException e) {
                                return get(new String(name, i + 1, name.length - i - 1), forName(new String(name, 0, i)), factory, thisRef);
                            }
                        }
                        meth = false;
                        last = i;
                        break;
                    case')':
                        if (depth++ == 0)
                            meth = true;
                        break;
                    case'(':
                        depth--;
                        break;
                }
            }
        }
        catch (Exception cnfe) {
            // do nothing.
        }

        return null;
    }


    @SuppressWarnings({"SuspiciousMethodCalls"})
    protected void setName(char[] name) {
        if ((fields & STR_LITERAL) != 0) {
            fields |= LITERAL;

            int escapes = 0;
            for (int i = 0; i < name.length; i++) {
                if (name[i] == '\\') {
                    name[i++] = 0;
                    name[i] = handleEscapeSequence(name[i]);
                    escapes++;
                }
            }

            char[] processedEscapeString = new char[name.length - escapes];
            int cursor = 0;
            for (char aName : name) {
                if (aName == 0) {
                    continue;
                }
                processedEscapeString[cursor++] = aName;
            }

            this.literal = new String(this.name = processedEscapeString);

        }
        else {
            this.literal = new String(this.name = name);
        }

        if ((fields & (LITERAL)) != 0) {
            //    return;
        }
        else if (AbstractParser.LITERALS.containsKey(literal)) {
            fields |= LITERAL | IDENTIFIER;
            if ((literal = AbstractParser.LITERALS.get(literal)) == ThisLiteral.class) fields |= THISREF;
            if (literal != null) egressType = literal.getClass();
        }
        else if (AbstractParser.OPERATORS.containsKey(literal)) {
            fields |= OPERATOR;
            literal = AbstractParser.OPERATORS.get(literal);
            egressType = literal.getClass();
            return;
        }
        else if (isNumber(name)) {
            fields |= NUMERIC | LITERAL | IDENTIFIER;
            literal = handleNumericConversion(name);
            egressType = literal.getClass();

            if ((fields & INVERT) != 0) {
                try {
                    literal = ~((Integer) literal);
                }
                catch (ClassCastException e) {
                    throw new CompileException("bitwise (~) operator can only be applied to integers");
                }
            }

            if (literal instanceof Integer) {
                intRegister = (Integer) literal;
                fields |= INTEGER32;

            }

            return;
        }
        else if ((fields & INLINE_COLLECTION) != 0) {
            return;
        }
        else if ((firstUnion = findFirst('.', name)) > 0) {
            if ((fields & METHOD) != 0) {
                if (firstUnion < findFirst('(', name)) {
                    fields |= DEEP_PROPERTY | IDENTIFIER;
                }
                else {
                    fields |= IDENTIFIER;
                }
            }
            else {
                fields |= DEEP_PROPERTY | IDENTIFIER;
            }

        }
        else {
            fields |= IDENTIFIER;
        }

        if ((endOfName = findFirst('[', name)) > 0) fields |= COLLECTION;

    }

    public void setAccessor(Accessor accessor) {
        this.accessor = accessor;
    }

    public boolean isIdentifier() {
        return (fields & IDENTIFIER) != 0;
    }


    public boolean isLiteral() {
        return (fields & LITERAL) != 0;
    }

    public boolean isThisVal() {
        return (fields & THISREF) != 0;
    }

    public boolean isOperator() {
        return (fields & OPERATOR) != 0;
    }

    public boolean isOperator(Integer operator) {
        return (fields & OPERATOR) != 0 && operator.equals(literal);
    }

    public Integer getOperator() {
        return (Integer) literal;
    }

    protected boolean isCollection() {
        return (fields & COLLECTION) != 0;
    }


    public boolean isAssignment() {
        return ((fields & ASSIGN) != 0);
    }

    public boolean isDeepProperty() {
        return ((fields & DEEP_PROPERTY) != 0);
    }

    public void setAsLiteral() {
        fields |= LITERAL;
    }

    public int getCursorPosition() {
        return cursorPosition;
    }

    public void setCursorPosition(int cursorPosition) {
        this.cursorPosition = cursorPosition;
    }

    public boolean isDiscard() {
        return discard;
    }

    public void setDiscard(boolean discard) {
        this.discard = discard;
    }

    public boolean isDebuggingSymbol() {
        return this.fields == -1;
    }

    public int getIntRegister() {
        return intRegister;
    }

    public void setIntRegister(int intRegister) {
        this.intRegister = intRegister;
    }


    public int getFields() {
        return fields;
    }


    public Accessor getAccessor() {
        return accessor;
    }
}


java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.