 
                        
        
                        
        org.apache.commons.configuration2.CompositeConfiguration Maven / Gradle / Ivy
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.configuration2;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import org.apache.commons.configuration2.convert.ListDelimiterHandler;
import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
/**
 * 
 * {@code CompositeConfiguration} allows you to add multiple {@code Configuration} objects to an aggregated
 * configuration. If you add Configuration1, and then Configuration2, any properties shared will mean that the value
 * defined by Configuration1 will be returned. If Configuration1 doesn't have the property, then Configuration2 will be
 * checked. You can add multiple different types or the same type of properties file.
 * 
 * 
 * When querying properties the order in which child configurations have been added is relevant. To deal with property
 * updates, a so-called in-memory configuration is used. Per default, such a configuration is created
 * automatically. All property writes target this special configuration. There are constructors which allow you to
 * provide a specific in-memory configuration. If used that way, the in-memory configuration is always the last one in
 * the list of child configurations. This means that for query operations all other configurations take precedence.
 * 
 * 
 * Alternatively it is possible to mark a child configuration as in-memory configuration when it is added. In this case
 * the treatment of the in-memory configuration is slightly different: it remains in the list of child configurations at
 * the position it was added, i.e. its priority for property queries can be defined by adding the child configurations
 * in the correct order.
 * 
 * 
 * This configuration class uses a {@code Synchronizer} to control concurrent access. While all methods for reading and
 * writing configuration properties make use of this {@code Synchronizer} per default, the methods for managing the list
 * of child configurations and the in-memory configuration
 * ({@code addConfiguration(), getNumberOfConfigurations(), removeConfiguration(),
 * getConfiguration(), getInMemoryConfiguration()}) are guarded, too. Because most methods for accessing configuration
 * data delegate to the list of child configurations, the thread-safety of a {@code CompositeConfiguration} object also
 * depends on the {@code Synchronizer} objects used by these children.
 * 
 */
public class CompositeConfiguration extends AbstractConfiguration implements Cloneable {
    /** List holding all the configuration */
    private List configList = new LinkedList<>();
    /**
     * Configuration that holds in memory stuff. Inserted as first so any setProperty() override anything else added.
     */
    private Configuration inMemoryConfiguration;
    /**
     * Stores a flag whether the current in-memory configuration is also a child configuration.
     */
    private boolean inMemoryConfigIsChild;
    /**
     * Creates an empty CompositeConfiguration object which can then be added some other Configuration files
     */
    public CompositeConfiguration() {
        clear();
    }
    /**
     * Create a CompositeConfiguration with an empty in memory configuration and adds the collection of configurations
     * specified.
     *
     * @param configurations the collection of configurations to add
     */
    public CompositeConfiguration(final Collection extends Configuration> configurations) {
        this(new BaseConfiguration(), configurations);
    }
    /**
     * Creates a CompositeConfiguration object with a specified in-memory configuration. This configuration will
     * store any changes made to the {@code CompositeConfiguration}. Note: Use this constructor if you want to set a special
     * type of in-memory configuration. If you have a configuration which should act as both a child configuration and as
     * in-memory configuration, use {@link #addConfiguration(Configuration, boolean)} with a value of true instead.
     *
     * @param inMemoryConfiguration the in memory configuration to use
     */
    public CompositeConfiguration(final Configuration inMemoryConfiguration) {
        this.configList.clear();
        this.inMemoryConfiguration = inMemoryConfiguration;
        this.configList.add(inMemoryConfiguration);
    }
    /**
     * Creates a CompositeConfiguration with a specified in-memory configuration, and then adds the given
     * collection of configurations.
     *
     * @param inMemoryConfiguration the in memory configuration to use
     * @param configurations the collection of configurations to add
     * @see #CompositeConfiguration(Configuration)
     */
    public CompositeConfiguration(final Configuration inMemoryConfiguration, final Collection extends Configuration> configurations) {
        this(inMemoryConfiguration);
        if (configurations != null) {
            configurations.forEach(this::addConfiguration);
        }
    }
    /**
     * Add a configuration.
     *
     * @param config the configuration to add
     */
    public void addConfiguration(final Configuration config) {
        addConfiguration(config, false);
    }
    /**
     * Adds a child configuration and optionally makes it the in-memory configuration. This means that all future
     * property write operations are executed on this configuration. Note that the current in-memory configuration is
     * replaced by the new one. If it was created automatically or passed to the constructor, it is removed from the list of
     * child configurations! Otherwise, it stays in the list of child configurations at its current position, but it passes
     * its role as in-memory configuration to the new one.
     *
     * @param config the configuration to be added
     * @param asInMemory true if this configuration becomes the new in-memory configuration, false
     *        otherwise
     * @since 1.8
     */
    public void addConfiguration(final Configuration config, final boolean asInMemory) {
        beginWrite(false);
        try {
            if (!configList.contains(config)) {
                if (asInMemory) {
                    replaceInMemoryConfiguration(config);
                    inMemoryConfigIsChild = true;
                }
                if (!inMemoryConfigIsChild) {
                    // As the inMemoryConfiguration contains all manually added
                    // keys, we must make sure that it is always last. "Normal", non
                    // composed configurations add their keys at the end of the
                    // configuration and we want to mimic this behavior.
                    configList.add(configList.indexOf(inMemoryConfiguration), config);
                } else {
                    // However, if the in-memory configuration is a regular child,
                    // only the order in which child configurations are added is relevant
                    configList.add(config);
                }
                if (config instanceof AbstractConfiguration) {
                    ((AbstractConfiguration) config).setThrowExceptionOnMissing(isThrowExceptionOnMissing());
                }
            }
        } finally {
            endWrite();
        }
    }
    /**
     * Add a configuration to the start of the list of child configurations.
     *
     * @param config the configuration to add
     * @since 2.3
     */
    public void addConfigurationFirst(final Configuration config) {
        addConfigurationFirst(config, false);
    }
    /**
     * Adds a child configuration to the start of the collection and optionally makes it the in-memory
     * configuration. This means that all future property write operations are executed on this configuration. Note
     * that the current in-memory configuration is replaced by the new one. If it was created automatically or passed to the
     * constructor, it is removed from the list of child configurations! Otherwise, it stays in the list of child
     * configurations at its current position, but it passes its role as in-memory configuration to the new one.
     *
     * @param config the configuration to be added
     * @param asInMemory true if this configuration becomes the new in-memory configuration, false
     *        otherwise
     * @since 2.3
     */
    public void addConfigurationFirst(final Configuration config, final boolean asInMemory) {
        beginWrite(false);
        try {
            if (!configList.contains(config)) {
                if (asInMemory) {
                    replaceInMemoryConfiguration(config);
                    inMemoryConfigIsChild = true;
                }
                configList.add(0, config);
                if (config instanceof AbstractConfiguration) {
                    ((AbstractConfiguration) config).setThrowExceptionOnMissing(isThrowExceptionOnMissing());
                }
            }
        } finally {
            endWrite();
        }
    }
    /**
     * Add this property to the in-memory Configuration.
     *
     * @param key The Key to add the property to.
     * @param token The Value to add.
     */
    @Override
    protected void addPropertyDirect(final String key, final Object token) {
        inMemoryConfiguration.addProperty(key, token);
    }
    /**
     * Adds the value of a property to the given list. This method is used by {@code getList()} for gathering property
     * values from the child configurations.
     *
     * @param dest the list for collecting the data
     * @param config the configuration to query
     * @param key the key of the property
     */
    private void appendListProperty(final List