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