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