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