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