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