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