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