    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 */
    017package org.apache.commons.configuration2.builder.combined;
    018
    019import java.net.URL;
    020import java.util.ArrayList;
    021import java.util.Collection;
    022import java.util.Collections;
    023import java.util.HashMap;
    024import java.util.Iterator;
    025import java.util.LinkedList;
    026import java.util.List;
    027import java.util.Map;
    028import java.util.Set;
    029
    030import org.apache.commons.configuration2.CombinedConfiguration;
    031import org.apache.commons.configuration2.Configuration;
    032import org.apache.commons.configuration2.ConfigurationLookup;
    033import org.apache.commons.configuration2.HierarchicalConfiguration;
    034import org.apache.commons.configuration2.SystemConfiguration;
    035import org.apache.commons.configuration2.XMLConfiguration;
    036import org.apache.commons.configuration2.beanutils.BeanDeclaration;
    037import org.apache.commons.configuration2.beanutils.BeanHelper;
    038import org.apache.commons.configuration2.beanutils.CombinedBeanDeclaration;
    039import org.apache.commons.configuration2.beanutils.XMLBeanDeclaration;
    040import org.apache.commons.configuration2.builder.BasicBuilderParameters;
    041import org.apache.commons.configuration2.builder.BasicConfigurationBuilder;
    042import org.apache.commons.configuration2.builder.BuilderParameters;
    043import org.apache.commons.configuration2.builder.ConfigurationBuilder;
    044import org.apache.commons.configuration2.builder.ConfigurationBuilderEvent;
    045import org.apache.commons.configuration2.builder.FileBasedBuilderParametersImpl;
    046import org.apache.commons.configuration2.builder.FileBasedBuilderProperties;
    047import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
    048import org.apache.commons.configuration2.builder.XMLBuilderParametersImpl;
    049import org.apache.commons.configuration2.builder.XMLBuilderProperties;
    050import org.apache.commons.configuration2.event.EventListener;
    051import org.apache.commons.configuration2.ex.ConfigurationException;
    052import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
    053import org.apache.commons.configuration2.interpol.Lookup;
    054import org.apache.commons.configuration2.io.FileSystem;
    055import org.apache.commons.configuration2.resolver.CatalogResolver;
    056import org.apache.commons.configuration2.tree.DefaultExpressionEngineSymbols;
    057import org.apache.commons.configuration2.tree.OverrideCombiner;
    058import org.apache.commons.configuration2.tree.UnionCombiner;
    059import org.xml.sax.EntityResolver;
    060
    061/**
    062 * <p>
    063 * A specialized {@code ConfigurationBuilder} implementation that creates a {@link CombinedConfiguration} from multiple
    064 * configuration sources defined by an XML-based <em>configuration definition file</em>.
    065 * </p>
    066 * <p>
    067 * This class provides an easy and flexible means for loading multiple configuration sources and combining the results
    068 * into a single configuration object. The sources to be loaded are defined in an XML document that can contain certain
    069 * tags representing the different supported configuration classes. If such a tag is found, a corresponding
    070 * {@code ConfigurationBuilder} class is instantiated and initialized using the classes of the {@code beanutils} package
    071 * (namely {@link org.apache.commons.configuration2.beanutils.XMLBeanDeclaration XMLBeanDeclaration} will be used to
    072 * extract the configuration's initialization parameters, which allows for complex initialization scenarios).
    073 * </p>
    074 * <p>
    075 * It is also possible to add custom tags to the configuration definition file. For this purpose an implementation of
    076 * {@link CombinedConfigurationBuilderProvider} has to be created which is responsible for the creation of a
    077 * {@code ConfigurationBuilder} associated with the custom tag. An instance of this class has to be registered at the
    078 * {@link CombinedBuilderParametersImpl} object which is used to initialize this {@code CombinedConfigurationBuilder}.
    079 * This provider will then be called when the corresponding custom tag is detected. For many default configuration
    080 * classes providers are already registered.
    081 * </p>
    082 * <p>
    083 * The configuration definition file has the following basic structure:
    084 * </p>
    085 *
    086 * <pre>
    087 * &lt;configuration systemProperties="properties file name"&gt;
    088 *   &lt;header&gt;
    089 *     &lt;!-- Optional meta information about the combined configuration --&gt;
    090 *   &lt;/header&gt;
    091 *   &lt;override&gt;
    092 *     &lt;!-- Declarations for override configurations --&gt;
    093 *   &lt;/override&gt;
    094 *   &lt;additional&gt;
    095 *     &lt;!-- Declarations for union configurations --&gt;
    096 *   &lt;/additional&gt;
    097 * &lt;/configuration&gt;
    098 * </pre>
    099 *
    100 * <p>
    101 * The name of the root element (here {@code configuration}) is arbitrary. The optional {@code systemProperties}
    102 * attribute identifies the path to a property file containing properties that should be added to the system properties.
    103 * If specified on the root element, the system properties are set before the rest of the configuration is processed.
    104 * </p>
    105 * <p>
    106 * There are two sections (both of them are optional) for declaring <em>override</em> and <em>additional</em>
    107 * configurations. Configurations in the former section are evaluated in the order of their declaration, and properties
    108 * of configurations declared earlier hide those of configurations declared later. Configurations in the latter section
    109 * are combined to a union configuration, i.e. all of their properties are added to a large hierarchical configuration.
    110 * Configuration declarations that occur as direct children of the root element are treated as override declarations.
    111 * </p>
    112 * <p>
    113 * Each configuration declaration consists of a tag whose name is associated with a
    114 * {@code CombinedConfigurationBuilderProvider}. This can be one of the predefined tags like {@code properties}, or
    115 * {@code xml}, or a custom tag, for which a configuration builder provider was registered (as described above).
    116 * Attributes and sub elements with specific initialization parameters can be added. There are some reserved attributes
    117 * with a special meaning that can be used in every configuration declaration:
    118 * </p>
    119 * <table border="1">
    120 * <caption>Standard attributes for configuration declarations</caption>
    121 * <tr>
    122 * <th>Attribute</th>
    123 * <th>Meaning</th>
    124 * </tr>
    125 * <tr>
    126 * <td valign="top">{@code config-name}</td>
    127 * <td>Allows specifying a name for this configuration. This name can be used to obtain a reference to the configuration
    128 * from the resulting combined configuration (see below). It can also be passed to the {@link #getNamedBuilder(String)}
    129 * method.</td>
    130 * </tr>
    131 * <tr>
    132 * <td valign="top">{@code config-at}</td>
    133 * <td>With this attribute an optional prefix can be specified for the properties of the corresponding
    134 * configuration.</td>
    135 * </tr>
    136 * <tr>
    137 * <td valign="top">{@code config-optional}</td>
    138 * <td>Declares a configuration source as optional. This means that errors that occur when creating the configuration
    139 * are ignored.</td>
    140 * </tr>
    141 * <tr>
    142 * <td valign="top">{@code config-reload}</td>
    143 * <td>Many configuration sources support a reloading mechanism. For those sources it is possible to enable reloading by
    144 * providing this attribute with a value of <strong>true</strong>.</td>
    145 * </tr>
    146 * </table>
    147 * <p>
    148 * The optional <em>header</em> section can contain some meta data about the created configuration itself. For instance,
    149 * it is possible to set further properties of the {@code NodeCombiner} objects used for constructing the resulting
    150 * configuration.
    151 * </p>
    152 * <p>
    153 * The default configuration object returned by this builder is an instance of the {@link CombinedConfiguration} class.
    154 * This allows for convenient access to the configuration objects maintained by the combined configuration (e.g. for
    155 * updates of single configuration objects). It has also the advantage that the properties stored in all declared
    156 * configuration objects are collected and transformed into a single hierarchical structure, which can be accessed using
    157 * different expression engines. The actual {@code CombinedConfiguration} implementation can be overridden by specifying
    158 * the class in the <em>config-class</em> attribute of the result element.
    159 * </p>
    160 * <p>
    161 * A custom EntityResolver can be used for all XMLConfigurations by adding
    162 * </p>
    163 *
    164 * <pre>
    165 * &lt;entity-resolver config-class="EntityResolver fully qualified class name"&gt;
    166 * </pre>
    167 *
    168 * <p>
    169 * A specific CatalogResolver can be specified for all XMLConfiguration sources by adding
    170 * </p>
    171 *
    172 * <pre>
    173 * &lt;entity-resolver catalogFiles="comma separated list of catalog files"&gt;
    174 * </pre>
    175 *
    176 * <p>
    177 * Additional ConfigurationProviders can be added by configuring them in the <em>header</em> section.
    178 * </p>
    179 *
    180 * <pre>
    181 * &lt;providers&gt;
    182 *   &lt;provider config-tag="tag name" config-class="provider fully qualified class name"/&gt;
    183 * &lt;/providers&gt;
    184 * </pre>
    185 *
    186 * <p>
    187 * Additional variable resolvers can be added by configuring them in the <em>header</em> section.
    188 * </p>
    189 *
    190 * <pre>
    191 * &lt;lookups&gt;
    192 *   &lt;lookup config-prefix="prefix" config-class="StrLookup fully qualified class name"/&gt;
    193 * &lt;/lookups&gt;
    194 * </pre>
    195 *
    196 * <p>
    197 * All declared override configurations are directly added to the resulting combined configuration. If they are given
    198 * names (using the {@code config-name} attribute), they can directly be accessed using the
    199 * {@code getConfiguration(String)} method of {@code CombinedConfiguration}. The additional configurations are
    200 * altogether added to another combined configuration, which uses a union combiner. Then this union configuration is
    201 * added to the resulting combined configuration under the name defined by the {@code ADDITIONAL_NAME} constant. The
    202 * {@link #getNamedBuilder(String)} method can be used to access the {@code ConfigurationBuilder} objects for all
    203 * configuration sources which have been assigned a name; care has to be taken that these names are unique.
    204 * </p>
    205 *
    206 * @since 1.3
    207 */
    208public class CombinedConfigurationBuilder extends BasicConfigurationBuilder<CombinedConfiguration> {
    209    /**
    210     * Constant for the name of the additional configuration. If the configuration definition file contains an
    211     * {@code additional} section, a special union configuration is created and added under this name to the resulting
    212     * combined configuration.
    213     */
    214    public static final String ADDITIONAL_NAME = CombinedConfigurationBuilder.class.getName() + "/ADDITIONAL_CONFIG";
    215
    216    /** Constant for the name of the configuration bean factory. */
    217    static final String CONFIG_BEAN_FACTORY_NAME = CombinedConfigurationBuilder.class.getName() + ".CONFIG_BEAN_FACTORY_NAME";
    218
    219    /** Constant for the reserved name attribute. */
    220    static final String ATTR_NAME = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START + XMLBeanDeclaration.RESERVED_PREFIX + "name"
    221        + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;
    222
    223    /** Constant for the name of the at attribute. */
    224    static final String ATTR_ATNAME = "at";
    225
    226    /** Constant for the reserved at attribute. */
    227    static final String ATTR_AT_RES = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START + XMLBeanDeclaration.RESERVED_PREFIX + ATTR_ATNAME
    228        + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;
    229
    230    /** Constant for the at attribute without the reserved prefix. */
    231    static final String ATTR_AT = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START + ATTR_ATNAME + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;
    232
    233    /** Constant for the name of the optional attribute. */
    234    static final String ATTR_OPTIONALNAME = "optional";
    235
    236    /** Constant for the reserved optional attribute. */
    237    static final String ATTR_OPTIONAL_RES = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START + XMLBeanDeclaration.RESERVED_PREFIX + ATTR_OPTIONALNAME
    238        + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;
    239
    240    /** Constant for the optional attribute without the reserved prefix. */
    241    static final String ATTR_OPTIONAL = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START + ATTR_OPTIONALNAME
    242        + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;
    243
    244    /** Constant for the forceCreate attribute. */
    245    static final String ATTR_FORCECREATE = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START + XMLBeanDeclaration.RESERVED_PREFIX + "forceCreate"
    246        + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;
    247
    248    /** Constant for the reload attribute. */
    249    static final String ATTR_RELOAD = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START + XMLBeanDeclaration.RESERVED_PREFIX + "reload"
    250        + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;
    251
    252    /**
    253     * Constant for the tag attribute for providers.
    254     */
    255    static final String KEY_SYSTEM_PROPS = "[@systemProperties]";
    256
    257    /** Constant for the name of the header section. */
    258    static final String SEC_HEADER = "header";
    259
    260    /** Constant for an expression that selects the union configurations. */
    261    static final String KEY_UNION = "additional";
    262
    263    /** An array with the names of top level configuration sections. */
    264    static final String[] CONFIG_SECTIONS = {"additional", "override", SEC_HEADER};
    265
    266    /**
    267     * Constant for an expression that selects override configurations in the override section.
    268     */
    269    static final String KEY_OVERRIDE = "override";
    270
    271    /**
    272     * Constant for the key that points to the list nodes definition of the override combiner.
    273     */
    274    static final String KEY_OVERRIDE_LIST = SEC_HEADER + ".combiner.override.list-nodes.node";
    275
    276    /**
    277     * Constant for the key that points to the list nodes definition of the additional combiner.
    278     */
    279    static final String KEY_ADDITIONAL_LIST = SEC_HEADER + ".combiner.additional.list-nodes.node";
    280
    281    /**
    282     * Constant for the key for defining providers in the configuration file.
    283     */
    284    static final String KEY_CONFIGURATION_PROVIDERS = SEC_HEADER + ".providers.provider";
    285
    286    /**
    287     * Constant for the tag attribute for providers.
    288     */
    289    static final String KEY_PROVIDER_KEY = XMLBeanDeclaration.ATTR_PREFIX + "tag]";
    290
    291    /**
    292     * Constant for the key for defining variable resolvers
    293     */
    294    static final String KEY_CONFIGURATION_LOOKUPS = SEC_HEADER + ".lookups.lookup";
    295
    296    /**
    297     * Constant for the key for defining entity resolvers
    298     */
    299    static final String KEY_ENTITY_RESOLVER = SEC_HEADER + ".entity-resolver";
    300
    301    /**
    302     * Constant for the prefix attribute for lookups.
    303     */
    304    static final String KEY_LOOKUP_KEY = XMLBeanDeclaration.ATTR_PREFIX + "prefix]";
    305
    306    /**
    307     * Constant for the FileSystem.
    308     */
    309    static final String FILE_SYSTEM = SEC_HEADER + ".fileSystem";
    310
    311    /**
    312     * Constant for the key of the result declaration. This key can point to a bean declaration, which defines properties of
    313     * the resulting combined configuration.
    314     */
    315    static final String KEY_RESULT = SEC_HEADER + ".result";
    316
    317    /** Constant for the key of the combiner in the result declaration. */
    318    static final String KEY_COMBINER = KEY_RESULT + ".nodeCombiner";
    319
    320    /** Constant for the XML file extension. */
    321    static final String EXT_XML = "xml";
    322
    323    /** Constant for the basic configuration builder class. */
    324    private static final String BASIC_BUILDER = "org.apache.commons.configuration2.builder.BasicConfigurationBuilder";
    325
    326    /** Constant for the file-based configuration builder class. */
    327    private static final String FILE_BUILDER = "org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder";
    328
    329    /** Constant for the reloading file-based configuration builder class. */
    330    private static final String RELOADING_BUILDER = "org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder";
    331
    332    /** Constant for the name of the file-based builder parameters class. */
    333    private static final String FILE_PARAMS = "org.apache.commons.configuration2.builder.FileBasedBuilderParametersImpl";
    334
    335    /** Constant for the provider for properties files. */
    336    private static final ConfigurationBuilderProvider PROPERTIES_PROVIDER = new FileExtensionConfigurationBuilderProvider(FILE_BUILDER, RELOADING_BUILDER,
    337        "org.apache.commons.configuration2.XMLPropertiesConfiguration", "org.apache.commons.configuration2.PropertiesConfiguration", EXT_XML,
    338        Collections.singletonList(FILE_PARAMS));
    339
    340    /** Constant for the provider for XML files. */
    341    private static final ConfigurationBuilderProvider XML_PROVIDER = new BaseConfigurationBuilderProvider(FILE_BUILDER, RELOADING_BUILDER,
    342        "org.apache.commons.configuration2.XMLConfiguration", Collections.singletonList("org.apache.commons.configuration2.builder.XMLBuilderParametersImpl"));
    343
    344    /** Constant for the provider for JNDI sources. */
    345    private static final BaseConfigurationBuilderProvider JNDI_PROVIDER = new BaseConfigurationBuilderProvider(BASIC_BUILDER, null,
    346        "org.apache.commons.configuration2.JNDIConfiguration",
    347        Collections.singletonList("org.apache.commons.configuration2.builder.JndiBuilderParametersImpl"));
    348
    349    /** Constant for the provider for system properties. */
    350    private static final BaseConfigurationBuilderProvider SYSTEM_PROVIDER = new BaseConfigurationBuilderProvider(BASIC_BUILDER, null,
    351        "org.apache.commons.configuration2.SystemConfiguration", Collections.singletonList("org.apache.commons.configuration2.builder.BasicBuilderParameters"));
    352
    353    /** Constant for the provider for ini files. */
    354    private static final BaseConfigurationBuilderProvider INI_PROVIDER = new BaseConfigurationBuilderProvider(FILE_BUILDER, RELOADING_BUILDER,
    355        "org.apache.commons.configuration2.INIConfiguration", Collections.singletonList(FILE_PARAMS));
    356
    357    /** Constant for the provider for environment properties. */
    358    private static final BaseConfigurationBuilderProvider ENV_PROVIDER = new BaseConfigurationBuilderProvider(BASIC_BUILDER, null,
    359        "org.apache.commons.configuration2.EnvironmentConfiguration",
    360        Collections.singletonList("org.apache.commons.configuration2.builder.BasicBuilderParameters"));
    361
    362    /** Constant for the provider for plist files. */
    363    private static final BaseConfigurationBuilderProvider PLIST_PROVIDER = new FileExtensionConfigurationBuilderProvider(FILE_BUILDER, RELOADING_BUILDER,
    364        "org.apache.commons.configuration2.plist.XMLPropertyListConfiguration", "org.apache.commons.configuration2.plist.PropertyListConfiguration", EXT_XML,
    365        Collections.singletonList(FILE_PARAMS));
    366
    367    /** Constant for the provider for configuration definition files. */
    368    private static final BaseConfigurationBuilderProvider COMBINED_PROVIDER = new CombinedConfigurationBuilderProvider();
    369
    370    /** Constant for the provider for multiple XML configurations. */
    371    private static final MultiFileConfigurationBuilderProvider MULTI_XML_PROVIDER = new MultiFileConfigurationBuilderProvider(
    372        "org.apache.commons.configuration2.XMLConfiguration", "org.apache.commons.configuration2.builder.XMLBuilderParametersImpl");
    373
    374    /** An array with the names of the default tags. */
    375    private static final String[] DEFAULT_TAGS = {"properties", "xml", "hierarchicalXml", "plist", "ini", "system", "env", "jndi", "configuration",
    376        "multiFile"};
    377
    378    /** An array with the providers for the default tags. */
    379    private static final ConfigurationBuilderProvider[] DEFAULT_PROVIDERS = {PROPERTIES_PROVIDER, XML_PROVIDER, XML_PROVIDER, PLIST_PROVIDER, INI_PROVIDER,
    380        SYSTEM_PROVIDER, ENV_PROVIDER, JNDI_PROVIDER, COMBINED_PROVIDER, MULTI_XML_PROVIDER};
    381
    382    /** A map with the default configuration builder providers. */
    383    private static final Map<String, ConfigurationBuilderProvider> DEFAULT_PROVIDERS_MAP;
    384
    385    /** The builder for the definition configuration. */
    386    private ConfigurationBuilder<? extends HierarchicalConfiguration<?>> definitionBuilder;
    387
    388    /** Stores temporarily the configuration with the builder definitions. */
    389    private HierarchicalConfiguration<?> definitionConfiguration;
    390
    391    /** The object with data about configuration sources. */
    392    private ConfigurationSourceData sourceData;
    393
    394    /** Stores the current parameters object. */
    395    private CombinedBuilderParametersImpl currentParameters;
    396
    397    /** The current XML parameters object. */
    398    private XMLBuilderParametersImpl currentXMLParameters;
    399
    400    /** The configuration that is currently constructed. */
    401    private CombinedConfiguration currentConfiguration;
    402
    403    /**
    404     * A {@code ConfigurationInterpolator} to be used as parent for all child configurations to enable cross-source
    405     * interpolation.
    406     */
    407    private ConfigurationInterpolator parentInterpolator;
    408
    409    /**
    410     * Creates a new instance of {@code CombinedConfigurationBuilder}. No parameters are set.
    411     */
    412    public CombinedConfigurationBuilder() {
    413        super(CombinedConfiguration.class);
    414    }
    415
    416    /**
    417     *
    418     * Creates a new instance of {@code CombinedConfigurationBuilder} and sets the specified initialization parameters.
    419     *
    420     * @param params a map with initialization parameters
    421     */
    422    public CombinedConfigurationBuilder(final Map<String, Object> params) {
    423        super(CombinedConfiguration.class, params);
    424    }
    425
    426    /**
    427     *
    428     * Creates a new instance of {@code CombinedConfigurationBuilder} and sets the specified initialization parameters and
    429     * the <em>allowFailOnInit</em> flag.
    430     *
    431     * @param params a map with initialization parameters
    432     * @param allowFailOnInit the <em>allowFailOnInit</em> flag
    433     */
    434    public CombinedConfigurationBuilder(final Map<String, Object> params, final boolean allowFailOnInit) {
    435        super(CombinedConfiguration.class, params, allowFailOnInit);
    436    }
    437
    438    /**
    439     * Returns the {@code ConfigurationBuilder} which creates the definition configuration.
    440     *
    441     * @return the builder for the definition configuration
    442     * @throws ConfigurationException if an error occurs
    443     */
    444    public synchronized ConfigurationBuilder<? extends HierarchicalConfiguration<?>> getDefinitionBuilder() throws ConfigurationException {
    445        if (definitionBuilder == null) {
    446            definitionBuilder = setupDefinitionBuilder(getParameters());
    447            addDefinitionBuilderChangeListener(definitionBuilder);
    448        }
    449        return definitionBuilder;
    450    }
    451
    452    /**
    453     * {@inheritDoc} This method is overridden to adapt the return type.
    454     */
    455    @Override
    456    public CombinedConfigurationBuilder configure(final BuilderParameters... params) {
    457        super.configure(params);
    458        return this;
    459    }
    460
    461    /**
    462     * <p>
    463     * Returns the configuration builder with the given name. With this method a builder of a child configuration which was
    464     * given a name in the configuration definition file can be accessed directly.
    465     * </p>
    466     * <p>
    467     * <strong>Important note:</strong> This method only returns a meaningful result after the result configuration has been
    468     * created by calling {@code getConfiguration()}. If called before, always an exception is thrown.
    469     * </p>
    470     *
    471     * @param name the name of the builder in question
    472     * @return the child configuration builder with this name
    473     * @throws ConfigurationException if information about named builders is not yet available or no builder with this name
    474     *         exists
    475     */
    476    public synchronized ConfigurationBuilder<? extends Configuration> getNamedBuilder(final String name) throws ConfigurationException {
    477        if (sourceData == null) {
    478            throw new ConfigurationException("Information about child builders" + " has not been setup yet! Call getConfiguration() first.");
    479        }
    480        final ConfigurationBuilder<? extends Configuration> builder = sourceData.getNamedBuilder(name);
    481        if (builder == null) {
    482            throw new ConfigurationException("Builder cannot be resolved: " + name);
    483        }
    484        return builder;
    485    }
    486
    487    /**
    488     * <p>
    489     * Returns a set with the names of all child configuration builders. A tag defining a configuration source in the
    490     * configuration definition file can have the {@code config-name} attribute. If this attribute is present, the
    491     * corresponding builder is assigned this name and can be directly accessed through the {@link #getNamedBuilder(String)}
    492     * method. This method returns a collection with all available builder names.
    493     * </p>
    494     * <p>
    495     * <strong>Important note:</strong> This method only returns a meaningful result after the result configuration has been
    496     * created by calling {@code getConfiguration()}. If called before, always an empty set is returned.
    497     * </p>
    498     *
    499     * @return a set with the names of all builders
    500     */
    501    public synchronized Set<String> builderNames() {
    502        if (sourceData == null) {
    503            return Collections.emptySet();
    504        }
    505        return Collections.unmodifiableSet(sourceData.builderNames());
    506    }
    507
    508    /**
    509     * {@inheritDoc} This implementation resets some specific internal state of this builder.
    510     */
    511    @Override
    512    public synchronized void resetParameters() {
    513        super.resetParameters();
    514        definitionBuilder = null;
    515        definitionConfiguration = null;
    516        currentParameters = null;
    517        currentXMLParameters = null;
    518
    519        if (sourceData != null) {
    520            sourceData.cleanUp();
    521            sourceData = null;
    522        }
    523    }
    524
    525    /**
    526     * Obtains the {@code ConfigurationBuilder} object which provides access to the configuration containing the definition
    527     * of the combined configuration to create. If a definition builder is defined in the parameters, it is used. Otherwise,
    528     * we check whether the combined builder parameters object contains a parameters object for the definition builder. If
    529     * this is the case, a builder for an {@code XMLConfiguration} is created and configured with this object. As a last
    530     * resort, it is looked for a {@link FileBasedBuilderParametersImpl} object in the properties. If found, also a XML
    531     * configuration builder is created which loads this file. Note: This method is called from a synchronized block.
    532     *
    533     * @param params the current parameters for this builder
    534     * @return the builder for the definition configuration
    535     * @throws ConfigurationException if an error occurs
    536     */
    537    protected ConfigurationBuilder<? extends HierarchicalConfiguration<?>> setupDefinitionBuilder(final Map<String, Object> params)
    538        throws ConfigurationException {
    539        final CombinedBuilderParametersImpl cbParams = CombinedBuilderParametersImpl.fromParameters(params);
    540        if (cbParams != null) {
    541            final ConfigurationBuilder<? extends HierarchicalConfiguration<?>> defBuilder = cbParams.getDefinitionBuilder();
    542            if (defBuilder != null) {
    543                return defBuilder;
    544            }
    545
    546            if (cbParams.getDefinitionBuilderParameters() != null) {
    547                return createXMLDefinitionBuilder(cbParams.getDefinitionBuilderParameters());
    548            }
    549        }
    550
    551        final BuilderParameters fileParams = FileBasedBuilderParametersImpl.fromParameters(params);
    552        if (fileParams != null) {
    553            return createXMLDefinitionBuilder(fileParams);
    554        }
    555
    556        throw new ConfigurationException("No builder for configuration definition specified!");
    557    }
    558
    559    /**
    560     * Creates a default builder for the definition configuration and initializes it with a parameters object. This method
    561     * is called if no definition builder is defined in this builder's parameters. This implementation creates a default
    562     * file-based builder which produces an {@code XMLConfiguration}; it expects a corresponding file specification. Note:
    563     * This method is called in a synchronized block.
    564     *
    565     * @param builderParams the parameters object for the builder
    566     * @return the standard builder for the definition configuration
    567     */
    568    protected ConfigurationBuilder<? extends HierarchicalConfiguration<?>> createXMLDefinitionBuilder(final BuilderParameters builderParams) {
    569        return new FileBasedConfigurationBuilder<>(XMLConfiguration.class).configure(builderParams);
    570    }
    571
    572    /**
    573     * Returns the configuration containing the definition of the combined configuration to be created. This method only
    574     * returns a defined result during construction of the result configuration. The definition configuration is obtained
    575     * from the definition builder at first access and then stored temporarily to ensure that during result construction
    576     * always the same configuration instance is used. (Otherwise, it would be possible that the definition builder returns
    577     * a different instance when queried multiple times.)
    578     *
    579     * @return the definition configuration
    580     * @throws ConfigurationException if an error occurs
    581     */
    582    protected HierarchicalConfiguration<?> getDefinitionConfiguration() throws ConfigurationException {
    583        if (definitionConfiguration == null) {
    584            definitionConfiguration = getDefinitionBuilder().getConfiguration();
    585        }
    586        return definitionConfiguration;
    587    }
    588
    589    /**
    590     * Returns a collection with the builders for all child configuration sources. This method can be used by derived
    591     * classes providing additional functionality on top of the declared configuration sources. It only returns a defined
    592     * value during construction of the result configuration instance.
    593     *
    594     * @return a collection with the builders for child configuration sources
    595     */
    596    protected synchronized Collection<ConfigurationBuilder<? extends Configuration>> getChildBuilders() {
    597        return sourceData.getChildBuilders();
    598    }
    599
    600    /**
    601     * {@inheritDoc} This implementation evaluates the {@code result} property of the definition configuration. It creates a
    602     * combined bean declaration with both the properties specified in the definition file and the properties defined as
    603     * initialization parameters.
    604     */
    605    @Override
    606    protected BeanDeclaration createResultDeclaration(final Map<String, Object> params) throws ConfigurationException {
    607        final BeanDeclaration paramsDecl = super.createResultDeclaration(params);
    608        final XMLBeanDeclaration resultDecl = new XMLBeanDeclaration(getDefinitionConfiguration(), KEY_RESULT, true, CombinedConfiguration.class.getName());
    609        return new CombinedBeanDeclaration(resultDecl, paramsDecl);
    610    }
    611
    612    /**
    613     * {@inheritDoc} This implementation processes the definition configuration in order to
    614     * <ul>
    615     * <li>initialize the resulting {@code CombinedConfiguration}</li>
    616     * <li>determine the builders for all configuration sources</li>
    617     * <li>populate the resulting {@code CombinedConfiguration}</li>
    618     * </ul>
    619     */
    620    @Override
    621    protected void initResultInstance(final CombinedConfiguration result) throws ConfigurationException {
    622        super.initResultInstance(result);
    623
    624        currentConfiguration = result;
    625        final HierarchicalConfiguration<?> config = getDefinitionConfiguration();
    626        if (config.getMaxIndex(KEY_COMBINER) < 0) {
    627            // No combiner defined => set default
    628            result.setNodeCombiner(new OverrideCombiner());
    629        }
    630
    631        setUpCurrentParameters();
    632        initNodeCombinerListNodes(result, config, KEY_OVERRIDE_LIST);
    633        registerConfiguredProviders(config);
    634        setUpCurrentXMLParameters();
    635        currentXMLParameters.setFileSystem(initFileSystem(config));
    636        initSystemProperties(config, getBasePath());
    637        registerConfiguredLookups(config, result);
    638        configureEntityResolver(config, currentXMLParameters);
    639        setUpParentInterpolator(currentConfiguration, config);
    640
    641        final ConfigurationSourceData data = getSourceData();
    642        final boolean createBuilders = data.getChildBuilders().isEmpty();
    643        final List<ConfigurationBuilder<? extends Configuration>> overrideBuilders = data.createAndAddConfigurations(result, data.getOverrideSources(),
    644            data.overrideBuilders);
    645        if (createBuilders) {
    646            data.overrideBuilders.addAll(overrideBuilders);
    647        }
    648        if (!data.getUnionSources().isEmpty()) {
    649            final CombinedConfiguration addConfig = createAdditionalsConfiguration(result);
    650            result.addConfiguration(addConfig, ADDITIONAL_NAME);
    651            initNodeCombinerListNodes(addConfig, config, KEY_ADDITIONAL_LIST);
    652            final List<ConfigurationBuilder<? extends Configuration>> unionBuilders = data.createAndAddConfigurations(addConfig, data.unionDeclarations,
    653                data.unionBuilders);
    654            if (createBuilders) {
    655                data.unionBuilders.addAll(unionBuilders);
    656            }
    657        }
    658
    659        result.isEmpty(); // this sets up the node structure
    660        currentConfiguration = null;
    661    }
    662
    663    /**
    664     * Creates the {@code CombinedConfiguration} for the configuration sources in the {@code &lt;additional&gt;} section.
    665     * This method is called when the builder constructs the final configuration. It creates a new
    666     * {@code CombinedConfiguration} and initializes some properties from the result configuration.
    667     *
    668     * @param resultConfig the result configuration (this is the configuration that will be returned by the builder)
    669     * @return the {@code CombinedConfiguration} for the additional configuration sources
    670     * @since 1.7
    671     */
    672    protected CombinedConfiguration createAdditionalsConfiguration(final CombinedConfiguration resultConfig) {
    673        final CombinedConfiguration addConfig = new CombinedConfiguration(new UnionCombiner());
    674        addConfig.setListDelimiterHandler(resultConfig.getListDelimiterHandler());
    675        return addConfig;
    676    }
    677
    678    /**
    679     * Processes custom {@link Lookup} objects that might be declared in the definition configuration. Each {@code Lookup}
    680     * object is registered at the definition configuration and at the result configuration. It is also added to all child
    681     * configurations added to the resulting combined configuration.
    682     *
    683     * @param defConfig the definition configuration
    684     * @param resultConfig the resulting configuration
    685     * @throws ConfigurationException if an error occurs
    686     */
    687    protected void registerConfiguredLookups(final HierarchicalConfiguration<?> defConfig, final Configuration resultConfig) throws ConfigurationException {
    688        final Map<String, Lookup> lookups = new HashMap<>();
    689
    690        final List<? extends HierarchicalConfiguration<?>> nodes = defConfig.configurationsAt(KEY_CONFIGURATION_LOOKUPS);
    691        for (final HierarchicalConfiguration<?> config : nodes) {
    692            final XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
    693            final String key = config.getString(KEY_LOOKUP_KEY);
    694            final Lookup lookup = (Lookup) fetchBeanHelper().createBean(decl);
    695            lookups.put(key, lookup);
    696        }
    697
    698        if (!lookups.isEmpty()) {
    699            final ConfigurationInterpolator defCI = defConfig.getInterpolator();
    700            if (defCI != null) {
    701                defCI.registerLookups(lookups);
    702            }
    703            resultConfig.getInterpolator().registerLookups(lookups);
    704        }
    705    }
    706
    707    /**
    708     * Creates and initializes a default {@code FileSystem} if the definition configuration contains a corresponding
    709     * declaration. The file system returned by this method is used as default for all file-based child configuration
    710     * sources.
    711     *
    712     * @param config the definition configuration
    713     * @return the default {@code FileSystem} (may be <b>null</b>)
    714     * @throws ConfigurationException if an error occurs
    715     */
    716    protected FileSystem initFileSystem(final HierarchicalConfiguration<?> config) throws ConfigurationException {
    717        if (config.getMaxIndex(FILE_SYSTEM) == 0) {
    718            final XMLBeanDeclaration decl = new XMLBeanDeclaration(config, FILE_SYSTEM);
    719            return (FileSystem) fetchBeanHelper().createBean(decl);
    720        }
    721        return null;
    722    }
    723
    724    /**
    725     * Handles a file with system properties that may be defined in the definition configuration. If such property file is
    726     * configured, all of its properties are added to the system properties.
    727     *
    728     * @param config the definition configuration
    729     * @param basePath the base path defined for this builder (may be <b>null</b>)
    730     * @throws ConfigurationException if an error occurs.
    731     */
    732    protected void initSystemProperties(final HierarchicalConfiguration<?> config, final String basePath) throws ConfigurationException {
    733        final String fileName = config.getString(KEY_SYSTEM_PROPS);
    734        if (fileName != null) {
    735            try {
    736                SystemConfiguration.setSystemProperties(basePath, fileName);
    737            } catch (final Exception ex) {
    738                throw new ConfigurationException("Error setting system properties from " + fileName, ex);
    739            }
    740        }
    741    }
    742
    743    /**
    744     * Creates and initializes a default {@code EntityResolver} if the definition configuration contains a corresponding
    745     * declaration.
    746     *
    747     * @param config the definition configuration
    748     * @param xmlParams the (already partly initialized) object with XML parameters; here the new resolver is to be stored
    749     * @throws ConfigurationException if an error occurs
    750     */
    751    protected void configureEntityResolver(final HierarchicalConfiguration<?> config, final XMLBuilderParametersImpl xmlParams) throws ConfigurationException {
    752        if (config.getMaxIndex(KEY_ENTITY_RESOLVER) == 0) {
    753            final XMLBeanDeclaration decl = new XMLBeanDeclaration(config, KEY_ENTITY_RESOLVER, true);
    754            final EntityResolver resolver = (EntityResolver) fetchBeanHelper().createBean(decl, CatalogResolver.class);
    755            final FileSystem fileSystem = xmlParams.getFileHandler().getFileSystem();
    756            if (fileSystem != null) {
    757                BeanHelper.setProperty(resolver, "fileSystem", fileSystem);
    758            }
    759            final String basePath = xmlParams.getFileHandler().getBasePath();
    760            if (basePath != null) {
    761                BeanHelper.setProperty(resolver, "baseDir", basePath);
    762            }
    763            final ConfigurationInterpolator ci = new ConfigurationInterpolator();
    764            ci.registerLookups(fetchPrefixLookups());
    765            BeanHelper.setProperty(resolver, "interpolator", ci);
    766
    767            xmlParams.setEntityResolver(resolver);
    768        }
    769    }
    770
    771    /**
    772     * Returns the {@code ConfigurationBuilderProvider} for the given tag. This method is called during creation of the
    773     * result configuration. (It is not allowed to call it at another point of time; result is then unpredictable!) It
    774     * supports all default providers and custom providers added through the parameters object as well.
    775     *
    776     * @param tagName the name of the tag
    777     * @return the provider that was registered for this tag or <b>null</b> if there is none
    778     */
    779    protected ConfigurationBuilderProvider providerForTag(final String tagName) {
    780        return currentParameters.providerForTag(tagName);
    781    }
    782
    783    /**
    784     * Initializes a parameters object for a child builder. This combined configuration builder has a bunch of properties
    785     * which may be inherited by child configurations, e.g. the base path, the file system, etc. While processing the
    786     * builders for child configurations, this method is called for each parameters object for a child builder. It
    787     * initializes some properties of the passed in parameters objects which are derived from this parent builder.
    788     *
    789     * @param params the parameters object to be initialized
    790     */
    791    protected void initChildBuilderParameters(final BuilderParameters params) {
    792        initDefaultChildParameters(params);
    793
    794        if (params instanceof BasicBuilderParameters) {
    795            initChildBasicParameters((BasicBuilderParameters) params);
    796        }
    797        if (params instanceof XMLBuilderProperties<?>) {
    798            initChildXMLParameters((XMLBuilderProperties<?>) params);
    799        }
    800        if (params instanceof FileBasedBuilderProperties<?>) {
    801            initChildFileBasedParameters((FileBasedBuilderProperties<?>) params);
    802        }
    803        if (params instanceof CombinedBuilderParametersImpl) {
    804            initChildCombinedParameters((CombinedBuilderParametersImpl) params);
    805        }
    806    }
    807
    808    /**
    809     * Initializes the event listeners of the specified builder from this object. This method is used to inherit all
    810     * listeners from a parent builder.
    811     *
    812     * @param dest the destination builder object which is to be initialized
    813     */
    814    void initChildEventListeners(final BasicConfigurationBuilder<? extends Configuration> dest) {
    815        copyEventListeners(dest);
    816    }
    817
    818    /**
    819     * Returns the configuration object that is currently constructed. This method can be called during construction of the
    820     * result configuration. It is intended for internal usage, e.g. some specialized builder providers need access to this
    821     * configuration to perform advanced initialization.
    822     *
    823     * @return the configuration that us currently under construction
    824     */
    825    CombinedConfiguration getConfigurationUnderConstruction() {
    826        return currentConfiguration;
    827    }
    828
    829    /**
    830     * Initializes a bean using the current {@code BeanHelper}. This is needed by builder providers when the configuration
    831     * objects for sub builders are constructed.
    832     *
    833     * @param bean the bean to be initialized
    834     * @param decl the {@code BeanDeclaration}
    835     */
    836    void initBean(final Object bean, final BeanDeclaration decl) {
    837        fetchBeanHelper().initBean(bean, decl);
    838    }
    839
    840    /**
    841     * Initializes the current parameters object. This object has either been passed at builder configuration time or it is
    842     * newly created. In any case, it is manipulated during result creation.
    843     */
    844    private void setUpCurrentParameters() {
    845        currentParameters = CombinedBuilderParametersImpl.fromParameters(getParameters(), true);
    846        currentParameters.registerMissingProviders(DEFAULT_PROVIDERS_MAP);
    847    }
    848
    849    /**
    850     * Sets up an XML parameters object which is used to store properties related to XML and file-based configurations
    851     * during creation of the result configuration. The properties stored in this object can be inherited to child
    852     * configurations.
    853     *
    854     * @throws ConfigurationException if an error occurs
    855     */
    856    private void setUpCurrentXMLParameters() throws ConfigurationException {
    857        currentXMLParameters = new XMLBuilderParametersImpl();
    858        initDefaultBasePath();
    859    }
    860
    861    /**
    862     * Sets up a parent {@code ConfigurationInterpolator} object. This object has a default {@link Lookup} querying the
    863     * resulting combined configuration. Thus interpolation works globally across all configuration sources.
    864     *
    865     * @param resultConfig the result configuration
    866     * @param defConfig the definition configuration
    867     */
    868    private void setUpParentInterpolator(final Configuration resultConfig, final Configuration defConfig) {
    869        parentInterpolator = new ConfigurationInterpolator();
    870        parentInterpolator.addDefaultLookup(new ConfigurationLookup(resultConfig));
    871        final ConfigurationInterpolator defInterpolator = defConfig.getInterpolator();
    872        if (defInterpolator != null) {
    873            defInterpolator.setParentInterpolator(parentInterpolator);
    874        }
    875    }
    876
    877    /**
    878     * Initializes the default base path for all file-based child configuration sources. The base path can be explicitly
    879     * defined in the parameters of this builder. Otherwise, if the definition builder is a file-based builder, it is
    880     * obtained from there.
    881     *
    882     * @throws ConfigurationException if an error occurs
    883     */
    884    private void initDefaultBasePath() throws ConfigurationException {
    885        assert currentParameters != null : "Current parameters undefined!";
    886        if (currentParameters.getBasePath() != null) {
    887            currentXMLParameters.setBasePath(currentParameters.getBasePath());
    888        } else {
    889            final ConfigurationBuilder<? extends HierarchicalConfiguration<?>> defBuilder = getDefinitionBuilder();
    890            if (defBuilder instanceof FileBasedConfigurationBuilder) {
    891                @SuppressWarnings("rawtypes")
    892                final FileBasedConfigurationBuilder fileBuilder = (FileBasedConfigurationBuilder) defBuilder;
    893                final URL url = fileBuilder.getFileHandler().getURL();
    894                currentXMLParameters.setBasePath(url != null ? url.toExternalForm() : fileBuilder.getFileHandler().getBasePath());
    895            }
    896        }
    897    }
    898
    899    /**
    900     * Executes the {@link org.apache.commons.configuration2.builder.DefaultParametersManager DefaultParametersManager}
    901     * stored in the current parameters on the passed in parameters object. If default handlers have been registered for
    902     * this type of parameters, an initialization is now performed. This method is called before the parameters object is
    903     * initialized from the configuration definition file. So default values can be overridden later with concrete property
    904     * definitions.
    905     *
    906     * @param params the parameters to be initialized
    907     * @throws org.apache.commons.configuration2.ex.ConfigurationRuntimeException if an error occurs when copying properties
    908     */
    909    private void initDefaultChildParameters(final BuilderParameters params) {
    910        currentParameters.getChildDefaultParametersManager().initializeParameters(params);
    911    }
    912
    913    /**
    914     * Initializes basic builder parameters for a child configuration with default settings set for this builder. This
    915     * implementation ensures that all {@code Lookup} objects are propagated to child configurations and interpolation is
    916     * setup correctly.
    917     *
    918     * @param params the parameters object
    919     */
    920    private void initChildBasicParameters(final BasicBuilderParameters params) {
    921        params.setPrefixLookups(fetchPrefixLookups());
    922        params.setParentInterpolator(parentInterpolator);
    923        if (currentParameters.isInheritSettings()) {
    924            params.inheritFrom(getParameters());
    925        }
    926    }
    927
    928    /**
    929     * Initializes a parameters object for a file-based configuration with properties already set for this parent builder.
    930     * This method handles properties like a default file system or a base path.
    931     *
    932     * @param params the parameters object
    933     */
    934    private void initChildFileBasedParameters(final FileBasedBuilderProperties<?> params) {
    935        params.setBasePath(getBasePath());
    936        params.setFileSystem(currentXMLParameters.getFileHandler().getFileSystem());
    937    }
    938
    939    /**
    940     * Initializes a parameters object for an XML configuration with properties already set for this parent builder.
    941     *
    942     * @param params the parameters object
    943     */
    944    private void initChildXMLParameters(final XMLBuilderProperties<?> params) {
    945        params.setEntityResolver(currentXMLParameters.getEntityResolver());
    946    }
    947
    948    /**
    949     * Initializes a parameters object for a combined configuration builder with properties already set for this parent
    950     * builder. This implementation deals only with a subset of properties. Other properties are already handled by the
    951     * specialized builder provider.
    952     *
    953     * @param params the parameters object
    954     */
    955    private void initChildCombinedParameters(final CombinedBuilderParametersImpl params) {
    956        params.registerMissingProviders(currentParameters);
    957        params.setBasePath(getBasePath());
    958    }
    959
    960    /**
    961     * Obtains the data object for the configuration sources and the corresponding builders. This object is created on first
    962     * access and reset when the definition builder sends a change event. This method is called in a synchronized block.
    963     *
    964     * @return the object with information about configuration sources
    965     * @throws ConfigurationException if an error occurs
    966     */
    967    private ConfigurationSourceData getSourceData() throws ConfigurationException {
    968        if (sourceData == null) {
    969            if (currentParameters == null) {
    970                setUpCurrentParameters();
    971                setUpCurrentXMLParameters();
    972            }
    973            sourceData = createSourceData();
    974        }
    975        return sourceData;
    976    }
    977
    978    /**
    979     * Creates the data object for configuration sources and the corresponding builders.
    980     *
    981     * @return the newly created data object
    982     * @throws ConfigurationException if an error occurs
    983     */
    984    private ConfigurationSourceData createSourceData() throws ConfigurationException {
    985        final ConfigurationSourceData result = new ConfigurationSourceData();
    986        result.initFromDefinitionConfiguration(getDefinitionConfiguration());
    987        return result;
    988    }
    989
    990    /**
    991     * Returns the current base path of this configuration builder. This is used for instance by all file-based child
    992     * configurations.
    993     *
    994     * @return the base path
    995     */
    996    private String getBasePath() {
    997        return currentXMLParameters.getFileHandler().getBasePath();
    998    }
    999
    1000    /**
    1001     * Registers providers defined in the configuration.
    1002     *
    1003     * @param defConfig the definition configuration
    1004     */
    1005    private void registerConfiguredProviders(final HierarchicalConfiguration<?> defConfig) {
    1006        final List<? extends HierarchicalConfiguration<?>> nodes = defConfig.configurationsAt(KEY_CONFIGURATION_PROVIDERS);
    1007        for (final HierarchicalConfiguration<?> config : nodes) {
    1008            final XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
    1009            final String key = config.getString(KEY_PROVIDER_KEY);
    1010            currentParameters.registerProvider(key, (ConfigurationBuilderProvider) fetchBeanHelper().createBean(decl));
    1011        }
    1012    }
    1013
    1014    /**
    1015     * Adds a listener at the given definition builder which resets this builder when a reset of the definition builder
    1016     * happens. This way it is ensured that this builder produces a new combined configuration when its definition
    1017     * configuration changes.
    1018     *
    1019     * @param defBuilder the definition builder
    1020     */
    1021    private void addDefinitionBuilderChangeListener(final ConfigurationBuilder<? extends HierarchicalConfiguration<?>> defBuilder) {
    1022        defBuilder.addEventListener(ConfigurationBuilderEvent.RESET, event -> {
    1023            synchronized (CombinedConfigurationBuilder.this) {
    1024                reset();
    1025                definitionBuilder = defBuilder;
    1026            }
    1027        });
    1028    }
    1029
    1030    /**
    1031     * Returns a map with the current prefix lookup objects. This map is obtained from the {@code ConfigurationInterpolator}
    1032     * of the configuration under construction.
    1033     *
    1034     * @return the map with current prefix lookups (may be <b>null</b>)
    1035     */
    1036    private Map<String, ? extends Lookup> fetchPrefixLookups() {
    1037        final CombinedConfiguration cc = getConfigurationUnderConstruction();
    1038        return cc != null ? cc.getInterpolator().getLookups() : null;
    1039    }
    1040
    1041    /**
    1042     * Creates {@code ConfigurationDeclaration} objects for the specified configurations.
    1043     *
    1044     * @param configs the list with configurations
    1045     * @return a collection with corresponding declarations
    1046     */
    1047    private Collection<ConfigurationDeclaration> createDeclarations(final Collection<? extends HierarchicalConfiguration<?>> configs) {
    1048        final Collection<ConfigurationDeclaration> declarations = new ArrayList<>(configs.size());
    1049        for (final HierarchicalConfiguration<?> c : configs) {
    1050            declarations.add(new ConfigurationDeclaration(this, c));
    1051        }
    1052        return declarations;
    1053    }
    1054
    1055    /**
    1056     * Initializes the list nodes of the node combiner for the given combined configuration. This information can be set in
    1057     * the header section of the configuration definition file for both the override and the union combiners.
    1058     *
    1059     * @param cc the combined configuration to initialize
    1060     * @param defConfig the definition configuration
    1061     * @param key the key for the list nodes
    1062     */
    1063    private static void initNodeCombinerListNodes(final CombinedConfiguration cc, final HierarchicalConfiguration<?> defConfig, final String key) {
    1064        final List<Object> listNodes = defConfig.getList(key);
    1065        for (final Object listNode : listNodes) {
    1066            cc.getNodeCombiner().addListNode((String) listNode);
    1067        }
    1068    }
    1069
    1070    /**
    1071     * Creates the map with the default configuration builder providers.
    1072     *
    1073     * @return the map with default providers
    1074     */
    1075    private static Map<String, ConfigurationBuilderProvider> createDefaultProviders() {
    1076        final Map<String, ConfigurationBuilderProvider> providers = new HashMap<>();
    1077        for (int i = 0; i < DEFAULT_TAGS.length; i++) {
    1078            providers.put(DEFAULT_TAGS[i], DEFAULT_PROVIDERS[i]);
    1079        }
    1080        return providers;
    1081    }
    1082
    1083    static {
    1084        DEFAULT_PROVIDERS_MAP = createDefaultProviders();
    1085    }
    1086
    1087    /**
    1088     * A data class for storing information about all configuration sources defined for a combined builder.
    1089     */
    1090    private class ConfigurationSourceData {
    1091        /** A list with data for all builders for override configurations. */
    1092        private final List<ConfigurationDeclaration> overrideDeclarations;
    1093
    1094        /** A list with data for all builders for union configurations. */
    1095        private final List<ConfigurationDeclaration> unionDeclarations;
    1096
    1097        /** A list with the builders for override configurations. */
    1098        private final List<ConfigurationBuilder<? extends Configuration>> overrideBuilders;
    1099
    1100        /** A list with the builders for union configurations. */
    1101        private final List<ConfigurationBuilder<? extends Configuration>> unionBuilders;
    1102
    1103        /** A map for direct access to a builder by its name. */
    1104        private final Map<String, ConfigurationBuilder<? extends Configuration>> namedBuilders;
    1105
    1106        /** A collection with all child builders. */
    1107        private final Collection<ConfigurationBuilder<? extends Configuration>> allBuilders;
    1108
    1109        /** A listener for reacting on changes of sub builders. */
    1110        private final EventListener<ConfigurationBuilderEvent> changeListener;
    1111
    1112        /**
    1113         * Creates a new instance of {@code ConfigurationSourceData}.
    1114         */
    1115        public ConfigurationSourceData() {
    1116            overrideDeclarations = new ArrayList<>();
    1117            unionDeclarations = new ArrayList<>();
    1118            overrideBuilders = new ArrayList<>();
    1119            unionBuilders = new ArrayList<>();
    1120            namedBuilders = new HashMap<>();
    1121            allBuilders = new LinkedList<>();
    1122            changeListener = createBuilderChangeListener();
    1123        }
    1124
    1125        /**
    1126         * Initializes this object from the specified definition configuration.
    1127         *
    1128         * @param config the definition configuration
    1129         * @throws ConfigurationException if an error occurs
    1130         */
    1131        public void initFromDefinitionConfiguration(final HierarchicalConfiguration<?> config) throws ConfigurationException {
    1132            overrideDeclarations.addAll(createDeclarations(fetchTopLevelOverrideConfigs(config)));
    1133            overrideDeclarations.addAll(createDeclarations(config.childConfigurationsAt(KEY_OVERRIDE)));
    1134            unionDeclarations.addAll(createDeclarations(config.childConfigurationsAt(KEY_UNION)));
    1135        }
    1136
    1137        /**
    1138         * Processes the declaration of configuration builder providers, creates the corresponding builder if necessary, obtains
    1139         * configurations, and adds them to the specified result configuration.
    1140         *
    1141         * @param ccResult the result configuration
    1142         * @param srcDecl the collection with the declarations of configuration sources to process
    1143         * @return a list with configuration builders
    1144         * @throws ConfigurationException if an error occurs
    1145         */
    1146        public List<ConfigurationBuilder<? extends Configuration>> createAndAddConfigurations(final CombinedConfiguration ccResult,
    1147            final List<ConfigurationDeclaration> srcDecl, final List<ConfigurationBuilder<? extends Configuration>> builders) throws ConfigurationException {
    1148            final boolean createBuilders = builders.isEmpty();
    1149            final List<ConfigurationBuilder<? extends Configuration>> newBuilders;
    1150            if (createBuilders) {
    1151                newBuilders = new ArrayList<>(srcDecl.size());
    1152            } else {
    1153                newBuilders = builders;
    1154            }
    1155
    1156            for (int i = 0; i < srcDecl.size(); i++) {
    1157                final ConfigurationBuilder<? extends Configuration> b;
    1158                if (createBuilders) {
    1159                    b = createConfigurationBuilder(srcDecl.get(i));
    1160                    newBuilders.add(b);
    1161                } else {
    1162                    b = builders.get(i);
    1163                }
    1164                addChildConfiguration(ccResult, srcDecl.get(i), b);
    1165            }
    1166
    1167            return newBuilders;
    1168        }
    1169
    1170        /**
    1171         * Frees resources used by this object and performs clean up. This method is called when the owning builder is reset.
    1172         */
    1173        public void cleanUp() {
    1174            for (final ConfigurationBuilder<?> b : getChildBuilders()) {
    1175                b.removeEventListener(ConfigurationBuilderEvent.RESET, changeListener);
    1176            }
    1177            namedBuilders.clear();
    1178        }
    1179
    1180        /**
    1181         * Returns a collection containing the builders for all child configuration sources.
    1182         *
    1183         * @return the child configuration builders
    1184         */
    1185        public Collection<ConfigurationBuilder<? extends Configuration>> getChildBuilders() {
    1186            return allBuilders;
    1187        }
    1188
    1189        /**
    1190         * Returns a collection with all configuration source declarations defined in the override section.
    1191         *
    1192         * @return the override configuration builders
    1193         */
    1194        public List<ConfigurationDeclaration> getOverrideSources() {
    1195            return overrideDeclarations;
    1196        }
    1197
    1198        /**
    1199         * Returns a collection with all configuration source declarations defined in the union section.
    1200         *
    1201         * @return the union configuration builders
    1202         */
    1203        public List<ConfigurationDeclaration> getUnionSources() {
    1204            return unionDeclarations;
    1205        }
    1206
    1207        /**
    1208         * Returns the {@code ConfigurationBuilder} with the given name. If no such builder is defined in the definition
    1209         * configuration, result is <b>null</b>.
    1210         *
    1211         * @param name the name of the builder in question
    1212         * @return the builder with this name or <b>null</b>
    1213         */
    1214        public ConfigurationBuilder<? extends Configuration> getNamedBuilder(final String name) {
    1215            return namedBuilders.get(name);
    1216        }
    1217
    1218        /**
    1219         * Returns a set with the names of all known named builders.
    1220         *
    1221         * @return the names of the available sub builders
    1222         */
    1223        public Set<String> builderNames() {
    1224            return namedBuilders.keySet();
    1225        }
    1226
    1227        /**
    1228         * Creates a configuration builder based on a source declaration in the definition configuration.
    1229         *
    1230         * @param decl the current {@code ConfigurationDeclaration}
    1231         * @return the newly created builder
    1232         * @throws ConfigurationException if an error occurs
    1233         */
    1234        private ConfigurationBuilder<? extends Configuration> createConfigurationBuilder(final ConfigurationDeclaration decl) throws ConfigurationException {
    1235            final ConfigurationBuilderProvider provider = providerForTag(decl.getConfiguration().getRootElementName());
    1236            if (provider == null) {
    1237                throw new ConfigurationException("Unsupported configuration source: " + decl.getConfiguration().getRootElementName());
    1238            }
    1239
    1240            final ConfigurationBuilder<? extends Configuration> builder = provider.getConfigurationBuilder(decl);
    1241            if (decl.getName() != null) {
    1242                namedBuilders.put(decl.getName(), builder);
    1243            }
    1244            allBuilders.add(builder);
    1245            builder.addEventListener(ConfigurationBuilderEvent.RESET, changeListener);
    1246            return builder;
    1247        }
    1248
    1249        /**
    1250         * Creates a new configuration using the specified builder and adds it to the resulting combined configuration.
    1251         *
    1252         * @param ccResult the resulting combined configuration
    1253         * @param decl the current {@code ConfigurationDeclaration}
    1254         * @param builder the configuration builder
    1255         * @throws ConfigurationException if an error occurs
    1256         */
    1257        private void addChildConfiguration(final CombinedConfiguration ccResult, final ConfigurationDeclaration decl,
    1258            final ConfigurationBuilder<? extends Configuration> builder) throws ConfigurationException {
    1259            try {
    1260                ccResult.addConfiguration(builder.getConfiguration(), decl.getName(), decl.getAt());
    1261            } catch (final ConfigurationException cex) {
    1262                // ignore exceptions for optional configurations
    1263                if (!decl.isOptional()) {
    1264                    throw cex;
    1265                }
    1266            }
    1267        }
    1268
    1269        /**
    1270         * Creates a listener for builder change events. This listener is registered at all builders for child configurations.
    1271         */
    1272        private EventListener<ConfigurationBuilderEvent> createBuilderChangeListener() {
    1273            return event -> resetResult();
    1274        }
    1275
    1276        /**
    1277         * Finds the override configurations that are defined as top level elements in the configuration definition file. This
    1278         * method fetches the child elements of the root node and removes the nodes that represent other configuration sections.
    1279         * The remaining nodes are treated as definitions for override configurations.
    1280         *
    1281         * @param config the definition configuration
    1282         * @return a list with sub configurations for the top level override configurations
    1283         */
    1284        private List<? extends HierarchicalConfiguration<?>> fetchTopLevelOverrideConfigs(final HierarchicalConfiguration<?> config) {
    1285
    1286            final List<? extends HierarchicalConfiguration<?>> configs = config.childConfigurationsAt(null);
    1287            for (final Iterator<? extends HierarchicalConfiguration<?>> it = configs.iterator(); it.hasNext();) {
    1288                final String nodeName = it.next().getRootElementName();
    1289                for (final String element : CONFIG_SECTIONS) {
    1290                    if (element.equals(nodeName)) {
    1291                        it.remove();
    1292                        break;
    1293                    }
    1294                }
    1295            }
    1296            return configs;
    1297        }
    1298    }
    1299}