CombinedConfigurationBuilder.java

  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.  *     http://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. import java.net.URL;
  19. import java.util.ArrayList;
  20. import java.util.Collection;
  21. import java.util.Collections;
  22. import java.util.HashMap;
  23. import java.util.Iterator;
  24. import java.util.LinkedList;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.Set;
  28. import java.util.stream.Collectors;

  29. import org.apache.commons.configuration2.CombinedConfiguration;
  30. import org.apache.commons.configuration2.Configuration;
  31. import org.apache.commons.configuration2.ConfigurationLookup;
  32. import org.apache.commons.configuration2.HierarchicalConfiguration;
  33. import org.apache.commons.configuration2.SystemConfiguration;
  34. import org.apache.commons.configuration2.XMLConfiguration;
  35. import org.apache.commons.configuration2.beanutils.BeanDeclaration;
  36. import org.apache.commons.configuration2.beanutils.BeanHelper;
  37. import org.apache.commons.configuration2.beanutils.CombinedBeanDeclaration;
  38. import org.apache.commons.configuration2.beanutils.XMLBeanDeclaration;
  39. import org.apache.commons.configuration2.builder.BasicBuilderParameters;
  40. import org.apache.commons.configuration2.builder.BasicConfigurationBuilder;
  41. import org.apache.commons.configuration2.builder.BuilderParameters;
  42. import org.apache.commons.configuration2.builder.ConfigurationBuilder;
  43. import org.apache.commons.configuration2.builder.ConfigurationBuilderEvent;
  44. import org.apache.commons.configuration2.builder.FileBasedBuilderParametersImpl;
  45. import org.apache.commons.configuration2.builder.FileBasedBuilderProperties;
  46. import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
  47. import org.apache.commons.configuration2.builder.XMLBuilderParametersImpl;
  48. import org.apache.commons.configuration2.builder.XMLBuilderProperties;
  49. import org.apache.commons.configuration2.event.EventListener;
  50. import org.apache.commons.configuration2.ex.ConfigurationException;
  51. import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
  52. import org.apache.commons.configuration2.interpol.Lookup;
  53. import org.apache.commons.configuration2.io.FileSystem;
  54. import org.apache.commons.configuration2.resolver.CatalogResolver;
  55. import org.apache.commons.configuration2.tree.DefaultExpressionEngineSymbols;
  56. import org.apache.commons.configuration2.tree.OverrideCombiner;
  57. import org.apache.commons.configuration2.tree.UnionCombiner;
  58. import org.xml.sax.EntityResolver;

  59. /**
  60.  * <p>
  61.  * A specialized {@code ConfigurationBuilder} implementation that creates a {@link CombinedConfiguration} from multiple
  62.  * configuration sources defined by an XML-based <em>configuration definition file</em>.
  63.  * </p>
  64.  * <p>
  65.  * This class provides an easy and flexible means for loading multiple configuration sources and combining the results
  66.  * into a single configuration object. The sources to be loaded are defined in an XML document that can contain certain
  67.  * tags representing the different supported configuration classes. If such a tag is found, a corresponding
  68.  * {@code ConfigurationBuilder} class is instantiated and initialized using the classes of the {@code beanutils} package
  69.  * (namely {@link org.apache.commons.configuration2.beanutils.XMLBeanDeclaration XMLBeanDeclaration} will be used to
  70.  * extract the configuration's initialization parameters, which allows for complex initialization scenarios).
  71.  * </p>
  72.  * <p>
  73.  * It is also possible to add custom tags to the configuration definition file. For this purpose an implementation of
  74.  * {@link CombinedConfigurationBuilderProvider} has to be created which is responsible for the creation of a
  75.  * {@code ConfigurationBuilder} associated with the custom tag. An instance of this class has to be registered at the
  76.  * {@link CombinedBuilderParametersImpl} object which is used to initialize this {@code CombinedConfigurationBuilder}.
  77.  * This provider will then be called when the corresponding custom tag is detected. For many default configuration
  78.  * classes providers are already registered.
  79.  * </p>
  80.  * <p>
  81.  * The configuration definition file has the following basic structure:
  82.  * </p>
  83.  *
  84.  * <pre>
  85.  * &lt;configuration systemProperties="properties file name"&gt;
  86.  *   &lt;header&gt;
  87.  *     &lt;!-- Optional meta information about the combined configuration --&gt;
  88.  *   &lt;/header&gt;
  89.  *   &lt;override&gt;
  90.  *     &lt;!-- Declarations for override configurations --&gt;
  91.  *   &lt;/override&gt;
  92.  *   &lt;additional&gt;
  93.  *     &lt;!-- Declarations for union configurations --&gt;
  94.  *   &lt;/additional&gt;
  95.  * &lt;/configuration&gt;
  96.  * </pre>
  97.  *
  98.  * <p>
  99.  * The name of the root element (here {@code configuration}) is arbitrary. The optional {@code systemProperties}
  100.  * attribute identifies the path to a property file containing properties that should be added to the system properties.
  101.  * If specified on the root element, the system properties are set before the rest of the configuration is processed.
  102.  * </p>
  103.  * <p>
  104.  * There are two sections (both of them are optional) for declaring <em>override</em> and <em>additional</em>
  105.  * configurations. Configurations in the former section are evaluated in the order of their declaration, and properties
  106.  * of configurations declared earlier hide those of configurations declared later. Configurations in the latter section
  107.  * are combined to a union configuration, i.e. all of their properties are added to a large hierarchical configuration.
  108.  * Configuration declarations that occur as direct children of the root element are treated as override declarations.
  109.  * </p>
  110.  * <p>
  111.  * Each configuration declaration consists of a tag whose name is associated with a
  112.  * {@code CombinedConfigurationBuilderProvider}. This can be one of the predefined tags like {@code properties}, or
  113.  * {@code xml}, or a custom tag, for which a configuration builder provider was registered (as described above).
  114.  * Attributes and sub elements with specific initialization parameters can be added. There are some reserved attributes
  115.  * with a special meaning that can be used in every configuration declaration:
  116.  * </p>
  117.  * <table border="1">
  118.  * <caption>Standard attributes for configuration declarations</caption>
  119.  * <tr>
  120.  * <th>Attribute</th>
  121.  * <th>Meaning</th>
  122.  * </tr>
  123.  * <tr>
  124.  * <td>{@code config-name}</td>
  125.  * <td>Allows specifying a name for this configuration. This name can be used to obtain a reference to the configuration
  126.  * from the resulting combined configuration (see below). It can also be passed to the {@link #getNamedBuilder(String)}
  127.  * method.</td>
  128.  * </tr>
  129.  * <tr>
  130.  * <td>{@code config-at}</td>
  131.  * <td>With this attribute an optional prefix can be specified for the properties of the corresponding
  132.  * configuration.</td>
  133.  * </tr>
  134.  * <tr>
  135.  * <td>{@code config-optional}</td>
  136.  * <td>Declares a configuration source as optional. This means that errors that occur when creating the configuration
  137.  * are ignored.</td>
  138.  * </tr>
  139.  * <tr>
  140.  * <td>{@code config-reload}</td>
  141.  * <td>Many configuration sources support a reloading mechanism. For those sources it is possible to enable reloading by
  142.  * providing this attribute with a value of <strong>true</strong>.</td>
  143.  * </tr>
  144.  * </table>
  145.  * <p>
  146.  * The optional <em>header</em> section can contain some meta data about the created configuration itself. For instance,
  147.  * it is possible to set further properties of the {@code NodeCombiner} objects used for constructing the resulting
  148.  * configuration.
  149.  * </p>
  150.  * <p>
  151.  * The default configuration object returned by this builder is an instance of the {@link CombinedConfiguration} class.
  152.  * This allows for convenient access to the configuration objects maintained by the combined configuration (for example for
  153.  * updates of single configuration objects). It has also the advantage that the properties stored in all declared
  154.  * configuration objects are collected and transformed into a single hierarchical structure, which can be accessed using
  155.  * different expression engines. The actual {@code CombinedConfiguration} implementation can be overridden by specifying
  156.  * the class in the <em>config-class</em> attribute of the result element.
  157.  * </p>
  158.  * <p>
  159.  * A custom EntityResolver can be used for all XMLConfigurations by adding
  160.  * </p>
  161.  *
  162.  * <pre>
  163.  * &lt;entity-resolver config-class="EntityResolver fully qualified class name"&gt;
  164.  * </pre>
  165.  *
  166.  * <p>
  167.  * A specific CatalogResolver can be specified for all XMLConfiguration sources by adding
  168.  * </p>
  169.  *
  170.  * <pre>
  171.  * &lt;entity-resolver catalogFiles="comma separated list of catalog files"&gt;
  172.  * </pre>
  173.  *
  174.  * <p>
  175.  * Additional ConfigurationProviders can be added by configuring them in the <em>header</em> section.
  176.  * </p>
  177.  *
  178.  * <pre>
  179.  * &lt;providers&gt;
  180.  *   &lt;provider config-tag="tag name" config-class="provider fully qualified class name"/&gt;
  181.  * &lt;/providers&gt;
  182.  * </pre>
  183.  *
  184.  * <p>
  185.  * Additional variable resolvers can be added by configuring them in the <em>header</em> section.
  186.  * </p>
  187.  *
  188.  * <pre>
  189.  * &lt;lookups&gt;
  190.  *   &lt;lookup config-prefix="prefix" config-class="StrLookup fully qualified class name"/&gt;
  191.  * &lt;/lookups&gt;
  192.  * </pre>
  193.  *
  194.  * <p>
  195.  * All declared override configurations are directly added to the resulting combined configuration. If they are given
  196.  * names (using the {@code config-name} attribute), they can directly be accessed using the
  197.  * {@code getConfiguration(String)} method of {@code CombinedConfiguration}. The additional configurations are
  198.  * altogether added to another combined configuration, which uses a union combiner. Then this union configuration is
  199.  * added to the resulting combined configuration under the name defined by the {@code ADDITIONAL_NAME} constant. The
  200.  * {@link #getNamedBuilder(String)} method can be used to access the {@code ConfigurationBuilder} objects for all
  201.  * configuration sources which have been assigned a name; care has to be taken that these names are unique.
  202.  * </p>
  203.  *
  204.  * @since 1.3
  205.  */
  206. public class CombinedConfigurationBuilder extends BasicConfigurationBuilder<CombinedConfiguration> {
  207.     /**
  208.      * A data class for storing information about all configuration sources defined for a combined builder.
  209.      */
  210.     private final class ConfigurationSourceData {
  211.         /** A list with data for all builders for override configurations. */
  212.         private final List<ConfigurationDeclaration> overrideDeclarations;

  213.         /** A list with data for all builders for union configurations. */
  214.         private final List<ConfigurationDeclaration> unionDeclarations;

  215.         /** A list with the builders for override configurations. */
  216.         private final List<ConfigurationBuilder<? extends Configuration>> overrideBuilders;

  217.         /** A list with the builders for union configurations. */
  218.         private final List<ConfigurationBuilder<? extends Configuration>> unionBuilders;

  219.         /** A map for direct access to a builder by its name. */
  220.         private final Map<String, ConfigurationBuilder<? extends Configuration>> namedBuilders;

  221.         /** A collection with all child builders. */
  222.         private final Collection<ConfigurationBuilder<? extends Configuration>> allBuilders;

  223.         /** A listener for reacting on changes of sub builders. */
  224.         private final EventListener<ConfigurationBuilderEvent> changeListener;

  225.         /**
  226.          * Creates a new instance of {@code ConfigurationSourceData}.
  227.          */
  228.         public ConfigurationSourceData() {
  229.             overrideDeclarations = new ArrayList<>();
  230.             unionDeclarations = new ArrayList<>();
  231.             overrideBuilders = new ArrayList<>();
  232.             unionBuilders = new ArrayList<>();
  233.             namedBuilders = new HashMap<>();
  234.             allBuilders = new LinkedList<>();
  235.             changeListener = createBuilderChangeListener();
  236.         }

  237.         /**
  238.          * Creates a new configuration using the specified builder and adds it to the resulting combined configuration.
  239.          *
  240.          * @param ccResult the resulting combined configuration
  241.          * @param decl the current {@code ConfigurationDeclaration}
  242.          * @param builder the configuration builder
  243.          * @throws ConfigurationException if an error occurs
  244.          */
  245.         private void addChildConfiguration(final CombinedConfiguration ccResult, final ConfigurationDeclaration decl,
  246.             final ConfigurationBuilder<? extends Configuration> builder) throws ConfigurationException {
  247.             try {
  248.                 ccResult.addConfiguration(builder.getConfiguration(), decl.getName(), decl.getAt());
  249.             } catch (final ConfigurationException cex) {
  250.                 // ignore exceptions for optional configurations
  251.                 if (!decl.isOptional()) {
  252.                     throw cex;
  253.                 }
  254.             }
  255.         }

  256.         /**
  257.          * Returns a set with the names of all known named builders.
  258.          *
  259.          * @return the names of the available sub builders
  260.          */
  261.         public Set<String> builderNames() {
  262.             return namedBuilders.keySet();
  263.         }

  264.         /**
  265.          * Frees resources used by this object and performs clean up. This method is called when the owning builder is reset.
  266.          */
  267.         public void cleanUp() {
  268.             getChildBuilders().forEach(b -> b.removeEventListener(ConfigurationBuilderEvent.RESET, changeListener));
  269.             namedBuilders.clear();
  270.         }

  271.         /**
  272.          * Processes the declaration of configuration builder providers, creates the corresponding builder if necessary, obtains
  273.          * configurations, and adds them to the specified result configuration.
  274.          *
  275.          * @param ccResult the result configuration.
  276.          * @param srcDecl the collection with the declarations of configuration sources to process.
  277.          * @param builders List of configuration builders.
  278.          * @return a list with configuration builders.
  279.          * @throws ConfigurationException if an error occurs.
  280.          */
  281.         public List<ConfigurationBuilder<? extends Configuration>> createAndAddConfigurations(final CombinedConfiguration ccResult,
  282.             final List<ConfigurationDeclaration> srcDecl, final List<ConfigurationBuilder<? extends Configuration>> builders) throws ConfigurationException {
  283.             final boolean createBuilders = builders.isEmpty();
  284.             final List<ConfigurationBuilder<? extends Configuration>> newBuilders;
  285.             if (createBuilders) {
  286.                 newBuilders = new ArrayList<>(srcDecl.size());
  287.             } else {
  288.                 newBuilders = builders;
  289.             }

  290.             for (int i = 0; i < srcDecl.size(); i++) {
  291.                 final ConfigurationBuilder<? extends Configuration> b;
  292.                 if (createBuilders) {
  293.                     b = createConfigurationBuilder(srcDecl.get(i));
  294.                     newBuilders.add(b);
  295.                 } else {
  296.                     b = builders.get(i);
  297.                 }
  298.                 addChildConfiguration(ccResult, srcDecl.get(i), b);
  299.             }

  300.             return newBuilders;
  301.         }

  302.         /**
  303.          * Creates a listener for builder change events. This listener is registered at all builders for child configurations.
  304.          */
  305.         private EventListener<ConfigurationBuilderEvent> createBuilderChangeListener() {
  306.             return event -> resetResult();
  307.         }

  308.         /**
  309.          * Creates a configuration builder based on a source declaration in the definition configuration.
  310.          *
  311.          * @param decl the current {@code ConfigurationDeclaration}
  312.          * @return the newly created builder
  313.          * @throws ConfigurationException if an error occurs
  314.          */
  315.         private ConfigurationBuilder<? extends Configuration> createConfigurationBuilder(final ConfigurationDeclaration decl) throws ConfigurationException {
  316.             final ConfigurationBuilderProvider provider = providerForTag(decl.getConfiguration().getRootElementName());
  317.             if (provider == null) {
  318.                 throw new ConfigurationException("Unsupported configuration source: " + decl.getConfiguration().getRootElementName());
  319.             }

  320.             final ConfigurationBuilder<? extends Configuration> builder = provider.getConfigurationBuilder(decl);
  321.             if (decl.getName() != null) {
  322.                 namedBuilders.put(decl.getName(), builder);
  323.             }
  324.             allBuilders.add(builder);
  325.             builder.addEventListener(ConfigurationBuilderEvent.RESET, changeListener);
  326.             return builder;
  327.         }

  328.         /**
  329.          * Finds the override configurations that are defined as top level elements in the configuration definition file. This
  330.          * method fetches the child elements of the root node and removes the nodes that represent other configuration sections.
  331.          * The remaining nodes are treated as definitions for override configurations.
  332.          *
  333.          * @param config the definition configuration
  334.          * @return a list with sub configurations for the top level override configurations
  335.          */
  336.         private List<? extends HierarchicalConfiguration<?>> fetchTopLevelOverrideConfigs(final HierarchicalConfiguration<?> config) {

  337.             final List<? extends HierarchicalConfiguration<?>> configs = config.childConfigurationsAt(null);
  338.             for (final Iterator<? extends HierarchicalConfiguration<?>> it = configs.iterator(); it.hasNext();) {
  339.                 final String nodeName = it.next().getRootElementName();
  340.                 for (final String element : CONFIG_SECTIONS) {
  341.                     if (element.equals(nodeName)) {
  342.                         it.remove();
  343.                         break;
  344.                     }
  345.                 }
  346.             }
  347.             return configs;
  348.         }

  349.         /**
  350.          * Gets a collection containing the builders for all child configuration sources.
  351.          *
  352.          * @return the child configuration builders
  353.          */
  354.         public Collection<ConfigurationBuilder<? extends Configuration>> getChildBuilders() {
  355.             return allBuilders;
  356.         }

  357.         /**
  358.          * Gets the {@code ConfigurationBuilder} with the given name. If no such builder is defined in the definition
  359.          * configuration, result is <strong>null</strong>.
  360.          *
  361.          * @param name the name of the builder in question
  362.          * @return the builder with this name or <strong>null</strong>
  363.          */
  364.         public ConfigurationBuilder<? extends Configuration> getNamedBuilder(final String name) {
  365.             return namedBuilders.get(name);
  366.         }

  367.         /**
  368.          * Gets a collection with all configuration source declarations defined in the override section.
  369.          *
  370.          * @return the override configuration builders
  371.          */
  372.         public List<ConfigurationDeclaration> getOverrideSources() {
  373.             return overrideDeclarations;
  374.         }

  375.         /**
  376.          * Gets a collection with all configuration source declarations defined in the union section.
  377.          *
  378.          * @return the union configuration builders
  379.          */
  380.         public List<ConfigurationDeclaration> getUnionSources() {
  381.             return unionDeclarations;
  382.         }

  383.         /**
  384.          * Initializes this object from the specified definition configuration.
  385.          *
  386.          * @param config the definition configuration
  387.          * @throws ConfigurationException if an error occurs
  388.          */
  389.         public void initFromDefinitionConfiguration(final HierarchicalConfiguration<?> config) throws ConfigurationException {
  390.             overrideDeclarations.addAll(createDeclarations(fetchTopLevelOverrideConfigs(config)));
  391.             overrideDeclarations.addAll(createDeclarations(config.childConfigurationsAt(KEY_OVERRIDE)));
  392.             unionDeclarations.addAll(createDeclarations(config.childConfigurationsAt(KEY_UNION)));
  393.         }
  394.     }

  395.     /**
  396.      * Constant for the name of the additional configuration. If the configuration definition file contains an
  397.      * {@code additional} section, a special union configuration is created and added under this name to the resulting
  398.      * combined configuration.
  399.      */
  400.     public static final String ADDITIONAL_NAME = CombinedConfigurationBuilder.class.getName() + "/ADDITIONAL_CONFIG";

  401.     /** Constant for the name of the configuration bean factory. */
  402.     static final String CONFIG_BEAN_FACTORY_NAME = CombinedConfigurationBuilder.class.getName() + ".CONFIG_BEAN_FACTORY_NAME";

  403.     /** Constant for the reserved name attribute. */
  404.     static final String ATTR_NAME = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START + XMLBeanDeclaration.RESERVED_PREFIX + "name"
  405.         + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;

  406.     /** Constant for the name of the at attribute. */
  407.     static final String ATTR_ATNAME = "at";

  408.     /** Constant for the reserved at attribute. */
  409.     static final String ATTR_AT_RES = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START + XMLBeanDeclaration.RESERVED_PREFIX + ATTR_ATNAME
  410.         + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;

  411.     /** Constant for the at attribute without the reserved prefix. */
  412.     static final String ATTR_AT = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START + ATTR_ATNAME + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;

  413.     /** Constant for the name of the optional attribute. */
  414.     static final String ATTR_OPTIONALNAME = "optional";

  415.     /** Constant for the reserved optional attribute. */
  416.     static final String ATTR_OPTIONAL_RES = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START + XMLBeanDeclaration.RESERVED_PREFIX + ATTR_OPTIONALNAME
  417.         + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;

  418.     /** Constant for the optional attribute without the reserved prefix. */
  419.     static final String ATTR_OPTIONAL = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START + ATTR_OPTIONALNAME
  420.         + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;

  421.     /** Constant for the forceCreate attribute. */
  422.     static final String ATTR_FORCECREATE = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START + XMLBeanDeclaration.RESERVED_PREFIX + "forceCreate"
  423.         + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;

  424.     /** Constant for the reload attribute. */
  425.     static final String ATTR_RELOAD = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START + XMLBeanDeclaration.RESERVED_PREFIX + "reload"
  426.         + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;

  427.     /**
  428.      * Constant for the tag attribute for providers.
  429.      */
  430.     static final String KEY_SYSTEM_PROPS = "[@systemProperties]";

  431.     /** Constant for the name of the header section. */
  432.     static final String SEC_HEADER = "header";

  433.     /** Constant for an expression that selects the union configurations. */
  434.     static final String KEY_UNION = "additional";

  435.     /** An array with the names of top level configuration sections. */
  436.     static final String[] CONFIG_SECTIONS = {"additional", "override", SEC_HEADER};

  437.     /**
  438.      * Constant for an expression that selects override configurations in the override section.
  439.      */
  440.     static final String KEY_OVERRIDE = "override";

  441.     /**
  442.      * Constant for the key that points to the list nodes definition of the override combiner.
  443.      */
  444.     static final String KEY_OVERRIDE_LIST = SEC_HEADER + ".combiner.override.list-nodes.node";

  445.     /**
  446.      * Constant for the key that points to the list nodes definition of the additional combiner.
  447.      */
  448.     static final String KEY_ADDITIONAL_LIST = SEC_HEADER + ".combiner.additional.list-nodes.node";

  449.     /**
  450.      * Constant for the key for defining providers in the configuration file.
  451.      */
  452.     static final String KEY_CONFIGURATION_PROVIDERS = SEC_HEADER + ".providers.provider";

  453.     /**
  454.      * Constant for the tag attribute for providers.
  455.      */
  456.     static final String KEY_PROVIDER_KEY = XMLBeanDeclaration.ATTR_PREFIX + "tag]";

  457.     /**
  458.      * Constant for the key for defining variable resolvers
  459.      */
  460.     static final String KEY_CONFIGURATION_LOOKUPS = SEC_HEADER + ".lookups.lookup";

  461.     /**
  462.      * Constant for the key for defining entity resolvers
  463.      */
  464.     static final String KEY_ENTITY_RESOLVER = SEC_HEADER + ".entity-resolver";

  465.     /**
  466.      * Constant for the prefix attribute for lookups.
  467.      */
  468.     static final String KEY_LOOKUP_KEY = XMLBeanDeclaration.ATTR_PREFIX + "prefix]";

  469.     /**
  470.      * Constant for the FileSystem.
  471.      */
  472.     static final String FILE_SYSTEM = SEC_HEADER + ".fileSystem";

  473.     /**
  474.      * Constant for the key of the result declaration. This key can point to a bean declaration, which defines properties of
  475.      * the resulting combined configuration.
  476.      */
  477.     static final String KEY_RESULT = SEC_HEADER + ".result";

  478.     /** Constant for the key of the combiner in the result declaration. */
  479.     static final String KEY_COMBINER = KEY_RESULT + ".nodeCombiner";

  480.     /** Constant for the XML file extension. */
  481.     static final String EXT_XML = "xml";

  482.     /** Constant for the basic configuration builder class. */
  483.     private static final String BASIC_BUILDER = "org.apache.commons.configuration2.builder.BasicConfigurationBuilder";

  484.     /** Constant for the file-based configuration builder class. */
  485.     private static final String FILE_BUILDER = "org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder";

  486.     /** Constant for the reloading file-based configuration builder class. */
  487.     private static final String RELOADING_BUILDER = "org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder";

  488.     /** Constant for the name of the file-based builder parameters class. */
  489.     private static final String FILE_PARAMS = "org.apache.commons.configuration2.builder.FileBasedBuilderParametersImpl";

  490.     /** Constant for the provider for properties files. */
  491.     private static final ConfigurationBuilderProvider PROPERTIES_PROVIDER = new FileExtensionConfigurationBuilderProvider(FILE_BUILDER, RELOADING_BUILDER,
  492.         "org.apache.commons.configuration2.XMLPropertiesConfiguration", "org.apache.commons.configuration2.PropertiesConfiguration", EXT_XML,
  493.         Collections.singletonList(FILE_PARAMS));

  494.     /** Constant for the provider for XML files. */
  495.     private static final ConfigurationBuilderProvider XML_PROVIDER = new BaseConfigurationBuilderProvider(FILE_BUILDER, RELOADING_BUILDER,
  496.         "org.apache.commons.configuration2.XMLConfiguration", Collections.singletonList("org.apache.commons.configuration2.builder.XMLBuilderParametersImpl"));

  497.     /** Constant for the provider for JNDI sources. */
  498.     private static final BaseConfigurationBuilderProvider JNDI_PROVIDER = new BaseConfigurationBuilderProvider(BASIC_BUILDER, null,
  499.         "org.apache.commons.configuration2.JNDIConfiguration",
  500.         Collections.singletonList("org.apache.commons.configuration2.builder.JndiBuilderParametersImpl"));

  501.     /** Constant for the provider for system properties. */
  502.     private static final BaseConfigurationBuilderProvider SYSTEM_PROVIDER = new BaseConfigurationBuilderProvider(BASIC_BUILDER, null,
  503.         "org.apache.commons.configuration2.SystemConfiguration", Collections.singletonList("org.apache.commons.configuration2.builder.BasicBuilderParameters"));

  504.     /** Constant for the provider for ini files. */
  505.     private static final BaseConfigurationBuilderProvider INI_PROVIDER = new BaseConfigurationBuilderProvider(FILE_BUILDER, RELOADING_BUILDER,
  506.         "org.apache.commons.configuration2.INIConfiguration", Collections.singletonList(FILE_PARAMS));

  507.     /** Constant for the provider for environment properties. */
  508.     private static final BaseConfigurationBuilderProvider ENV_PROVIDER = new BaseConfigurationBuilderProvider(BASIC_BUILDER, null,
  509.         "org.apache.commons.configuration2.EnvironmentConfiguration",
  510.         Collections.singletonList("org.apache.commons.configuration2.builder.BasicBuilderParameters"));

  511.     /** Constant for the provider for plist files. */
  512.     private static final BaseConfigurationBuilderProvider PLIST_PROVIDER = new FileExtensionConfigurationBuilderProvider(FILE_BUILDER, RELOADING_BUILDER,
  513.         "org.apache.commons.configuration2.plist.XMLPropertyListConfiguration", "org.apache.commons.configuration2.plist.PropertyListConfiguration", EXT_XML,
  514.         Collections.singletonList(FILE_PARAMS));

  515.     /** Constant for the provider for configuration definition files. */
  516.     private static final BaseConfigurationBuilderProvider COMBINED_PROVIDER = new CombinedConfigurationBuilderProvider();

  517.     /** Constant for the provider for multiple XML configurations. */
  518.     private static final MultiFileConfigurationBuilderProvider MULTI_XML_PROVIDER = new MultiFileConfigurationBuilderProvider(
  519.         "org.apache.commons.configuration2.XMLConfiguration", "org.apache.commons.configuration2.builder.XMLBuilderParametersImpl");

  520.     /** An array with the names of the default tags. */
  521.     private static final String[] DEFAULT_TAGS = {"properties", "xml", "hierarchicalXml", "plist", "ini", "system", "env", "jndi", "configuration",
  522.         "multiFile"};

  523.     /** An array with the providers for the default tags. */
  524.     private static final ConfigurationBuilderProvider[] DEFAULT_PROVIDERS = {PROPERTIES_PROVIDER, XML_PROVIDER, XML_PROVIDER, PLIST_PROVIDER, INI_PROVIDER,
  525.         SYSTEM_PROVIDER, ENV_PROVIDER, JNDI_PROVIDER, COMBINED_PROVIDER, MULTI_XML_PROVIDER};

  526.     /** A map with the default configuration builder providers. */
  527.     private static final Map<String, ConfigurationBuilderProvider> DEFAULT_PROVIDERS_MAP;

  528.     static {
  529.         DEFAULT_PROVIDERS_MAP = createDefaultProviders();
  530.     }

  531.     /**
  532.      * Creates the map with the default configuration builder providers.
  533.      *
  534.      * @return the map with default providers
  535.      */
  536.     private static Map<String, ConfigurationBuilderProvider> createDefaultProviders() {
  537.         final Map<String, ConfigurationBuilderProvider> providers = new HashMap<>();
  538.         for (int i = 0; i < DEFAULT_TAGS.length; i++) {
  539.             providers.put(DEFAULT_TAGS[i], DEFAULT_PROVIDERS[i]);
  540.         }
  541.         return providers;
  542.     }

  543.     /**
  544.      * Initializes the list nodes of the node combiner for the given combined configuration. This information can be set in
  545.      * the header section of the configuration definition file for both the override and the union combiners.
  546.      *
  547.      * @param cc the combined configuration to initialize
  548.      * @param defConfig the definition configuration
  549.      * @param key the key for the list nodes
  550.      */
  551.     private static void initNodeCombinerListNodes(final CombinedConfiguration cc, final HierarchicalConfiguration<?> defConfig, final String key) {
  552.         defConfig.getList(key).forEach(listNode -> cc.getNodeCombiner().addListNode((String) listNode));
  553.     }

  554.     /** The builder for the definition configuration. */
  555.     private ConfigurationBuilder<? extends HierarchicalConfiguration<?>> definitionBuilder;

  556.     /** Stores temporarily the configuration with the builder definitions. */
  557.     private HierarchicalConfiguration<?> definitionConfiguration;

  558.     /** The object with data about configuration sources. */
  559.     private ConfigurationSourceData sourceData;

  560.     /** Stores the current parameters object. */
  561.     private CombinedBuilderParametersImpl currentParameters;

  562.     /** The current XML parameters object. */
  563.     private XMLBuilderParametersImpl currentXMLParameters;

  564.     /** The configuration that is currently constructed. */
  565.     private CombinedConfiguration currentConfiguration;

  566.     /**
  567.      * A {@code ConfigurationInterpolator} to be used as parent for all child configurations to enable cross-source
  568.      * interpolation.
  569.      */
  570.     private ConfigurationInterpolator parentInterpolator;

  571.     /**
  572.      * Creates a new instance of {@code CombinedConfigurationBuilder}. No parameters are set.
  573.      */
  574.     public CombinedConfigurationBuilder() {
  575.         super(CombinedConfiguration.class);
  576.     }

  577.     /**
  578.      *
  579.      * Creates a new instance of {@code CombinedConfigurationBuilder} and sets the specified initialization parameters.
  580.      *
  581.      * @param params a map with initialization parameters
  582.      */
  583.     public CombinedConfigurationBuilder(final Map<String, Object> params) {
  584.         super(CombinedConfiguration.class, params);
  585.     }

  586.     /**
  587.      *
  588.      * Creates a new instance of {@code CombinedConfigurationBuilder} and sets the specified initialization parameters and
  589.      * the <em>allowFailOnInit</em> flag.
  590.      *
  591.      * @param params a map with initialization parameters
  592.      * @param allowFailOnInit the <em>allowFailOnInit</em> flag
  593.      */
  594.     public CombinedConfigurationBuilder(final Map<String, Object> params, final boolean allowFailOnInit) {
  595.         super(CombinedConfiguration.class, params, allowFailOnInit);
  596.     }

  597.     /**
  598.      * Adds a listener at the given definition builder which resets this builder when a reset of the definition builder
  599.      * happens. This way it is ensured that this builder produces a new combined configuration when its definition
  600.      * configuration changes.
  601.      *
  602.      * @param defBuilder the definition builder
  603.      */
  604.     private void addDefinitionBuilderChangeListener(final ConfigurationBuilder<? extends HierarchicalConfiguration<?>> defBuilder) {
  605.         defBuilder.addEventListener(ConfigurationBuilderEvent.RESET, event -> {
  606.             synchronized (this) {
  607.                 reset();
  608.                 definitionBuilder = defBuilder;
  609.             }
  610.         });
  611.     }

  612.     /**
  613.      * <p>
  614.      * Returns a set with the names of all child configuration builders. A tag defining a configuration source in the
  615.      * configuration definition file can have the {@code config-name} attribute. If this attribute is present, the
  616.      * corresponding builder is assigned this name and can be directly accessed through the {@link #getNamedBuilder(String)}
  617.      * method. This method returns a collection with all available builder names.
  618.      * </p>
  619.      * <p>
  620.      * <strong>Important note:</strong> This method only returns a meaningful result after the result configuration has been
  621.      * created by calling {@code getConfiguration()}. If called before, always an empty set is returned.
  622.      * </p>
  623.      *
  624.      * @return a set with the names of all builders
  625.      */
  626.     public synchronized Set<String> builderNames() {
  627.         if (sourceData == null) {
  628.             return Collections.emptySet();
  629.         }
  630.         return Collections.unmodifiableSet(sourceData.builderNames());
  631.     }

  632.     /**
  633.      * {@inheritDoc} This method is overridden to adapt the return type.
  634.      */
  635.     @Override
  636.     public CombinedConfigurationBuilder configure(final BuilderParameters... params) {
  637.         super.configure(params);
  638.         return this;
  639.     }

  640.     /**
  641.      * Creates and initializes a default {@code EntityResolver} if the definition configuration contains a corresponding
  642.      * declaration.
  643.      *
  644.      * @param config the definition configuration
  645.      * @param xmlParams the (already partly initialized) object with XML parameters; here the new resolver is to be stored
  646.      * @throws ConfigurationException if an error occurs
  647.      */
  648.     protected void configureEntityResolver(final HierarchicalConfiguration<?> config, final XMLBuilderParametersImpl xmlParams) throws ConfigurationException {
  649.         if (config.getMaxIndex(KEY_ENTITY_RESOLVER) == 0) {
  650.             final XMLBeanDeclaration decl = new XMLBeanDeclaration(config, KEY_ENTITY_RESOLVER, true);
  651.             final EntityResolver resolver = (EntityResolver) fetchBeanHelper().createBean(decl, CatalogResolver.class);
  652.             final FileSystem fileSystem = xmlParams.getFileHandler().getFileSystem();
  653.             if (fileSystem != null) {
  654.                 BeanHelper.setProperty(resolver, "fileSystem", fileSystem);
  655.             }
  656.             final String basePath = xmlParams.getFileHandler().getBasePath();
  657.             if (basePath != null) {
  658.                 BeanHelper.setProperty(resolver, "baseDir", basePath);
  659.             }
  660.             final ConfigurationInterpolator ci = new ConfigurationInterpolator();
  661.             ci.registerLookups(fetchPrefixLookups());
  662.             BeanHelper.setProperty(resolver, "interpolator", ci);

  663.             xmlParams.setEntityResolver(resolver);
  664.         }
  665.     }

  666.     /**
  667.      * Creates the {@code CombinedConfiguration} for the configuration sources in the {@code &lt;additional&gt;} section.
  668.      * This method is called when the builder constructs the final configuration. It creates a new
  669.      * {@code CombinedConfiguration} and initializes some properties from the result configuration.
  670.      *
  671.      * @param resultConfig the result configuration (this is the configuration that will be returned by the builder)
  672.      * @return the {@code CombinedConfiguration} for the additional configuration sources
  673.      * @since 1.7
  674.      */
  675.     protected CombinedConfiguration createAdditionalsConfiguration(final CombinedConfiguration resultConfig) {
  676.         final CombinedConfiguration addConfig = new CombinedConfiguration(new UnionCombiner());
  677.         addConfig.setListDelimiterHandler(resultConfig.getListDelimiterHandler());
  678.         return addConfig;
  679.     }

  680.     /**
  681.      * Creates {@code ConfigurationDeclaration} objects for the specified configurations.
  682.      *
  683.      * @param configs the list with configurations
  684.      * @return a collection with corresponding declarations
  685.      */
  686.     private Collection<ConfigurationDeclaration> createDeclarations(final Collection<? extends HierarchicalConfiguration<?>> configs) {
  687.         return configs.stream().map(c -> new ConfigurationDeclaration(this, c)).collect(Collectors.toList());
  688.     }

  689.     /**
  690.      * {@inheritDoc} This implementation evaluates the {@code result} property of the definition configuration. It creates a
  691.      * combined bean declaration with both the properties specified in the definition file and the properties defined as
  692.      * initialization parameters.
  693.      */
  694.     @Override
  695.     protected BeanDeclaration createResultDeclaration(final Map<String, Object> params) throws ConfigurationException {
  696.         final BeanDeclaration paramsDecl = super.createResultDeclaration(params);
  697.         final XMLBeanDeclaration resultDecl = new XMLBeanDeclaration(getDefinitionConfiguration(), KEY_RESULT, true, CombinedConfiguration.class.getName());
  698.         return new CombinedBeanDeclaration(resultDecl, paramsDecl);
  699.     }

  700.     /**
  701.      * Creates the data object for configuration sources and the corresponding builders.
  702.      *
  703.      * @return the newly created data object
  704.      * @throws ConfigurationException if an error occurs
  705.      */
  706.     private ConfigurationSourceData createSourceData() throws ConfigurationException {
  707.         final ConfigurationSourceData result = new ConfigurationSourceData();
  708.         result.initFromDefinitionConfiguration(getDefinitionConfiguration());
  709.         return result;
  710.     }

  711.     /**
  712.      * Creates a default builder for the definition configuration and initializes it with a parameters object. This method
  713.      * is called if no definition builder is defined in this builder's parameters. This implementation creates a default
  714.      * file-based builder which produces an {@code XMLConfiguration}; it expects a corresponding file specification. Note:
  715.      * This method is called in a synchronized block.
  716.      *
  717.      * @param builderParams the parameters object for the builder
  718.      * @return the standard builder for the definition configuration
  719.      */
  720.     protected ConfigurationBuilder<? extends HierarchicalConfiguration<?>> createXMLDefinitionBuilder(final BuilderParameters builderParams) {
  721.         return new FileBasedConfigurationBuilder<>(XMLConfiguration.class).configure(builderParams);
  722.     }

  723.     /**
  724.      * Returns a map with the current prefix lookup objects. This map is obtained from the {@code ConfigurationInterpolator}
  725.      * of the configuration under construction.
  726.      *
  727.      * @return the map with current prefix lookups (may be <strong>null</strong>)
  728.      */
  729.     private Map<String, ? extends Lookup> fetchPrefixLookups() {
  730.         final CombinedConfiguration cc = getConfigurationUnderConstruction();
  731.         return cc != null ? cc.getInterpolator().getLookups() : null;
  732.     }

  733.     /**
  734.      * Gets the current base path of this configuration builder. This is used for instance by all file-based child
  735.      * configurations.
  736.      *
  737.      * @return the base path
  738.      */
  739.     private String getBasePath() {
  740.         return currentXMLParameters.getFileHandler().getBasePath();
  741.     }

  742.     /**
  743.      * Gets a collection with the builders for all child configuration sources. This method can be used by derived
  744.      * classes providing additional functionality on top of the declared configuration sources. It only returns a defined
  745.      * value during construction of the result configuration instance.
  746.      *
  747.      * @return a collection with the builders for child configuration sources
  748.      */
  749.     protected synchronized Collection<ConfigurationBuilder<? extends Configuration>> getChildBuilders() {
  750.         return sourceData.getChildBuilders();
  751.     }

  752.     /**
  753.      * Gets the configuration object that is currently constructed. This method can be called during construction of the
  754.      * result configuration. It is intended for internal usage, for example some specialized builder providers need access to this
  755.      * configuration to perform advanced initialization.
  756.      *
  757.      * @return the configuration that us currently under construction
  758.      */
  759.     CombinedConfiguration getConfigurationUnderConstruction() {
  760.         return currentConfiguration;
  761.     }

  762.     /**
  763.      * Gets the {@code ConfigurationBuilder} which creates the definition configuration.
  764.      *
  765.      * @return the builder for the definition configuration
  766.      * @throws ConfigurationException if an error occurs
  767.      */
  768.     public synchronized ConfigurationBuilder<? extends HierarchicalConfiguration<?>> getDefinitionBuilder() throws ConfigurationException {
  769.         if (definitionBuilder == null) {
  770.             definitionBuilder = setupDefinitionBuilder(getParameters());
  771.             addDefinitionBuilderChangeListener(definitionBuilder);
  772.         }
  773.         return definitionBuilder;
  774.     }

  775.     /**
  776.      * Gets the configuration containing the definition of the combined configuration to be created. This method only
  777.      * returns a defined result during construction of the result configuration. The definition configuration is obtained
  778.      * from the definition builder at first access and then stored temporarily to ensure that during result construction
  779.      * always the same configuration instance is used. (Otherwise, it would be possible that the definition builder returns
  780.      * a different instance when queried multiple times.)
  781.      *
  782.      * @return the definition configuration
  783.      * @throws ConfigurationException if an error occurs
  784.      */
  785.     protected HierarchicalConfiguration<?> getDefinitionConfiguration() throws ConfigurationException {
  786.         if (definitionConfiguration == null) {
  787.             definitionConfiguration = getDefinitionBuilder().getConfiguration();
  788.         }
  789.         return definitionConfiguration;
  790.     }

  791.     /**
  792.      * <p>
  793.      * Gets the configuration builder with the given name. With this method a builder of a child configuration which was
  794.      * given a name in the configuration definition file can be accessed directly.
  795.      * </p>
  796.      * <p>
  797.      * <strong>Important note:</strong> This method only returns a meaningful result after the result configuration has been
  798.      * created by calling {@code getConfiguration()}. If called before, always an exception is thrown.
  799.      * </p>
  800.      *
  801.      * @param name the name of the builder in question
  802.      * @return the child configuration builder with this name
  803.      * @throws ConfigurationException if information about named builders is not yet available or no builder with this name
  804.      *         exists
  805.      */
  806.     public synchronized ConfigurationBuilder<? extends Configuration> getNamedBuilder(final String name) throws ConfigurationException {
  807.         if (sourceData == null) {
  808.             throw new ConfigurationException("Information about child builders" + " has not been setup yet! Call getConfiguration() first.");
  809.         }
  810.         final ConfigurationBuilder<? extends Configuration> builder = sourceData.getNamedBuilder(name);
  811.         if (builder == null) {
  812.             throw new ConfigurationException("Builder cannot be resolved: " + name);
  813.         }
  814.         return builder;
  815.     }

  816.     /**
  817.      * Obtains the data object for the configuration sources and the corresponding builders. This object is created on first
  818.      * access and reset when the definition builder sends a change event. This method is called in a synchronized block.
  819.      *
  820.      * @return the object with information about configuration sources
  821.      * @throws ConfigurationException if an error occurs
  822.      */
  823.     private ConfigurationSourceData getSourceData() throws ConfigurationException {
  824.         if (sourceData == null) {
  825.             if (currentParameters == null) {
  826.                 setUpCurrentParameters();
  827.                 setUpCurrentXMLParameters();
  828.             }
  829.             sourceData = createSourceData();
  830.         }
  831.         return sourceData;
  832.     }

  833.     /**
  834.      * Initializes a bean using the current {@code BeanHelper}. This is needed by builder providers when the configuration
  835.      * objects for sub builders are constructed.
  836.      *
  837.      * @param bean the bean to be initialized
  838.      * @param decl the {@code BeanDeclaration}
  839.      */
  840.     void initBean(final Object bean, final BeanDeclaration decl) {
  841.         fetchBeanHelper().initBean(bean, decl);
  842.     }

  843.     /**
  844.      * Initializes basic builder parameters for a child configuration with default settings set for this builder. This
  845.      * implementation ensures that all {@code Lookup} objects are propagated to child configurations and interpolation is
  846.      * setup correctly.
  847.      *
  848.      * @param params the parameters object
  849.      */
  850.     private void initChildBasicParameters(final BasicBuilderParameters params) {
  851.         params.setPrefixLookups(fetchPrefixLookups());
  852.         params.setParentInterpolator(parentInterpolator);
  853.         if (currentParameters.isInheritSettings()) {
  854.             params.inheritFrom(getParameters());
  855.         }
  856.     }

  857.     /**
  858.      * Initializes a parameters object for a child builder. This combined configuration builder has a bunch of properties
  859.      * which may be inherited by child configurations, for example the base path, the file system, etc. While processing the
  860.      * builders for child configurations, this method is called for each parameters object for a child builder. It
  861.      * initializes some properties of the passed in parameters objects which are derived from this parent builder.
  862.      *
  863.      * @param params the parameters object to be initialized
  864.      */
  865.     protected void initChildBuilderParameters(final BuilderParameters params) {
  866.         initDefaultChildParameters(params);

  867.         if (params instanceof BasicBuilderParameters) {
  868.             initChildBasicParameters((BasicBuilderParameters) params);
  869.         }
  870.         if (params instanceof XMLBuilderProperties<?>) {
  871.             initChildXMLParameters((XMLBuilderProperties<?>) params);
  872.         }
  873.         if (params instanceof FileBasedBuilderProperties<?>) {
  874.             initChildFileBasedParameters((FileBasedBuilderProperties<?>) params);
  875.         }
  876.         if (params instanceof CombinedBuilderParametersImpl) {
  877.             initChildCombinedParameters((CombinedBuilderParametersImpl) params);
  878.         }
  879.     }

  880.     /**
  881.      * Initializes a parameters object for a combined configuration builder with properties already set for this parent
  882.      * builder. This implementation deals only with a subset of properties. Other properties are already handled by the
  883.      * specialized builder provider.
  884.      *
  885.      * @param params the parameters object
  886.      */
  887.     private void initChildCombinedParameters(final CombinedBuilderParametersImpl params) {
  888.         params.registerMissingProviders(currentParameters);
  889.         params.setBasePath(getBasePath());
  890.     }

  891.     /**
  892.      * Initializes the event listeners of the specified builder from this object. This method is used to inherit all
  893.      * listeners from a parent builder.
  894.      *
  895.      * @param dest the destination builder object which is to be initialized
  896.      */
  897.     void initChildEventListeners(final BasicConfigurationBuilder<? extends Configuration> dest) {
  898.         copyEventListeners(dest);
  899.     }

  900.     /**
  901.      * Initializes a parameters object for a file-based configuration with properties already set for this parent builder.
  902.      * This method handles properties like a default file system or a base path.
  903.      *
  904.      * @param params the parameters object
  905.      */
  906.     private void initChildFileBasedParameters(final FileBasedBuilderProperties<?> params) {
  907.         params.setBasePath(getBasePath());
  908.         params.setFileSystem(currentXMLParameters.getFileHandler().getFileSystem());
  909.     }

  910.     /**
  911.      * Initializes a parameters object for an XML configuration with properties already set for this parent builder.
  912.      *
  913.      * @param params the parameters object
  914.      */
  915.     private void initChildXMLParameters(final XMLBuilderProperties<?> params) {
  916.         params.setEntityResolver(currentXMLParameters.getEntityResolver());
  917.     }

  918.     /**
  919.      * Initializes the default base path for all file-based child configuration sources. The base path can be explicitly
  920.      * defined in the parameters of this builder. Otherwise, if the definition builder is a file-based builder, it is
  921.      * obtained from there.
  922.      *
  923.      * @throws ConfigurationException if an error occurs
  924.      */
  925.     private void initDefaultBasePath() throws ConfigurationException {
  926.         assert currentParameters != null : "Current parameters undefined!";
  927.         if (currentParameters.getBasePath() != null) {
  928.             currentXMLParameters.setBasePath(currentParameters.getBasePath());
  929.         } else {
  930.             final ConfigurationBuilder<? extends HierarchicalConfiguration<?>> defBuilder = getDefinitionBuilder();
  931.             if (defBuilder instanceof FileBasedConfigurationBuilder) {
  932.                 @SuppressWarnings("rawtypes")
  933.                 final FileBasedConfigurationBuilder fileBuilder = (FileBasedConfigurationBuilder) defBuilder;
  934.                 final URL url = fileBuilder.getFileHandler().getURL();
  935.                 currentXMLParameters.setBasePath(url != null ? url.toExternalForm() : fileBuilder.getFileHandler().getBasePath());
  936.             }
  937.         }
  938.     }

  939.     /**
  940.      * Executes the {@link org.apache.commons.configuration2.builder.DefaultParametersManager DefaultParametersManager}
  941.      * stored in the current parameters on the passed in parameters object. If default handlers have been registered for
  942.      * this type of parameters, an initialization is now performed. This method is called before the parameters object is
  943.      * initialized from the configuration definition file. So default values can be overridden later with concrete property
  944.      * definitions.
  945.      *
  946.      * @param params the parameters to be initialized
  947.      * @throws org.apache.commons.configuration2.ex.ConfigurationRuntimeException if an error occurs when copying properties
  948.      */
  949.     private void initDefaultChildParameters(final BuilderParameters params) {
  950.         currentParameters.getChildDefaultParametersManager().initializeParameters(params);
  951.     }

  952.     /**
  953.      * Creates and initializes a default {@code FileSystem} if the definition configuration contains a corresponding
  954.      * declaration. The file system returned by this method is used as default for all file-based child configuration
  955.      * sources.
  956.      *
  957.      * @param config the definition configuration
  958.      * @return the default {@code FileSystem} (may be <strong>null</strong>)
  959.      * @throws ConfigurationException if an error occurs
  960.      */
  961.     protected FileSystem initFileSystem(final HierarchicalConfiguration<?> config) throws ConfigurationException {
  962.         if (config.getMaxIndex(FILE_SYSTEM) == 0) {
  963.             final XMLBeanDeclaration decl = new XMLBeanDeclaration(config, FILE_SYSTEM);
  964.             return (FileSystem) fetchBeanHelper().createBean(decl);
  965.         }
  966.         return null;
  967.     }

  968.     /**
  969.      * {@inheritDoc} This implementation processes the definition configuration in order to
  970.      * <ul>
  971.      * <li>initialize the resulting {@code CombinedConfiguration}</li>
  972.      * <li>determine the builders for all configuration sources</li>
  973.      * <li>populate the resulting {@code CombinedConfiguration}</li>
  974.      * </ul>
  975.      */
  976.     @Override
  977.     protected void initResultInstance(final CombinedConfiguration result) throws ConfigurationException {
  978.         super.initResultInstance(result);

  979.         currentConfiguration = result;
  980.         final HierarchicalConfiguration<?> config = getDefinitionConfiguration();
  981.         if (config.getMaxIndex(KEY_COMBINER) < 0) {
  982.             // No combiner defined => set default
  983.             result.setNodeCombiner(new OverrideCombiner());
  984.         }

  985.         setUpCurrentParameters();
  986.         initNodeCombinerListNodes(result, config, KEY_OVERRIDE_LIST);
  987.         registerConfiguredProviders(config);
  988.         setUpCurrentXMLParameters();
  989.         currentXMLParameters.setFileSystem(initFileSystem(config));
  990.         initSystemProperties(config, getBasePath());
  991.         registerConfiguredLookups(config, result);
  992.         configureEntityResolver(config, currentXMLParameters);
  993.         setUpParentInterpolator(currentConfiguration, config);

  994.         final ConfigurationSourceData data = getSourceData();
  995.         final boolean createBuilders = data.getChildBuilders().isEmpty();
  996.         final List<ConfigurationBuilder<? extends Configuration>> overrideBuilders = data.createAndAddConfigurations(result, data.getOverrideSources(),
  997.             data.overrideBuilders);
  998.         if (createBuilders) {
  999.             data.overrideBuilders.addAll(overrideBuilders);
  1000.         }
  1001.         if (!data.getUnionSources().isEmpty()) {
  1002.             final CombinedConfiguration addConfig = createAdditionalsConfiguration(result);
  1003.             result.addConfiguration(addConfig, ADDITIONAL_NAME);
  1004.             initNodeCombinerListNodes(addConfig, config, KEY_ADDITIONAL_LIST);
  1005.             final List<ConfigurationBuilder<? extends Configuration>> unionBuilders = data.createAndAddConfigurations(addConfig, data.unionDeclarations,
  1006.                 data.unionBuilders);
  1007.             if (createBuilders) {
  1008.                 data.unionBuilders.addAll(unionBuilders);
  1009.             }
  1010.         }

  1011.         result.isEmpty(); // this sets up the node structure
  1012.         currentConfiguration = null;
  1013.     }

  1014.     /**
  1015.      * Handles a file with system properties that may be defined in the definition configuration. If such property file is
  1016.      * configured, all of its properties are added to the system properties.
  1017.      *
  1018.      * @param config the definition configuration
  1019.      * @param basePath the base path defined for this builder (may be <strong>null</strong>)
  1020.      * @throws ConfigurationException if an error occurs.
  1021.      */
  1022.     protected void initSystemProperties(final HierarchicalConfiguration<?> config, final String basePath) throws ConfigurationException {
  1023.         final String fileName = config.getString(KEY_SYSTEM_PROPS);
  1024.         if (fileName != null) {
  1025.             try {
  1026.                 SystemConfiguration.setSystemProperties(basePath, fileName);
  1027.             } catch (final Exception ex) {
  1028.                 throw new ConfigurationException("Error setting system properties from " + fileName, ex);
  1029.             }
  1030.         }
  1031.     }

  1032.     /**
  1033.      * Returns the {@code ConfigurationBuilderProvider} for the given tag. This method is called during creation of the
  1034.      * result configuration. (It is not allowed to call it at another point of time; result is then unpredictable!) It
  1035.      * supports all default providers and custom providers added through the parameters object as well.
  1036.      *
  1037.      * @param tagName the name of the tag
  1038.      * @return the provider that was registered for this tag or <strong>null</strong> if there is none
  1039.      */
  1040.     protected ConfigurationBuilderProvider providerForTag(final String tagName) {
  1041.         return currentParameters.providerForTag(tagName);
  1042.     }

  1043.     /**
  1044.      * Processes custom {@link Lookup} objects that might be declared in the definition configuration. Each {@code Lookup}
  1045.      * object is registered at the definition configuration and at the result configuration. It is also added to all child
  1046.      * configurations added to the resulting combined configuration.
  1047.      *
  1048.      * @param defConfig the definition configuration
  1049.      * @param resultConfig the resulting configuration
  1050.      * @throws ConfigurationException if an error occurs
  1051.      */
  1052.     protected void registerConfiguredLookups(final HierarchicalConfiguration<?> defConfig, final Configuration resultConfig) throws ConfigurationException {
  1053.         final Map<String, Lookup> lookups = defConfig.configurationsAt(KEY_CONFIGURATION_LOOKUPS).stream().collect(
  1054.                 Collectors.toMap(config -> config.getString(KEY_LOOKUP_KEY), config -> (Lookup) fetchBeanHelper().createBean(new XMLBeanDeclaration(config))));

  1055.         if (!lookups.isEmpty()) {
  1056.             final ConfigurationInterpolator defCI = defConfig.getInterpolator();
  1057.             if (defCI != null) {
  1058.                 defCI.registerLookups(lookups);
  1059.             }
  1060.             resultConfig.getInterpolator().registerLookups(lookups);
  1061.         }
  1062.     }

  1063.     /**
  1064.      * Registers providers defined in the configuration.
  1065.      *
  1066.      * @param defConfig the definition configuration
  1067.      */
  1068.     private void registerConfiguredProviders(final HierarchicalConfiguration<?> defConfig) {
  1069.         defConfig.configurationsAt(KEY_CONFIGURATION_PROVIDERS).forEach(config -> {
  1070.             final XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
  1071.             final String key = config.getString(KEY_PROVIDER_KEY);
  1072.             currentParameters.registerProvider(key, (ConfigurationBuilderProvider) fetchBeanHelper().createBean(decl));
  1073.         });
  1074.     }

  1075.     /**
  1076.      * {@inheritDoc} This implementation resets some specific internal state of this builder.
  1077.      */
  1078.     @Override
  1079.     public synchronized void resetParameters() {
  1080.         super.resetParameters();
  1081.         definitionBuilder = null;
  1082.         definitionConfiguration = null;
  1083.         currentParameters = null;
  1084.         currentXMLParameters = null;

  1085.         if (sourceData != null) {
  1086.             sourceData.cleanUp();
  1087.             sourceData = null;
  1088.         }
  1089.     }

  1090.     /**
  1091.      * Initializes the current parameters object. This object has either been passed at builder configuration time or it is
  1092.      * newly created. In any case, it is manipulated during result creation.
  1093.      */
  1094.     private void setUpCurrentParameters() {
  1095.         currentParameters = CombinedBuilderParametersImpl.fromParameters(getParameters(), true);
  1096.         currentParameters.registerMissingProviders(DEFAULT_PROVIDERS_MAP);
  1097.     }

  1098.     /**
  1099.      * Sets up an XML parameters object which is used to store properties related to XML and file-based configurations
  1100.      * during creation of the result configuration. The properties stored in this object can be inherited to child
  1101.      * configurations.
  1102.      *
  1103.      * @throws ConfigurationException if an error occurs
  1104.      */
  1105.     private void setUpCurrentXMLParameters() throws ConfigurationException {
  1106.         currentXMLParameters = new XMLBuilderParametersImpl();
  1107.         initDefaultBasePath();
  1108.     }

  1109.     /**
  1110.      * Obtains the {@code ConfigurationBuilder} object which provides access to the configuration containing the definition
  1111.      * of the combined configuration to create. If a definition builder is defined in the parameters, it is used. Otherwise,
  1112.      * we check whether the combined builder parameters object contains a parameters object for the definition builder. If
  1113.      * this is the case, a builder for an {@code XMLConfiguration} is created and configured with this object. As a last
  1114.      * resort, it is looked for a {@link FileBasedBuilderParametersImpl} object in the properties. If found, also a XML
  1115.      * configuration builder is created which loads this file. Note: This method is called from a synchronized block.
  1116.      *
  1117.      * @param params the current parameters for this builder
  1118.      * @return the builder for the definition configuration
  1119.      * @throws ConfigurationException if an error occurs
  1120.      */
  1121.     protected ConfigurationBuilder<? extends HierarchicalConfiguration<?>> setupDefinitionBuilder(final Map<String, Object> params)
  1122.         throws ConfigurationException {
  1123.         final CombinedBuilderParametersImpl cbParams = CombinedBuilderParametersImpl.fromParameters(params);
  1124.         if (cbParams != null) {
  1125.             final ConfigurationBuilder<? extends HierarchicalConfiguration<?>> defBuilder = cbParams.getDefinitionBuilder();
  1126.             if (defBuilder != null) {
  1127.                 return defBuilder;
  1128.             }

  1129.             if (cbParams.getDefinitionBuilderParameters() != null) {
  1130.                 return createXMLDefinitionBuilder(cbParams.getDefinitionBuilderParameters());
  1131.             }
  1132.         }

  1133.         final BuilderParameters fileParams = FileBasedBuilderParametersImpl.fromParameters(params);
  1134.         if (fileParams != null) {
  1135.             return createXMLDefinitionBuilder(fileParams);
  1136.         }

  1137.         throw new ConfigurationException("No builder for configuration definition specified!");
  1138.     }

  1139.     /**
  1140.      * Sets up a parent {@code ConfigurationInterpolator} object. This object has a default {@link Lookup} querying the
  1141.      * resulting combined configuration. Thus interpolation works globally across all configuration sources.
  1142.      *
  1143.      * @param resultConfig the result configuration
  1144.      * @param defConfig the definition configuration
  1145.      */
  1146.     private void setUpParentInterpolator(final Configuration resultConfig, final Configuration defConfig) {
  1147.         parentInterpolator = new ConfigurationInterpolator();
  1148.         parentInterpolator.addDefaultLookup(new ConfigurationLookup(resultConfig));
  1149.         final ConfigurationInterpolator defInterpolator = defConfig.getInterpolator();
  1150.         if (defInterpolator != null) {
  1151.             defInterpolator.setParentInterpolator(parentInterpolator);
  1152.         }
  1153.     }
  1154. }