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