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     */
017    
018    package org.apache.commons.configuration;
019    
020    import java.util.ArrayList;
021    import java.util.Collection;
022    import java.util.Iterator;
023    import java.util.List;
024    import java.util.Map;
025    
026    import org.apache.commons.collections.map.LinkedMap;
027    
028    /**
029     * Basic configuration class. Stores the configuration data but does not
030     * provide any load or save functions. If you want to load your Configuration
031     * from a file use PropertiesConfiguration or XmlConfiguration.
032     *
033     * This class extends normal Java properties by adding the possibility
034     * to use the same key many times concatenating the value strings
035     * instead of overwriting them.
036     *
037     * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
038     * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
039     * @author <a href="mailto:daveb@miceda-data">Dave Bryson</a>
040     * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
041     * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
042     * @author <a href="mailto:kjohnson@transparent.com">Kent Johnson</a>
043     * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
044     * @author <a href="mailto:ipriha@surfeu.fi">Ilkka Priha</a>
045     * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
046     * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a>
047     * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
048     * @author <a href="mailto:ksh@scand.com">Konstantin Shaposhnikov</a>
049     * @author Oliver Heger
050     * @version $Id: BaseConfiguration.java 806862 2009-08-22 15:13:31Z oheger $
051     */
052    public class BaseConfiguration extends AbstractConfiguration implements Cloneable
053    {
054        /** stores the configuration key-value pairs */
055        private Map store = new LinkedMap();
056    
057        /**
058         * Adds a key/value pair to the map.  This routine does no magic morphing.
059         * It ensures the keylist is maintained
060         *
061         * @param key key to use for mapping
062         * @param value object to store
063         */
064        protected void addPropertyDirect(String key, Object value)
065        {
066            Object previousValue = getProperty(key);
067    
068            if (previousValue == null)
069            {
070                store.put(key, value);
071            }
072            else if (previousValue instanceof List)
073            {
074                // the value is added to the existing list
075                ((List) previousValue).add(value);
076            }
077            else
078            {
079                // the previous value is replaced by a list containing the previous value and the new value
080                List list = new ArrayList();
081                list.add(previousValue);
082                list.add(value);
083    
084                store.put(key, list);
085            }
086        }
087    
088        /**
089         * Read property from underlying map.
090         *
091         * @param key key to use for mapping
092         *
093         * @return object associated with the given configuration key.
094         */
095        public Object getProperty(String key)
096        {
097            return store.get(key);
098        }
099    
100        /**
101         * Check if the configuration is empty
102         *
103         * @return <code>true</code> if Configuration is empty,
104         * <code>false</code> otherwise.
105         */
106        public boolean isEmpty()
107        {
108            return store.isEmpty();
109        }
110    
111        /**
112         * check if the configuration contains the key
113         *
114         * @param key the configuration key
115         *
116         * @return <code>true</code> if Configuration contain given key,
117         * <code>false</code> otherwise.
118         */
119        public boolean containsKey(String key)
120        {
121            return store.containsKey(key);
122        }
123    
124        /**
125         * Clear a property in the configuration.
126         *
127         * @param key the key to remove along with corresponding value.
128         */
129        protected void clearPropertyDirect(String key)
130        {
131            if (containsKey(key))
132            {
133                store.remove(key);
134            }
135        }
136    
137        public void clear()
138        {
139            fireEvent(EVENT_CLEAR, null, null, true);
140            store.clear();
141            fireEvent(EVENT_CLEAR, null, null, false);
142        }
143    
144        /**
145         * Get the list of the keys contained in the configuration
146         * repository.
147         *
148         * @return An Iterator.
149         */
150        public Iterator getKeys()
151        {
152            return store.keySet().iterator();
153        }
154    
155        /**
156         * Creates a copy of this object. This implementation will create a deep
157         * clone, i.e. the map that stores the properties is cloned, too. So changes
158         * performed at the copy won't affect the original and vice versa.
159         *
160         * @return the copy
161         * @since 1.3
162         */
163        public Object clone()
164        {
165            try
166            {
167                BaseConfiguration copy = (BaseConfiguration) super.clone();
168                copy.store = (Map) ConfigurationUtils.clone(store);
169    
170                // Handle collections in the map; they have to be cloned, too
171                for (Iterator it = store.entrySet().iterator(); it.hasNext();)
172                {
173                    Map.Entry e = (Map.Entry) it.next();
174                    if (e.getValue() instanceof Collection)
175                    {
176                        copy.store.put(e.getKey(), new ArrayList((Collection) e
177                                .getValue()));
178                    }
179                }
180    
181                return copy;
182            }
183            catch (CloneNotSupportedException cex)
184            {
185                // should not happen
186                throw new ConfigurationRuntimeException(cex);
187            }
188        }
189    }