    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.LinkedHashMap;
    024    import java.util.List;
    025    import 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     */
    050    public 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    }