001    /*
002     * Copyright 1999-2001,2004 The Apache Software Foundation.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */ 
016    
017    package org.apache.commons.workflow.base;
018    
019    
020    import java.util.Collection;
021    import java.util.HashMap;
022    import java.util.Iterator;
023    import java.util.Map;
024    import java.util.Set;
025    import org.apache.commons.jxpath.JXPathIntrospector;
026    import org.apache.commons.jxpath.MapDynamicPropertyHandler;
027    import org.apache.commons.workflow.Scope;
028    import org.apache.commons.workflow.ScopeListener;
029    import org.apache.commons.workflow.util.ScopeSupport;
030    
031    
032    /**
033     * <strong>BaseScope</strong> is a basic <code>Scope</code> implementation
034     * that maintains its bean collection in an in-memory HashMap.  This can
035     * also serve as a convenient base class for more sophisticated
036     * <code>Scope</code> implementations.
037     *
038     * <p><strong>WARNING</strong> - No synchronization is performed within this
039     * class.  If it is used in a multiple thread environment, callers must
040     * take suitable precations.</p>
041     *
042     * @version $Revision: 155475 $ $Date: 2005-02-26 13:31:11 +0000 (Sat, 26 Feb 2005) $
043     * @author Craig R. McClanahan
044     */
045    
046    public class BaseScope implements Scope {
047    
048    
049        // -------------------------------------------------- Static Initialization
050    
051    
052        /**
053         * Register ourselves with JXPathIntrospector as an instance of a
054         * dynamic class (in JXPath terminology).
055         */
056        static {
057            JXPathIntrospector.registerDynamicClass
058                (BaseScope.class, MapDynamicPropertyHandler.class);
059        }
060    
061    
062        // ----------------------------------------------------- Instance Variables
063    
064    
065        /**
066         * The HashMap that contains our registered keys and beans.
067         */
068        protected HashMap map = new HashMap();
069    
070    
071        /**
072         * The event listener support object for this <code>Scope</code>.
073         */
074        protected ScopeSupport support = new ScopeSupport(this);
075    
076    
077        // ------------------------------------------------------------ Map Methods
078    
079    
080        /**
081         * Remove all beans from this Map and call <code>scopeCleared() on
082         * all registered <code>ScopeListeners</code>.
083         */
084        public void clear() {
085    
086            map.clear();
087            support.fireScopeCleared();
088    
089        }
090    
091    
092        /**
093         * Return <code>true</code> if this map contains the specified key.
094         *
095         * @param key Key to be looked up
096         */
097        public boolean containsKey(Object key) {
098    
099            return (map.containsKey(key));
100    
101        }
102    
103    
104        /**
105         * Return <code>true</code> if this map contains the specified value.
106         *
107         * @param value Value to be looked up
108         */
109        public boolean containsValue(Object value) {
110    
111            return (map.containsValue(value));
112    
113        }
114    
115    
116        /**
117         * Return a set view of the mappings contained in this map.
118         */
119        public Set entrySet() {
120    
121            return (map.entrySet());
122    
123        }
124    
125    
126        /**
127         * Compare the specified object with this map for equality.
128         *
129         * @param object Object to be compared
130         */
131        public boolean equals(Object object) {
132    
133            return (map.equals(object));
134    
135        }
136    
137    
138        /**
139         * Return the value to which this map maps the specified key.
140         *
141         * @param key Key to be looked up
142         */
143        public Object get(Object key) {
144    
145            return (get((String) key));
146    
147        }
148    
149    
150        /**
151         * Return the value to which this map maps the specified key.
152         *
153         * @param key Key to be looked up
154         */
155        public Object get(String key) {
156    
157            return (map.get(key));
158    
159        }
160    
161    
162        /**
163         * Return the hash code value for this map.
164         */
165        public int hashCode() {
166    
167            return (map.hashCode());
168    
169        }
170    
171    
172        /**
173         * Return <code>true</code> if this map is empty.
174         */
175        public boolean isEmpty() {
176    
177            return (map.isEmpty());
178    
179        }
180    
181    
182        /**
183         * Return a set view of the keys contained in this map.
184         */
185        public Set keySet() {
186    
187            return (map.keySet());
188    
189        }
190    
191    
192        /**
193         * Add or replace the bean associated with the specified key.
194         *
195         * @param key Key with which the new value should be associated
196         *  (cannot be null)
197         * @param bean Bean to be associated with this key (cannot be null)
198         */
199        public Object put(Object key, Object bean) {
200    
201            return (put((String) key, bean));
202    
203        }
204    
205    
206        /**
207         * Add the specified bean, associated with the specified key, to this
208         * scope and replace any previous bean associated with this key.  If
209         * the bean was added, call <code>beanAdded()</code> on all registered
210         * listeners after the add is done.  If an old bean was replaced,
211         * call <code>beanReplaced()</code> (passing the old value in the event)
212         * on all registered <code>ScopeListeners</code> after the removal
213         * is done.  If a bean was replaced, the old value is also returned;
214         * otherwise <code>null</code> is returned.
215         *
216         * @param key Key with which the new value should be associated
217         *  (cannot be null)
218         * @param bean Bean to be associated with this key (cannot be null)
219         *
220         * @exception IllegalArgumentException if <code>key</code> or
221         *  <code>bean</code> is null
222         */
223        public Object put(String key, Object bean) {
224    
225            if (key == null)
226                throw new IllegalArgumentException("Key cannot be null");
227            if (bean == null)
228                throw new IllegalArgumentException("Value cannot be null");
229    
230            Object old = get(key);
231            if (map.containsKey(key)) {
232                map.put(key, bean);
233                support.fireBeanReplaced(key, old);
234            } else {
235                map.put(key, bean);
236                support.fireBeanAdded(key, bean);
237            }
238            return (old);
239                
240        }
241    
242    
243        /**
244         * Copy all of the mappings from the specified map into this map,
245         * firing appropriate <code>beanAdded()</code> and
246         * <code>beanReplaced()</code> events along the way.
247         *
248         * @param in Map whose contents are to be added
249         */
250        public void putAll(Map in) {
251    
252            Iterator keys = in.keySet().iterator();
253            while (keys.hasNext()) {
254                Object key = keys.next();
255                put(key, in.get(key));
256            }
257    
258        }
259    
260    
261        /**
262         * Remove the bean associated with the specified key (if any), and return
263         * the old value if removed.
264         *
265         * @param key Key of the bean to remove (cannot be null)
266         */
267        public Object remove(Object key) {
268    
269            return (remove((String) key));
270    
271        }
272    
273    
274    
275        /**
276         * Remove the bean associated with the specified key (if any).  If such
277         * a bean is found and removed, call <code>beanRemoved()</code> on all
278         * registered <code>ScopeListeners</code> after the removal is done.
279         * Return the old value (if any); otherwise return <code>null</code>.
280         *
281         * @param key Key of the bean to remove (cannot be null)
282         *
283         * @exception IllegalArgumentException if <code>key</code> is null
284         */
285        public Object remove(String key) {
286    
287            if (map.containsKey(key)) {
288                Object old = map.remove(key);
289                support.fireBeanRemoved(key, old);
290                return (old);
291            }
292            return (null);
293    
294        }
295    
296    
297        /**
298         * Return the number of key-value mappings in this map.
299         */
300        public int size() {
301    
302            return (map.size());
303    
304        }
305    
306    
307        /**
308         * Return a Collection view of the values contained in this map.
309         */
310        public Collection values() {
311    
312            return (map.values());
313    
314        }
315    
316    
317        // ------------------------------------------------- Event Listener Methods
318    
319    
320        /**
321         * Add a listener that is notified each time beans are added,
322         * replaced, or removed in this scope.
323         *
324         * @param listener The ScopeListener to be added
325         */
326        public void addScopeListener(ScopeListener listener) {
327    
328            support.addScopeListener(listener);
329    
330        }
331    
332    
333        /**
334         * Remove a listener that is notified each time beans are added,
335         * replaced, or removed in this scope.
336         *
337         * @param listener The ScopeListener to be removed
338         */
339        public void removeScopeListener(ScopeListener listener) {
340    
341            support.removeScopeListener(listener);
342    
343        }
344    
345    
346    }