001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.scxml2.env;
018
019import java.io.Serializable;
020import java.util.HashMap;
021import java.util.Map;
022
023import org.apache.commons.logging.Log;
024import org.apache.commons.logging.LogFactory;
025import org.apache.commons.scxml2.Context;
026import org.apache.commons.scxml2.SCXMLSystemContext;
027
028/**
029 * Simple Context wrapping a map of variables.
030 *
031 */
032public class SimpleContext implements Context, Serializable {
033
034    /** Serial version UID. */
035    private static final long serialVersionUID = 1L;
036    /** Implementation independent log category. */
037    private static final Log DEFAULT_LOG = LogFactory.getLog(Context.class);
038    private Log log = DEFAULT_LOG;
039    /** The parent Context to this Context. */
040    private Context parent;
041    /** The Map of variables and their values in this Context. */
042    private Map<String, Object> vars;
043
044    protected final SCXMLSystemContext systemContext;
045
046    /**
047     * Constructor.
048     *
049     */
050    public SimpleContext() {
051        this(null, null);
052    }
053
054    /**
055     * Constructor.
056     *
057     * @param parent A parent Context, can be null
058     */
059    public SimpleContext(final Context parent) {
060        this(parent, null);
061    }
062
063    /**
064     * Constructor.
065     *
066     * @param parent A parent Context, can be null
067     * @param initialVars A pre-populated initial variables map
068     */
069    public SimpleContext(final Context parent, final Map<String, Object> initialVars) {
070        this.parent = parent;
071        this.systemContext = parent instanceof SCXMLSystemContext ?
072                (SCXMLSystemContext) parent : parent != null ? parent.getSystemContext() : null;
073        if (initialVars == null) {
074            setVars(new HashMap<String, Object>());
075        } else {
076            setVars(this.vars = initialVars);
077        }
078    }
079
080    /**
081     * Assigns a new value to an existing variable or creates a new one.
082     * The method searches the chain of parent Contexts for variable
083     * existence.
084     *
085     * @param name The variable name
086     * @param value The variable value
087     * @see org.apache.commons.scxml2.Context#set(String, Object)
088     */
089    public void set(final String name, final Object value) {
090        if (getVars().containsKey(name)) { //first try to override local
091            setLocal(name, value);
092        } else if (parent != null && parent.has(name)) { //then check for global
093            parent.set(name, value);
094        } else { //otherwise create a new local variable
095            setLocal(name, value);
096        }
097    }
098
099    /**
100     * Get the value of this variable; delegating to parent.
101     *
102     * @param name The variable name
103     * @return Object The variable value
104     * @see org.apache.commons.scxml2.Context#get(java.lang.String)
105     */
106    public Object get(final String name) {
107        Object localValue = getVars().get(name);
108        if (localValue != null) {
109            return localValue;
110        } else if (parent != null) {
111            return parent.get(name);
112        } else {
113            return null;
114        }
115    }
116
117    /**
118     * Check if this variable exists, delegating to parent.
119     *
120     * @param name The variable name
121     * @return boolean true if this variable exists
122     * @see org.apache.commons.scxml2.Context#has(java.lang.String)
123     */
124    public boolean has(final String name) {
125        return (hasLocal(name) || (parent != null && parent.has(name)));
126    }
127
128    /**
129     * Check if this variable exists, only checking this Context
130     *
131     * @param name The variable name
132     * @return boolean true if this variable exists
133     * @see org.apache.commons.scxml2.Context#hasLocal(java.lang.String)
134     */
135    public boolean hasLocal(final String name) {
136        return (getVars().containsKey(name));
137    }
138
139    /**
140     * Clear this Context.
141     *
142     * @see org.apache.commons.scxml2.Context#reset()
143     */
144    public void reset() {
145        getVars().clear();
146    }
147
148    /**
149     * Get the parent Context, may be null.
150     *
151     * @return Context The parent Context
152     * @see org.apache.commons.scxml2.Context#getParent()
153     */
154    public Context getParent() {
155        return parent;
156    }
157
158    /**
159     * Get the SCXMLSystemContext for this Context, should not be null unless this is the root Context
160     *
161     * @return The SCXMLSystemContext in a chained Context environment
162     */
163    public final SCXMLSystemContext getSystemContext() {
164        return systemContext;
165    }
166
167    /**
168     * Assigns a new value to an existing variable or creates a new one.
169     * The method allows to shaddow a variable of the same name up the
170     * Context chain.
171     *
172     * @param name The variable name
173     * @param value The variable value
174     * @see org.apache.commons.scxml2.Context#setLocal(String, Object)
175     */
176    public void setLocal(final String name, final Object value) {
177        getVars().put(name, value);
178        if (log.isDebugEnabled()) {
179            log.debug(name + " = " + String.valueOf(value));
180        }
181    }
182
183    /**
184     * Set the variables map.
185     *
186     * @param vars The new Map of variables.
187     */
188    protected void setVars(final Map<String, Object> vars) {
189        this.vars = vars;
190    }
191
192    /**
193     * Get the Map of all local variables in this Context.
194     *
195     * @return Returns the vars.
196     */
197    public Map<String, Object> getVars() {
198        return vars;
199    }
200
201    /**
202     * Set the log used by this <code>Context</code> instance.
203     *
204     * @param log The new log.
205     */
206    protected void setLog(final Log log) {
207        this.log = log;
208    }
209
210    /**
211     * Get the log used by this <code>Context</code> instance.
212     *
213     * @return Log The log being used.
214     */
215    protected Log getLog() {
216        return log;
217    }
218
219}
220