org.apache.commons.ognl.OgnlContext.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.commons.ognl.OgnlContext.java

Source

package org.apache.commons.ognl;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

import org.apache.commons.ognl.enhance.LocalReference;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

/**
 * This class defines the execution context for an OGNL expression
 */
public class OgnlContext implements Map<String, Object> {

    public static final String CONTEXT_CONTEXT_KEY = "context";

    public static final String ROOT_CONTEXT_KEY = "root";

    public static final String THIS_CONTEXT_KEY = "this";

    public static final String TRACE_EVALUATIONS_CONTEXT_KEY = "_traceEvaluations";

    public static final String LAST_EVALUATION_CONTEXT_KEY = "_lastEvaluation";

    public static final String KEEP_LAST_EVALUATION_CONTEXT_KEY = "_keepLastEvaluation";

    public static final String CLASS_RESOLVER_CONTEXT_KEY = "_classResolver";

    public static final String TYPE_CONVERTER_CONTEXT_KEY = "_typeConverter";

    public static final String MEMBER_ACCESS_CONTEXT_KEY = "_memberAccess";

    private static final String PROPERTY_KEY_PREFIX = "ognl";

    private static boolean defaultTraceEvaluations = false;

    private static boolean defaultKeepLastEvaluation = false;

    public static final DefaultClassResolver DEFAULT_CLASS_RESOLVER = new DefaultClassResolver();

    public static final TypeConverter DEFAULT_TYPE_CONVERTER = new DefaultTypeConverter();

    public static final MemberAccess DEFAULT_MEMBER_ACCESS = new DefaultMemberAccess(false);

    private static final Set<String> RESERVED_KEYS = new HashSet<String>(11);

    private Object root;

    private Object currentObject;

    private Node currentNode;

    private boolean traceEvaluations = defaultTraceEvaluations;

    private Evaluation rootEvaluation;

    private Evaluation currentEvaluation;

    private Evaluation lastEvaluation;

    private boolean keepLastEvaluation = defaultKeepLastEvaluation;

    private Map<String, Object> values = new HashMap<String, Object>(23);

    private ClassResolver classResolver = DEFAULT_CLASS_RESOLVER;

    private TypeConverter typeConverter = DEFAULT_TYPE_CONVERTER;

    private MemberAccess memberAccess = DEFAULT_MEMBER_ACCESS;

    static {
        String s;

        RESERVED_KEYS.add(CONTEXT_CONTEXT_KEY);
        RESERVED_KEYS.add(ROOT_CONTEXT_KEY);
        RESERVED_KEYS.add(THIS_CONTEXT_KEY);
        RESERVED_KEYS.add(TRACE_EVALUATIONS_CONTEXT_KEY);
        RESERVED_KEYS.add(LAST_EVALUATION_CONTEXT_KEY);
        RESERVED_KEYS.add(KEEP_LAST_EVALUATION_CONTEXT_KEY);
        RESERVED_KEYS.add(CLASS_RESOLVER_CONTEXT_KEY);
        RESERVED_KEYS.add(TYPE_CONVERTER_CONTEXT_KEY);
        RESERVED_KEYS.add(MEMBER_ACCESS_CONTEXT_KEY);

        try {
            s = System.getProperty(PROPERTY_KEY_PREFIX + ".traceEvaluations");
            if (s != null) {
                defaultTraceEvaluations = Boolean.valueOf(s.trim());
            }
            s = System.getProperty(PROPERTY_KEY_PREFIX + ".keepLastEvaluation");
            if (s != null) {
                defaultKeepLastEvaluation = Boolean.valueOf(s.trim());
            }
        } catch (SecurityException ex) {
            // restricted access environment, just keep defaults
        }
    }

    private Stack<Class<?>> typeStack = new Stack<Class<?>>();

    private Stack<Class<?>> accessorStack = new Stack<Class<?>>();

    private int localReferenceCounter = 0;

    private Map<String, LocalReference> localReferenceMap = null;

    /**
     * Constructs a new OgnlContext with the default class resolver, type converter and member access.
     */
    public OgnlContext() {
    }

    /**
     * Constructs a new OgnlContext with the given class resolver, type converter and member access. If any of these
     * parameters is null the default will be used.
     */
    public OgnlContext(ClassResolver classResolver, TypeConverter typeConverter, MemberAccess memberAccess) {
        this();
        if (classResolver != null) {
            this.classResolver = classResolver;
        }
        if (typeConverter != null) {
            this.typeConverter = typeConverter;
        }
        if (memberAccess != null) {
            this.memberAccess = memberAccess;
        }
    }

    public OgnlContext(Map<String, Object> values) {
        super();
        this.values = values;
    }

    public OgnlContext(ClassResolver classResolver, TypeConverter typeConverter, MemberAccess memberAccess,
            Map<String, Object> values) {
        this(classResolver, typeConverter, memberAccess);
        this.values = values;
    }

    public void setValues(Map<String, Object> value) {
        values.putAll(value);
    }

    public Map<String, Object> getValues() {
        return values;
    }

    public void setClassResolver(ClassResolver value) {
        if (value == null) {
            throw new IllegalArgumentException("cannot set ClassResolver to null");
        }
        classResolver = value;
    }

    public ClassResolver getClassResolver() {
        return classResolver;
    }

    public void setTypeConverter(TypeConverter value) {
        if (value == null) {
            throw new IllegalArgumentException("cannot set TypeConverter to null");
        }
        typeConverter = value;
    }

    public TypeConverter getTypeConverter() {
        return typeConverter;
    }

    public void setMemberAccess(MemberAccess value) {
        if (value == null) {
            throw new IllegalArgumentException("cannot set MemberAccess to null");
        }
        memberAccess = value;
    }

    public MemberAccess getMemberAccess() {
        return memberAccess;
    }

    public void setRoot(Object value) {
        root = value;
        accessorStack.clear();
        typeStack.clear();
        currentObject = value;

        if (currentObject != null) {
            setCurrentType(currentObject.getClass());
        }
    }

    public Object getRoot() {
        return root;
    }

    public boolean getTraceEvaluations() {
        return traceEvaluations;
    }

    public void setTraceEvaluations(boolean value) {
        traceEvaluations = value;
    }

    public Evaluation getLastEvaluation() {
        return lastEvaluation;
    }

    public void setLastEvaluation(Evaluation value) {
        lastEvaluation = value;
    }

    /**
     * This method can be called when the last evaluation has been used and can be returned for reuse in the free pool
     * maintained by the runtime. This is not a necessary step, but is useful for keeping memory usage down. This will
     * recycle the last evaluation and then set the last evaluation to null.
     */
    public void recycleLastEvaluation() {
        lastEvaluation = null;
    }

    /**
     * Returns true if the last evaluation that was done on this context is retained and available through
     * <code>getLastEvaluation()</code>. The default is true.
     */
    public boolean getKeepLastEvaluation() {
        return keepLastEvaluation;
    }

    /**
     * Sets whether the last evaluation that was done on this context is retained and available through
     * <code>getLastEvaluation()</code>. The default is true.
     */
    public void setKeepLastEvaluation(boolean value) {
        keepLastEvaluation = value;
    }

    public void setCurrentObject(Object value) {
        currentObject = value;
    }

    public Object getCurrentObject() {
        return currentObject;
    }

    public void setCurrentAccessor(Class<?> type) {
        accessorStack.add(type);
    }

    public Class<?> getCurrentAccessor() {
        if (accessorStack.isEmpty()) {
            return null;
        }

        return accessorStack.peek();
    }

    public Class<?> getPreviousAccessor() {
        if (accessorStack.isEmpty()) {
            return null;
        }

        if (accessorStack.size() > 1) {
            return accessorStack.get(accessorStack.size() - 2);
        }

        return null;
    }

    public Class<?> getFirstAccessor() {
        if (accessorStack.isEmpty()) {
            return null;
        }

        return accessorStack.get(0);
    }

    /**
     * Gets the current class type being evaluated on the stack, as set by {@link #setCurrentType(Class)}.
     * 
     * @return The current object type, may be null.
     */
    public Class<?> getCurrentType() {
        if (typeStack.isEmpty()) {
            return null;
        }

        return typeStack.peek();
    }

    public void setCurrentType(Class<?> type) {
        typeStack.add(type);
    }

    /**
     * Represents the last known object type on the evaluation stack, will be the value of the last known
     * {@link #getCurrentType()}.
     * 
     * @return The previous type of object on the stack, may be null.
     */
    public Class<?> getPreviousType() {
        if (typeStack.isEmpty()) {
            return null;
        }

        if (typeStack.size() > 1) {
            return typeStack.get(typeStack.size() - 2);
        }

        return null;
    }

    public void setPreviousType(Class<?> type) {
        if (typeStack.isEmpty() || typeStack.size() < 2) {
            return;
        }

        typeStack.set(typeStack.size() - 2, type);
    }

    public Class<?> getFirstType() {
        if (typeStack.isEmpty()) {
            return null;
        }

        return typeStack.get(0);
    }

    public void setCurrentNode(Node value) {
        currentNode = value;
    }

    public Node getCurrentNode() {
        return currentNode;
    }

    /**
     * Gets the current Evaluation from the top of the stack. This is the Evaluation that is in process of evaluating.
     */
    public Evaluation getCurrentEvaluation() {
        return currentEvaluation;
    }

    public void setCurrentEvaluation(Evaluation value) {
        currentEvaluation = value;
    }

    /**
     * Gets the root of the evaluation stack. This Evaluation contains the node representing the root expression and the
     * source is the root source object.
     */
    public Evaluation getRootEvaluation() {
        return rootEvaluation;
    }

    public void setRootEvaluation(Evaluation value) {
        rootEvaluation = value;
    }

    /**
     * Returns the Evaluation at the relative index given. This should be zero or a negative number as a relative
     * reference back up the evaluation stack. Therefore getEvaluation(0) returns the current Evaluation.
     */
    public Evaluation getEvaluation(int relativeIndex) {
        Evaluation result = null;

        if (relativeIndex <= 0) {
            result = currentEvaluation;
            while ((++relativeIndex < 0) && (result != null)) {
                result = result.getParent();
            }
        }
        return result;
    }

    /**
     * Pushes a new Evaluation onto the stack. This is done before a node evaluates. When evaluation is complete it
     * should be popped from the stack via <code>popEvaluation()</code>.
     */
    public void pushEvaluation(Evaluation value) {
        if (currentEvaluation != null) {
            currentEvaluation.addChild(value);
        } else {
            setRootEvaluation(value);
        }
        setCurrentEvaluation(value);
    }

    /**
     * Pops the current Evaluation off of the top of the stack. This is done after a node has completed its evaluation.
     */
    public Evaluation popEvaluation() {
        Evaluation result;

        result = currentEvaluation;
        setCurrentEvaluation(result.getParent());
        if (currentEvaluation == null) {
            setLastEvaluation(getKeepLastEvaluation() ? result : null);
            setRootEvaluation(null);
            setCurrentNode(null);
        }
        return result;
    }

    public int incrementLocalReferenceCounter() {
        return ++localReferenceCounter;
    }

    public void addLocalReference(String key, LocalReference reference) {
        if (localReferenceMap == null) {
            localReferenceMap = new LinkedHashMap<String, LocalReference>();
        }

        localReferenceMap.put(key, reference);
    }

    public Map<String, LocalReference> getLocalReferences() {
        return localReferenceMap;
    }

    /* ================= Map interface ================= */
    public int size() {
        return values.size();
    }

    public boolean isEmpty() {
        return values.isEmpty();
    }

    public boolean containsKey(Object key) {
        return values.containsKey(key);
    }

    public boolean containsValue(Object value) {
        return values.containsValue(value);
    }

    public Object get(Object key) {
        Object result = null;

        // FIXME: complexity is O(n)
        if (RESERVED_KEYS.contains(key)) {
            if (THIS_CONTEXT_KEY.equals(key)) {
                result = getCurrentObject();
            } else if (ROOT_CONTEXT_KEY.equals(key)) {
                result = getRoot();
            } else if (CONTEXT_CONTEXT_KEY.equals(key)) {
                result = this;
            } else if (TRACE_EVALUATIONS_CONTEXT_KEY.equals(key)) {
                result = getTraceEvaluations() ? Boolean.TRUE : Boolean.FALSE;
            } else if (LAST_EVALUATION_CONTEXT_KEY.equals(key)) {
                result = getLastEvaluation();
            } else if (KEEP_LAST_EVALUATION_CONTEXT_KEY.equals(key)) {
                result = getKeepLastEvaluation() ? Boolean.TRUE : Boolean.FALSE;
            } else if (CLASS_RESOLVER_CONTEXT_KEY.equals(key)) {
                result = getClassResolver();
            } else if (TYPE_CONVERTER_CONTEXT_KEY.equals(key)) {
                result = getTypeConverter();
            } else if (MEMBER_ACCESS_CONTEXT_KEY.equals(key)) {
                result = getMemberAccess();
            }
        } else {
            result = values.get(key);
        }
        return result;
    }

    public Object put(String key, Object value) {
        Object result = null;

        // FIXME: complexity is O(n)
        if (RESERVED_KEYS.contains(key)) {
            if (CONTEXT_CONTEXT_KEY.equals(key)) {
                throw new IllegalArgumentException("can't change " + CONTEXT_CONTEXT_KEY + " in context");
            }

            if (THIS_CONTEXT_KEY.equals(key)) {
                result = getCurrentObject();
                setCurrentObject(value);
            } else if (ROOT_CONTEXT_KEY.equals(key)) {
                result = getRoot();
                setRoot(value);
            } else if (TRACE_EVALUATIONS_CONTEXT_KEY.equals(key)) {
                result = getTraceEvaluations() ? Boolean.TRUE : Boolean.FALSE;
                setTraceEvaluations(OgnlOps.booleanValue(value));
            } else if (LAST_EVALUATION_CONTEXT_KEY.equals(key)) {
                result = getLastEvaluation();
                lastEvaluation = (Evaluation) value;
            } else if (KEEP_LAST_EVALUATION_CONTEXT_KEY.equals(key)) {
                result = getKeepLastEvaluation() ? Boolean.TRUE : Boolean.FALSE;
                setKeepLastEvaluation(OgnlOps.booleanValue(value));
            } else if (CLASS_RESOLVER_CONTEXT_KEY.equals(key)) {
                result = getClassResolver();
                setClassResolver((ClassResolver) value);
            } else if (TYPE_CONVERTER_CONTEXT_KEY.equals(key)) {
                result = getTypeConverter();
                setTypeConverter((TypeConverter) value);
            } else if (MEMBER_ACCESS_CONTEXT_KEY.equals(key)) {
                result = getMemberAccess();
                setMemberAccess((MemberAccess) value);
            }
        } else {
            result = values.put(key, value);
        }

        return result;
    }

    public Object remove(Object key) {
        Object result = null;

        // FIXME: complexity is O(n)
        if (RESERVED_KEYS.contains(key)) {
            if (CONTEXT_CONTEXT_KEY.equals(key) || TRACE_EVALUATIONS_CONTEXT_KEY.equals(key)
                    || KEEP_LAST_EVALUATION_CONTEXT_KEY.equals(key)) {
                throw new IllegalArgumentException("can't remove " + key + " from context");
            }

            if (THIS_CONTEXT_KEY.equals(key)) {
                result = getCurrentObject();
                setCurrentObject(null);
            } else if (ROOT_CONTEXT_KEY.equals(key)) {
                result = getRoot();
                setRoot(null);
            } else if (LAST_EVALUATION_CONTEXT_KEY.equals(key)) {
                result = lastEvaluation;
                setLastEvaluation(null);
            } else if (CLASS_RESOLVER_CONTEXT_KEY.equals(key)) {
                result = getClassResolver();
                setClassResolver(null);
            } else if (TYPE_CONVERTER_CONTEXT_KEY.equals(key)) {
                result = getTypeConverter();
                setTypeConverter(null);
            } else if (MEMBER_ACCESS_CONTEXT_KEY.equals(key)) {
                result = getMemberAccess();
                setMemberAccess(null);
            }
        } else {
            result = values.remove(key);
        }
        return result;
    }

    public void putAll(Map<? extends String, ?> t) {
        for (Entry<? extends String, ?> entry : t.entrySet()) {
            put(entry.getKey(), entry.getValue());
        }
    }

    public void clear() {
        values.clear();
        typeStack.clear();
        accessorStack.clear();

        localReferenceCounter = 0;
        if (localReferenceMap != null) {
            localReferenceMap.clear();
        }

        setRoot(null);
        setCurrentObject(null);
        setRootEvaluation(null);
        setCurrentEvaluation(null);
        setLastEvaluation(null);
        setCurrentNode(null);
        setClassResolver(DEFAULT_CLASS_RESOLVER);
        setTypeConverter(DEFAULT_TYPE_CONVERTER);
        setMemberAccess(DEFAULT_MEMBER_ACCESS);
    }

    public Set<String> keySet() {
        /* Should root, currentObject, classResolver, typeConverter & memberAccess be included here? */
        return values.keySet();
    }

    public Collection<Object> values() {
        /* Should root, currentObject, classResolver, typeConverter & memberAccess be included here? */
        return values.values();
    }

    public Set<Entry<String, Object>> entrySet() {
        /* Should root, currentObject, classResolver, typeConverter & memberAccess be included here? */
        return values.entrySet();
    }

    @Override
    public boolean equals(Object o) {
        return values.equals(o);
    }

    @Override
    public int hashCode() {
        return values.hashCode();
    }
}