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