    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 *     https://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.configuration2;
    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
    027import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
    028
    029/**
    030 * Basic configuration class. Stores the configuration data but does not provide any load or save functions. If you want
    031 * to load your Configuration from a file use PropertiesConfiguration or XmlConfiguration.
    032 *
    033 * This class extends normal Java properties by adding the possibility to use the same key many times concatenating the
    034 * value strings instead of overwriting them.
    035 */
    036public class BaseConfiguration extends AbstractConfiguration implements Cloneable {
    037
    038    /**
    039     * Stores the configuration key-value pairs.
    040     */
    041    private Map<String, Object> store = new LinkedHashMap<>();
    042
    043    /**
    044     * Constructs a new instance.
    045     */
    046    public BaseConfiguration() {
    047        // empty
    048    }
    049
    050    /**
    051     * Adds a key/value pair to the map. This routine does no magic morphing. It ensures the keylist is maintained
    052     *
    053     * @param key key to use for mapping
    054     * @param value object to store
    055     */
    056    @Override
    057    protected void addPropertyDirect(final String key, final Object value) {
    058        final Object previousValue = getPropertyInternal(key);
    059        if (previousValue == null) {
    060            store.put(key, value);
    061        } else if (previousValue instanceof List) {
    062            // safe to case because we have created the lists ourselves
    063            @SuppressWarnings("unchecked")
    064            final List<Object> valueList = (List<Object>) previousValue;
    065            // the value is added to the existing list
    066            valueList.add(value);
    067        } else {
    068            // the previous value is replaced by a list containing the previous value and the new value
    069            final List<Object> list = new ArrayList<>();
    070            list.add(previousValue);
    071            list.add(value);
    072            store.put(key, list);
    073        }
    074    }
    075
    076    @Override
    077    protected void clearInternal() {
    078        store.clear();
    079    }
    080
    081    /**
    082     * Clear a property in the configuration.
    083     *
    084     * @param key the key to remove along with corresponding value.
    085     */
    086    @Override
    087    protected void clearPropertyDirect(final String key) {
    088        store.remove(key);
    089    }
    090
    091    /**
    092     * Creates a copy of this object. This implementation will create a deep clone, i.e. the map that stores the properties
    093     * is cloned, too. So changes performed at the copy won't affect the original and vice versa.
    094     *
    095     * @return the copy
    096     * @since 1.3
    097     */
    098    @Override
    099    public Object clone() {
    100        try {
    101            final BaseConfiguration copy = (BaseConfiguration) super.clone();
    102            cloneStore(copy);
    103            copy.cloneInterpolator(this);
    104            return copy;
    105        } catch (final CloneNotSupportedException cex) {
    106            // should not happen
    107            throw new ConfigurationRuntimeException(cex);
    108        }
    109    }
    110
    111    /**
    112     * Clones the internal map with the data of this configuration.
    113     *
    114     * @param copy the copy created by the {@code clone()} method
    115     * @throws CloneNotSupportedException if the map cannot be cloned
    116     */
    117    private void cloneStore(final BaseConfiguration copy) throws CloneNotSupportedException {
    118        // This is safe because the type of the map is known
    119        copy.store = ConfigurationUtils.clone(store);
    120        // Handle collections in the map; they have to be cloned, too
    121        store.forEach((k, v) -> {
    122            if (v instanceof Collection) {
    123                // This is safe because the collections were created by ourselves
    124                @SuppressWarnings("unchecked")
    125                final Collection<String> strList = (Collection<String>) v;
    126                copy.store.put(k, new ArrayList<>(strList));
    127            }
    128        });
    129    }
    130
    131    /**
    132     * check if the configuration contains the key
    133     *
    134     * @param key the configuration key
    135     * @return {@code true} if Configuration contain given key, {@code false} otherwise.
    136     */
    137    @Override
    138    protected boolean containsKeyInternal(final String key) {
    139        return store.containsKey(key);
    140    }
    141
    142    /**
    143     * Tests whether this configuration contains one or more matches to this value. This operation stops at first
    144     * match but may be more expensive than the containsKey method.
    145     *
    146     * @since 2.11.0
    147     */
    148    @Override
    149    protected boolean containsValueInternal(final Object value) {
    150        return store.containsValue(value);
    151    }
    152
    153    /**
    154     * Gets the list of the keys contained in the configuration repository.
    155     *
    156     * @return An Iterator.
    157     */
    158    @Override
    159    protected Iterator<String> getKeysInternal() {
    160        return store.keySet().iterator();
    161    }
    162
    163    /**
    164     * Reads property from underlying map.
    165     *
    166     * @param key key to use for mapping
    167     * @return object associated with the given configuration key.
    168     */
    169    @Override
    170    protected Object getPropertyInternal(final String key) {
    171        return store.get(key);
    172    }
    173
    174    /**
    175     * Check if the configuration is empty
    176     *
    177     * @return {@code true} if Configuration is empty, {@code false} otherwise.
    178     */
    179    @Override
    180    protected boolean isEmptyInternal() {
    181        return store.isEmpty();
    182    }
    183
    184    /**
    185     * {@inheritDoc} This implementation obtains the size directly from the map used as data store. So this is a rather
    186     * efficient implementation.
    187     */
    188    @Override
    189    protected int sizeInternal() {
    190        return store.size();
    191    }
    192}