blob: 387a02a0eb5509e2f3078e0177cd427af819abe8 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.configuration2.builder.combined;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.configuration2.CombinedConfiguration;
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.ConfigurationLookup;
import org.apache.commons.configuration2.HierarchicalConfiguration;
import org.apache.commons.configuration2.SystemConfiguration;
import org.apache.commons.configuration2.XMLConfiguration;
import org.apache.commons.configuration2.beanutils.BeanDeclaration;
import org.apache.commons.configuration2.beanutils.BeanHelper;
import org.apache.commons.configuration2.beanutils.CombinedBeanDeclaration;
import org.apache.commons.configuration2.beanutils.XMLBeanDeclaration;
import org.apache.commons.configuration2.builder.BasicBuilderParameters;
import org.apache.commons.configuration2.builder.BasicConfigurationBuilder;
import org.apache.commons.configuration2.builder.BuilderParameters;
import org.apache.commons.configuration2.builder.ConfigurationBuilder;
import org.apache.commons.configuration2.builder.ConfigurationBuilderEvent;
import org.apache.commons.configuration2.builder.FileBasedBuilderParametersImpl;
import org.apache.commons.configuration2.builder.FileBasedBuilderProperties;
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.XMLBuilderParametersImpl;
import org.apache.commons.configuration2.builder.XMLBuilderProperties;
import org.apache.commons.configuration2.event.EventListener;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
import org.apache.commons.configuration2.interpol.Lookup;
import org.apache.commons.configuration2.io.FileSystem;
import org.apache.commons.configuration2.resolver.CatalogResolver;
import org.apache.commons.configuration2.tree.DefaultExpressionEngineSymbols;
import org.apache.commons.configuration2.tree.OverrideCombiner;
import org.apache.commons.configuration2.tree.UnionCombiner;
import org.xml.sax.EntityResolver;
/**
* <p>
* A specialized {@code ConfigurationBuilder} implementation that creates a
* {@link CombinedConfiguration} from multiple configuration sources defined by
* an XML-based <em>configuration definition file</em>.
* </p>
* <p>
* This class provides an easy and flexible means for loading multiple
* configuration sources and combining the results into a single configuration
* object. The sources to be loaded are defined in an XML document that can
* contain certain tags representing the different supported configuration
* classes. If such a tag is found, a corresponding {@code ConfigurationBuilder}
* class is instantiated and initialized using the classes of the
* {@code beanutils} package (namely
* {@link org.apache.commons.configuration2.beanutils.XMLBeanDeclaration
* XMLBeanDeclaration} will be used to extract the configuration's
* initialization parameters, which allows for complex initialization
* scenarios).
* </p>
* <p>
* It is also possible to add custom tags to the configuration definition file.
* For this purpose an implementation of
* {@link CombinedConfigurationBuilderProvider} has to be created which is
* responsible for the creation of a {@code ConfigurationBuilder} associated
* with the custom tag. An instance of this class has to be registered at the
* {@link CombinedBuilderParametersImpl} object which is used to initialize this
* {@code CombinedConfigurationBuilder}. This provider will then be called when
* the corresponding custom tag is detected. For many default configuration
* classes providers are already registered.
* </p>
* <p>
* The configuration definition file has the following basic structure:
* </p>
*
* <pre>
* &lt;configuration systemProperties="properties file name"&gt;
* &lt;header&gt;
* &lt;!-- Optional meta information about the combined configuration --&gt;
* &lt;/header&gt;
* &lt;override&gt;
* &lt;!-- Declarations for override configurations --&gt;
* &lt;/override&gt;
* &lt;additional&gt;
* &lt;!-- Declarations for union configurations --&gt;
* &lt;/additional&gt;
* &lt;/configuration&gt;
* </pre>
*
* <p>
* The name of the root element (here {@code configuration}) is arbitrary. The
* optional {@code systemProperties} attribute identifies the path to a property
* file containing properties that should be added to the system properties. If
* specified on the root element, the system properties are set before the rest
* of the configuration is processed.
* </p>
* <p>
* There are two sections (both of them are optional) for declaring
* <em>override</em> and <em>additional</em> configurations. Configurations in
* the former section are evaluated in the order of their declaration, and
* properties of configurations declared earlier hide those of configurations
* declared later. Configurations in the latter section are combined to a union
* configuration, i.e. all of their properties are added to a large hierarchical
* configuration. Configuration declarations that occur as direct children of
* the root element are treated as override declarations.
* </p>
* <p>
* Each configuration declaration consists of a tag whose name is associated
* with a {@code CombinedConfigurationBuilderProvider}. This can be one of the
* predefined tags like {@code properties}, or {@code xml}, or a custom tag, for
* which a configuration builder provider was registered (as described above).
* Attributes and sub elements with specific initialization parameters can be
* added. There are some reserved attributes with a special meaning that can be
* used in every configuration declaration:
* </p>
* <table border="1">
* <caption>Standard attributes for configuration declarations</caption>
* <tr>
* <th>Attribute</th>
* <th>Meaning</th>
* </tr>
* <tr>
* <td valign="top">{@code config-name}</td>
* <td>Allows specifying a name for this configuration. This name can be used to
* obtain a reference to the configuration from the resulting combined
* configuration (see below). It can also be passed to the
* {@link #getNamedBuilder(String)} method.</td>
* </tr>
* <tr>
* <td valign="top">{@code config-at}</td>
* <td>With this attribute an optional prefix can be specified for the
* properties of the corresponding configuration.</td>
* </tr>
* <tr>
* <td valign="top">{@code config-optional}</td>
* <td>Declares a configuration source as optional. This means that errors that
* occur when creating the configuration are ignored.</td>
* </tr>
* <tr>
* <td valign="top">{@code config-reload}</td>
* <td>Many configuration sources support a reloading mechanism. For those
* sources it is possible to enable reloading by providing this attribute with a
* value of <strong>true</strong>.</td>
* </tr>
* </table>
* <p>
* The optional <em>header</em> section can contain some meta data about the
* created configuration itself. For instance, it is possible to set further
* properties of the {@code NodeCombiner} objects used for constructing the
* resulting configuration.
* </p>
* <p>
* The default configuration object returned by this builder is an instance of
* the {@link CombinedConfiguration} class. This allows for convenient access to
* the configuration objects maintained by the combined configuration (e.g. for
* updates of single configuration objects). It has also the advantage that the
* properties stored in all declared configuration objects are collected and
* transformed into a single hierarchical structure, which can be accessed using
* different expression engines. The actual {@code CombinedConfiguration}
* implementation can be overridden by specifying the class in the
* <em>config-class</em> attribute of the result element.
* </p>
* <p>
* A custom EntityResolver can be used for all XMLConfigurations by adding
* </p>
*
* <pre>
* &lt;entity-resolver config-class="EntityResolver fully qualified class name"&gt;
* </pre>
*
* <p>
* A specific CatalogResolver can be specified for all XMLConfiguration sources
* by adding
* </p>
* <pre>
* &lt;entity-resolver catalogFiles="comma separated list of catalog files"&gt;
* </pre>
*
* <p>
* Additional ConfigurationProviders can be added by configuring them in the
* <em>header</em> section.
* </p>
*
* <pre>
* &lt;providers&gt;
* &lt;provider config-tag="tag name" config-class="provider fully qualified class name"/&gt;
* &lt;/providers&gt;
* </pre>
*
* <p>
* Additional variable resolvers can be added by configuring them in the
* <em>header</em> section.
* </p>
*
* <pre>
* &lt;lookups&gt;
* &lt;lookup config-prefix="prefix" config-class="StrLookup fully qualified class name"/&gt;
* &lt;/lookups&gt;
* </pre>
*
* <p>
* All declared override configurations are directly added to the resulting
* combined configuration. If they are given names (using the
* {@code config-name} attribute), they can directly be accessed using the
* {@code getConfiguration(String)} method of {@code CombinedConfiguration}. The
* additional configurations are altogether added to another combined
* configuration, which uses a union combiner. Then this union configuration is
* added to the resulting combined configuration under the name defined by the
* {@code ADDITIONAL_NAME} constant. The {@link #getNamedBuilder(String)} method
* can be used to access the {@code ConfigurationBuilder} objects for all
* configuration sources which have been assigned a name; care has to be taken
* that these names are unique.
* </p>
*
* @since 1.3
*/
public class CombinedConfigurationBuilder extends BasicConfigurationBuilder<CombinedConfiguration>
{
/**
* Constant for the name of the additional configuration. If the
* configuration definition file contains an {@code additional}
* section, a special union configuration is created and added under this
* name to the resulting combined configuration.
*/
public static final String ADDITIONAL_NAME = CombinedConfigurationBuilder.class
.getName()
+ "/ADDITIONAL_CONFIG";
/** Constant for the name of the configuration bean factory. */
static final String CONFIG_BEAN_FACTORY_NAME = CombinedConfigurationBuilder.class
.getName()
+ ".CONFIG_BEAN_FACTORY_NAME";
/** Constant for the reserved name attribute. */
static final String ATTR_NAME = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START
+ XMLBeanDeclaration.RESERVED_PREFIX
+ "name"
+ DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;
/** Constant for the name of the at attribute. */
static final String ATTR_ATNAME = "at";
/** Constant for the reserved at attribute. */
static final String ATTR_AT_RES = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START
+ XMLBeanDeclaration.RESERVED_PREFIX
+ ATTR_ATNAME
+ DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;
/** Constant for the at attribute without the reserved prefix. */
static final String ATTR_AT = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START
+ ATTR_ATNAME + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;
/** Constant for the name of the optional attribute. */
static final String ATTR_OPTIONALNAME = "optional";
/** Constant for the reserved optional attribute. */
static final String ATTR_OPTIONAL_RES = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START
+ XMLBeanDeclaration.RESERVED_PREFIX
+ ATTR_OPTIONALNAME
+ DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;
/** Constant for the optional attribute without the reserved prefix. */
static final String ATTR_OPTIONAL = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START
+ ATTR_OPTIONALNAME + DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;
/** Constant for the forceCreate attribute. */
static final String ATTR_FORCECREATE = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START
+ XMLBeanDeclaration.RESERVED_PREFIX
+ "forceCreate"
+ DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;
/** Constant for the reload attribute. */
static final String ATTR_RELOAD = DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_START
+ XMLBeanDeclaration.RESERVED_PREFIX
+ "reload"
+ DefaultExpressionEngineSymbols.DEFAULT_ATTRIBUTE_END;
/**
* Constant for the tag attribute for providers.
*/
static final String KEY_SYSTEM_PROPS = "[@systemProperties]";
/** Constant for the name of the header section. */
static final String SEC_HEADER = "header";
/** Constant for an expression that selects the union configurations. */
static final String KEY_UNION = "additional";
/** An array with the names of top level configuration sections.*/
static final String[] CONFIG_SECTIONS = {
"additional", "override", SEC_HEADER
};
/**
* Constant for an expression that selects override configurations in the
* override section.
*/
static final String KEY_OVERRIDE = "override";
/**
* Constant for the key that points to the list nodes definition of the
* override combiner.
*/
static final String KEY_OVERRIDE_LIST = SEC_HEADER
+ ".combiner.override.list-nodes.node";
/**
* Constant for the key that points to the list nodes definition of the
* additional combiner.
*/
static final String KEY_ADDITIONAL_LIST = SEC_HEADER
+ ".combiner.additional.list-nodes.node";
/**
* Constant for the key for defining providers in the configuration file.
*/
static final String KEY_CONFIGURATION_PROVIDERS = SEC_HEADER
+ ".providers.provider";
/**
* Constant for the tag attribute for providers.
*/
static final String KEY_PROVIDER_KEY = XMLBeanDeclaration.ATTR_PREFIX + "tag]";
/**
* Constant for the key for defining variable resolvers
*/
static final String KEY_CONFIGURATION_LOOKUPS = SEC_HEADER
+ ".lookups.lookup";
/**
* Constant for the key for defining entity resolvers
*/
static final String KEY_ENTITY_RESOLVER = SEC_HEADER + ".entity-resolver";
/**
* Constant for the prefix attribute for lookups.
*/
static final String KEY_LOOKUP_KEY = XMLBeanDeclaration.ATTR_PREFIX + "prefix]";
/**
* Constant for the FileSystem.
*/
static final String FILE_SYSTEM = SEC_HEADER + ".fileSystem";
/**
* Constant for the key of the result declaration. This key can point to a
* bean declaration, which defines properties of the resulting combined
* configuration.
*/
static final String KEY_RESULT = SEC_HEADER + ".result";
/** Constant for the key of the combiner in the result declaration.*/
static final String KEY_COMBINER = KEY_RESULT + ".nodeCombiner";
/** Constant for the XML file extension. */
static final String EXT_XML = "xml";
/** Constant for the basic configuration builder class. */
private static final String BASIC_BUILDER =
"org.apache.commons.configuration2.builder.BasicConfigurationBuilder";
/** Constant for the file-based configuration builder class. */
private static final String FILE_BUILDER =
"org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder";
/** Constant for the reloading file-based configuration builder class. */
private static final String RELOADING_BUILDER =
"org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder";
/** Constant for the name of the file-based builder parameters class. */
private static final String FILE_PARAMS =
"org.apache.commons.configuration2.builder.FileBasedBuilderParametersImpl";
/** Constant for the provider for properties files. */
private static final ConfigurationBuilderProvider PROPERTIES_PROVIDER =
new FileExtensionConfigurationBuilderProvider(
FILE_BUILDER,
RELOADING_BUILDER,
"org.apache.commons.configuration2.XMLPropertiesConfiguration",
"org.apache.commons.configuration2.PropertiesConfiguration",
EXT_XML, Collections.singletonList(FILE_PARAMS));
/** Constant for the provider for XML files. */
private static final ConfigurationBuilderProvider XML_PROVIDER =
new BaseConfigurationBuilderProvider(FILE_BUILDER, RELOADING_BUILDER,
"org.apache.commons.configuration2.XMLConfiguration",
Collections.singletonList("org.apache.commons.configuration2.builder.XMLBuilderParametersImpl"));
/** Constant for the provider for JNDI sources. */
private static final BaseConfigurationBuilderProvider JNDI_PROVIDER =
new BaseConfigurationBuilderProvider(
BASIC_BUILDER,
null,
"org.apache.commons.configuration2.JNDIConfiguration",
Collections.singletonList("org.apache.commons.configuration2.builder.JndiBuilderParametersImpl"));
/** Constant for the provider for system properties. */
private static final BaseConfigurationBuilderProvider SYSTEM_PROVIDER =
new BaseConfigurationBuilderProvider(
BASIC_BUILDER,
null,
"org.apache.commons.configuration2.SystemConfiguration",
Collections.singletonList("org.apache.commons.configuration2.builder.BasicBuilderParameters"));
/** Constant for the provider for ini files. */
private static final BaseConfigurationBuilderProvider INI_PROVIDER =
new BaseConfigurationBuilderProvider(FILE_BUILDER, RELOADING_BUILDER,
"org.apache.commons.configuration2.INIConfiguration",
Collections.singletonList(FILE_PARAMS));
/** Constant for the provider for environment properties. */
private static final BaseConfigurationBuilderProvider ENV_PROVIDER =
new BaseConfigurationBuilderProvider(
BASIC_BUILDER,
null,
"org.apache.commons.configuration2.EnvironmentConfiguration",
Collections.singletonList("org.apache.commons.configuration2.builder.BasicBuilderParameters"));
/** Constant for the provider for plist files. */
private static final BaseConfigurationBuilderProvider PLIST_PROVIDER =
new FileExtensionConfigurationBuilderProvider(
FILE_BUILDER,
RELOADING_BUILDER,
"org.apache.commons.configuration2.plist.XMLPropertyListConfiguration",
"org.apache.commons.configuration2.plist.PropertyListConfiguration",
EXT_XML, Collections.singletonList(FILE_PARAMS));
/** Constant for the provider for configuration definition files. */
private static final BaseConfigurationBuilderProvider COMBINED_PROVIDER =
new CombinedConfigurationBuilderProvider();
/** Constant for the provider for multiple XML configurations. */
private static final MultiFileConfigurationBuilderProvider MULTI_XML_PROVIDER =
new MultiFileConfigurationBuilderProvider(
"org.apache.commons.configuration2.XMLConfiguration",
"org.apache.commons.configuration2.builder.XMLBuilderParametersImpl");
/** An array with the names of the default tags. */
private static final String[] DEFAULT_TAGS = {
"properties", "xml", "hierarchicalXml", "plist",
"ini", "system", "env", "jndi", "configuration", "multiFile"
};
/** An array with the providers for the default tags. */
private static final ConfigurationBuilderProvider[] DEFAULT_PROVIDERS = {
PROPERTIES_PROVIDER, XML_PROVIDER, XML_PROVIDER, PLIST_PROVIDER, INI_PROVIDER,
SYSTEM_PROVIDER, ENV_PROVIDER, JNDI_PROVIDER, COMBINED_PROVIDER,
MULTI_XML_PROVIDER
};
/** A map with the default configuration builder providers. */
private static final Map<String, ConfigurationBuilderProvider> DEFAULT_PROVIDERS_MAP;
/** The builder for the definition configuration. */
private ConfigurationBuilder<? extends HierarchicalConfiguration<?>> definitionBuilder;
/** Stores temporarily the configuration with the builder definitions. */
private HierarchicalConfiguration<?> definitionConfiguration;
/** The object with data about configuration sources. */
private ConfigurationSourceData sourceData;
/** Stores the current parameters object. */
private CombinedBuilderParametersImpl currentParameters;
/** The current XML parameters object. */
private XMLBuilderParametersImpl currentXMLParameters;
/** The configuration that is currently constructed. */
private CombinedConfiguration currentConfiguration;
/**
* A {@code ConfigurationInterpolator} to be used as parent for all child
* configurations to enable cross-source interpolation.
*/
private ConfigurationInterpolator parentInterpolator;
/**
* Creates a new instance of {@code CombinedConfigurationBuilder}. No parameters
* are set.
*/
public CombinedConfigurationBuilder()
{
super(CombinedConfiguration.class);
}
/**
*
* Creates a new instance of {@code CombinedConfigurationBuilder} and sets
* the specified initialization parameters.
* @param params a map with initialization parameters
*/
public CombinedConfigurationBuilder(final Map<String, Object> params)
{
super(CombinedConfiguration.class, params);
}
/**
*
* Creates a new instance of {@code CombinedConfigurationBuilder} and sets
* the specified initialization parameters and the <em>allowFailOnInit</em> flag.
* @param params a map with initialization parameters
* @param allowFailOnInit the <em>allowFailOnInit</em> flag
*/
public CombinedConfigurationBuilder(final Map<String, Object> params, final boolean allowFailOnInit)
{
super(CombinedConfiguration.class, params, allowFailOnInit);
}
/**
* Returns the {@code ConfigurationBuilder} which creates the definition
* configuration.
*
* @return the builder for the definition configuration
* @throws ConfigurationException if an error occurs
*/
public synchronized ConfigurationBuilder<? extends HierarchicalConfiguration<?>> getDefinitionBuilder()
throws ConfigurationException
{
if (definitionBuilder == null)
{
definitionBuilder = setupDefinitionBuilder(getParameters());
addDefinitionBuilderChangeListener(definitionBuilder);
}
return definitionBuilder;
}
/**
* {@inheritDoc} This method is overridden to adapt the return type.
*/
@Override
public CombinedConfigurationBuilder configure(final BuilderParameters... params)
{
super.configure(params);
return this;
}
/**
* <p>
* Returns the configuration builder with the given name. With this method a
* builder of a child configuration which was given a name in the
* configuration definition file can be accessed directly.
* </p>
* <p>
* <strong>Important note:</strong> This method only returns a meaningful
* result after the result configuration has been created by calling
* {@code getConfiguration()}. If called before, always an exception is
* thrown.
* </p>
*
* @param name the name of the builder in question
* @return the child configuration builder with this name
* @throws ConfigurationException if information about named builders is not
* yet available or no builder with this name exists
*/
public synchronized ConfigurationBuilder<? extends Configuration> getNamedBuilder(
final String name) throws ConfigurationException
{
if (sourceData == null)
{
throw new ConfigurationException("Information about child builders"
+ " has not been setup yet! Call getConfiguration() first.");
}
final ConfigurationBuilder<? extends Configuration> builder =
sourceData.getNamedBuilder(name);
if (builder == null)
{
throw new ConfigurationException("Builder cannot be resolved: "
+ name);
}
return builder;
}
/**
* <p>
* Returns a set with the names of all child configuration builders. A tag
* defining a configuration source in the configuration definition file can
* have the {@code config-name} attribute. If this attribute is present, the
* corresponding builder is assigned this name and can be directly accessed
* through the {@link #getNamedBuilder(String)} method. This method returns
* a collection with all available builder names.
* </p>
* <p>
* <strong>Important note:</strong> This method only returns a meaningful
* result after the result configuration has been created by calling
* {@code getConfiguration()}. If called before, always an empty set is
* returned.
* </p>
*
* @return a set with the names of all builders
*/
public synchronized Set<String> builderNames()
{
if (sourceData == null)
{
return Collections.emptySet();
}
return Collections.unmodifiableSet(sourceData.builderNames());
}
/**
* {@inheritDoc} This implementation resets some specific internal state of
* this builder.
*/
@Override
public synchronized void resetParameters()
{
super.resetParameters();
definitionBuilder = null;
definitionConfiguration = null;
currentParameters = null;
currentXMLParameters = null;
if (sourceData != null)
{
sourceData.cleanUp();
sourceData = null;
}
}
/**
* Obtains the {@code ConfigurationBuilder} object which provides access to
* the configuration containing the definition of the combined configuration
* to create. If a definition builder is defined in the parameters, it is
* used. Otherwise, we check whether the combined builder parameters object
* contains a parameters object for the definition builder. If this is the
* case, a builder for an {@code XMLConfiguration} is created and configured
* with this object. As a last resort, it is looked for a
* {@link FileBasedBuilderParametersImpl} object in the properties. If
* found, also a XML configuration builder is created which loads this file.
* Note: This method is called from a synchronized block.
*
* @param params the current parameters for this builder
* @return the builder for the definition configuration
* @throws ConfigurationException if an error occurs
*/
protected ConfigurationBuilder<? extends HierarchicalConfiguration<?>> setupDefinitionBuilder(
final Map<String, Object> params) throws ConfigurationException
{
final CombinedBuilderParametersImpl cbParams =
CombinedBuilderParametersImpl.fromParameters(params);
if (cbParams != null)
{
final ConfigurationBuilder<? extends HierarchicalConfiguration<?>> defBuilder =
cbParams.getDefinitionBuilder();
if (defBuilder != null)
{
return defBuilder;
}
if (cbParams.getDefinitionBuilderParameters() != null)
{
return createXMLDefinitionBuilder(cbParams
.getDefinitionBuilderParameters());
}
}
final BuilderParameters fileParams =
FileBasedBuilderParametersImpl.fromParameters(params);
if (fileParams != null)
{
return createXMLDefinitionBuilder(fileParams);
}
throw new ConfigurationException(
"No builder for configuration definition specified!");
}
/**
* Creates a default builder for the definition configuration and
* initializes it with a parameters object. This method is called if no
* definition builder is defined in this builder's parameters. This
* implementation creates a default file-based builder which produces an
* {@code XMLConfiguration}; it expects a corresponding file specification.
* Note: This method is called in a synchronized block.
*
* @param builderParams the parameters object for the builder
* @return the standard builder for the definition configuration
*/
protected ConfigurationBuilder<? extends HierarchicalConfiguration<?>> createXMLDefinitionBuilder(
final BuilderParameters builderParams)
{
return new FileBasedConfigurationBuilder<>(
XMLConfiguration.class).configure(builderParams);
}
/**
* Returns the configuration containing the definition of the combined
* configuration to be created. This method only returns a defined result
* during construction of the result configuration. The definition
* configuration is obtained from the definition builder at first access and
* then stored temporarily to ensure that during result construction always
* the same configuration instance is used. (Otherwise, it would be possible
* that the definition builder returns a different instance when queried
* multiple times.)
*
* @return the definition configuration
* @throws ConfigurationException if an error occurs
*/
protected HierarchicalConfiguration<?> getDefinitionConfiguration()
throws ConfigurationException
{
if (definitionConfiguration == null)
{
definitionConfiguration = getDefinitionBuilder().getConfiguration();
}
return definitionConfiguration;
}
/**
* Returns a collection with the builders for all child configuration
* sources. This method can be used by derived classes providing additional
* functionality on top of the declared configuration sources. It only
* returns a defined value during construction of the result configuration
* instance.
*
* @return a collection with the builders for child configuration sources
*/
protected synchronized Collection<ConfigurationBuilder<? extends Configuration>> getChildBuilders()
{
return sourceData.getChildBuilders();
}
/**
* {@inheritDoc} This implementation evaluates the {@code result} property
* of the definition configuration. It creates a combined bean declaration
* with both the properties specified in the definition file and the
* properties defined as initialization parameters.
*/
@Override
protected BeanDeclaration createResultDeclaration(final Map<String, Object> params)
throws ConfigurationException
{
final BeanDeclaration paramsDecl = super.createResultDeclaration(params);
final XMLBeanDeclaration resultDecl =
new XMLBeanDeclaration(getDefinitionConfiguration(),
KEY_RESULT, true, CombinedConfiguration.class.getName());
return new CombinedBeanDeclaration(resultDecl, paramsDecl);
}
/**
* {@inheritDoc} This implementation processes the definition configuration
* in order to
* <ul>
* <li>initialize the resulting {@code CombinedConfiguration}</li>
* <li>determine the builders for all configuration sources</li>
* <li>populate the resulting {@code CombinedConfiguration}</li>
* </ul>
*/
@Override
protected void initResultInstance(final CombinedConfiguration result)
throws ConfigurationException
{
super.initResultInstance(result);
currentConfiguration = result;
final HierarchicalConfiguration<?> config = getDefinitionConfiguration();
if (config.getMaxIndex(KEY_COMBINER) < 0)
{
// No combiner defined => set default
result.setNodeCombiner(new OverrideCombiner());
}
setUpCurrentParameters();
initNodeCombinerListNodes(result, config, KEY_OVERRIDE_LIST);
registerConfiguredProviders(config);
setUpCurrentXMLParameters();
currentXMLParameters.setFileSystem(initFileSystem(config));
initSystemProperties(config, getBasePath());
registerConfiguredLookups(config, result);
configureEntityResolver(config, currentXMLParameters);
setUpParentInterpolator(currentConfiguration, config);
final ConfigurationSourceData data = getSourceData();
final boolean createBuilders = data.getChildBuilders().isEmpty();
final List<ConfigurationBuilder<? extends Configuration>> overrideBuilders =
data.createAndAddConfigurations(result,
data.getOverrideSources(), data.overrideBuilders);
if (createBuilders)
{
data.overrideBuilders.addAll(overrideBuilders);
}
if (!data.getUnionSources().isEmpty())
{
final CombinedConfiguration addConfig = createAdditionalsConfiguration(result);
result.addConfiguration(addConfig, ADDITIONAL_NAME);
initNodeCombinerListNodes(addConfig, config, KEY_ADDITIONAL_LIST);
final List<ConfigurationBuilder<? extends Configuration>> unionBuilders =
data.createAndAddConfigurations(addConfig,
data.unionDeclarations, data.unionBuilders);
if (createBuilders)
{
data.unionBuilders.addAll(unionBuilders);
}
}
result.isEmpty(); // this sets up the node structure
currentConfiguration = null;
}
/**
* Creates the {@code CombinedConfiguration} for the configuration
* sources in the {@code &lt;additional&gt;} section. This method is
* called when the builder constructs the final configuration. It creates a
* new {@code CombinedConfiguration} and initializes some properties
* from the result configuration.
*
* @param resultConfig the result configuration (this is the configuration
* that will be returned by the builder)
* @return the {@code CombinedConfiguration} for the additional
* configuration sources
* @since 1.7
*/
protected CombinedConfiguration createAdditionalsConfiguration(
final CombinedConfiguration resultConfig)
{
final CombinedConfiguration addConfig =
new CombinedConfiguration(new UnionCombiner());
addConfig.setListDelimiterHandler(resultConfig.getListDelimiterHandler());
return addConfig;
}
/**
* Processes custom {@link Lookup} objects that might be declared in the
* definition configuration. Each {@code Lookup} object is registered at the
* definition configuration and at the result configuration. It is also
* added to all child configurations added to the resulting combined
* configuration.
*
* @param defConfig the definition configuration
* @param resultConfig the resulting configuration
* @throws ConfigurationException if an error occurs
*/
protected void registerConfiguredLookups(
final HierarchicalConfiguration<?> defConfig, final Configuration resultConfig)
throws ConfigurationException
{
final Map<String, Lookup> lookups = new HashMap<>();
final List<? extends HierarchicalConfiguration<?>> nodes =
defConfig.configurationsAt(KEY_CONFIGURATION_LOOKUPS);
for (final HierarchicalConfiguration<?> config : nodes)
{
final XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
final String key = config.getString(KEY_LOOKUP_KEY);
final Lookup lookup = (Lookup) fetchBeanHelper().createBean(decl);
lookups.put(key, lookup);
}
if (!lookups.isEmpty())
{
final ConfigurationInterpolator defCI = defConfig.getInterpolator();
if (defCI != null)
{
defCI.registerLookups(lookups);
}
resultConfig.getInterpolator().registerLookups(lookups);
}
}
/**
* Creates and initializes a default {@code FileSystem} if the definition
* configuration contains a corresponding declaration. The file system
* returned by this method is used as default for all file-based child
* configuration sources.
*
* @param config the definition configuration
* @return the default {@code FileSystem} (may be <b>null</b>)
* @throws ConfigurationException if an error occurs
*/
protected FileSystem initFileSystem(final HierarchicalConfiguration<?> config)
throws ConfigurationException
{
if (config.getMaxIndex(FILE_SYSTEM) == 0)
{
final XMLBeanDeclaration decl =
new XMLBeanDeclaration(config, FILE_SYSTEM);
return (FileSystem) fetchBeanHelper().createBean(decl);
}
return null;
}
/**
* Handles a file with system properties that may be defined in the
* definition configuration. If such property file is configured, all of its
* properties are added to the system properties.
*
* @param config the definition configuration
* @param basePath the base path defined for this builder (may be
* <b>null</b>)
* @throws ConfigurationException if an error occurs.
*/
protected void initSystemProperties(final HierarchicalConfiguration<?> config,
final String basePath) throws ConfigurationException
{
final String fileName = config.getString(KEY_SYSTEM_PROPS);
if (fileName != null)
{
try
{
SystemConfiguration.setSystemProperties(basePath, fileName);
}
catch (final Exception ex)
{
throw new ConfigurationException(
"Error setting system properties from " + fileName, ex);
}
}
}
/**
* Creates and initializes a default {@code EntityResolver} if the
* definition configuration contains a corresponding declaration.
*
* @param config the definition configuration
* @param xmlParams the (already partly initialized) object with XML
* parameters; here the new resolver is to be stored
* @throws ConfigurationException if an error occurs
*/
protected void configureEntityResolver(final HierarchicalConfiguration<?> config,
final XMLBuilderParametersImpl xmlParams) throws ConfigurationException
{
if (config.getMaxIndex(KEY_ENTITY_RESOLVER) == 0)
{
final XMLBeanDeclaration decl =
new XMLBeanDeclaration(config, KEY_ENTITY_RESOLVER, true);
final EntityResolver resolver =
(EntityResolver) fetchBeanHelper().createBean(decl,
CatalogResolver.class);
final FileSystem fileSystem = xmlParams.getFileHandler().getFileSystem();
if (fileSystem != null)
{
BeanHelper.setProperty(resolver, "fileSystem", fileSystem);
}
final String basePath = xmlParams.getFileHandler().getBasePath();
if (basePath != null)
{
BeanHelper.setProperty(resolver, "baseDir", basePath);
}
final ConfigurationInterpolator ci = new ConfigurationInterpolator();
ci.registerLookups(fetchPrefixLookups());
BeanHelper.setProperty(resolver, "interpolator", ci);
xmlParams.setEntityResolver(resolver);
}
}
/**
* Returns the {@code ConfigurationBuilderProvider} for the given tag. This
* method is called during creation of the result configuration. (It is not
* allowed to call it at another point of time; result is then
* unpredictable!) It supports all default providers and custom providers
* added through the parameters object as well.
*
* @param tagName the name of the tag
* @return the provider that was registered for this tag or <b>null</b> if
* there is none
*/
protected ConfigurationBuilderProvider providerForTag(final String tagName)
{
return currentParameters.providerForTag(tagName);
}
/**
* Initializes a parameters object for a child builder. This combined
* configuration builder has a bunch of properties which may be inherited by
* child configurations, e.g. the base path, the file system, etc. While
* processing the builders for child configurations, this method is called
* for each parameters object for a child builder. It initializes some
* properties of the passed in parameters objects which are derived from
* this parent builder.
*
* @param params the parameters object to be initialized
*/
protected void initChildBuilderParameters(final BuilderParameters params)
{
initDefaultChildParameters(params);
if (params instanceof BasicBuilderParameters)
{
initChildBasicParameters((BasicBuilderParameters) params);
}
if (params instanceof XMLBuilderProperties<?>)
{
initChildXMLParameters((XMLBuilderProperties<?>) params);
}
if (params instanceof FileBasedBuilderProperties<?>)
{
initChildFileBasedParameters((FileBasedBuilderProperties<?>) params);
}
if (params instanceof CombinedBuilderParametersImpl)
{
initChildCombinedParameters((CombinedBuilderParametersImpl) params);
}
}
/**
* Initializes the event listeners of the specified builder from this
* object. This method is used to inherit all listeners from a parent
* builder.
*
* @param dest the destination builder object which is to be initialized
*/
void initChildEventListeners(
final BasicConfigurationBuilder<? extends Configuration> dest)
{
copyEventListeners(dest);
}
/**
* Returns the configuration object that is currently constructed. This
* method can be called during construction of the result configuration. It
* is intended for internal usage, e.g. some specialized builder providers
* need access to this configuration to perform advanced initialization.
*
* @return the configuration that us currently under construction
*/
CombinedConfiguration getConfigurationUnderConstruction()
{
return currentConfiguration;
}
/**
* Initializes a bean using the current {@code BeanHelper}. This is needed
* by builder providers when the configuration objects for sub builders are
* constructed.
*
* @param bean the bean to be initialized
* @param decl the {@code BeanDeclaration}
*/
void initBean(final Object bean, final BeanDeclaration decl)
{
fetchBeanHelper().initBean(bean, decl);
}
/**
* Initializes the current parameters object. This object has either been
* passed at builder configuration time or it is newly created. In any
* case, it is manipulated during result creation.
*/
private void setUpCurrentParameters()
{
currentParameters =
CombinedBuilderParametersImpl.fromParameters(getParameters(), true);
currentParameters.registerMissingProviders(DEFAULT_PROVIDERS_MAP);
}
/**
* Sets up an XML parameters object which is used to store properties
* related to XML and file-based configurations during creation of the
* result configuration. The properties stored in this object can be
* inherited to child configurations.
*
* @throws ConfigurationException if an error occurs
*/
private void setUpCurrentXMLParameters() throws ConfigurationException
{
currentXMLParameters = new XMLBuilderParametersImpl();
initDefaultBasePath();
}
/**
* Sets up a parent {@code ConfigurationInterpolator} object. This object
* has a default {@link Lookup} querying the resulting combined
* configuration. Thus interpolation works globally across all configuration
* sources.
*
* @param resultConfig the result configuration
* @param defConfig the definition configuration
*/
private void setUpParentInterpolator(final Configuration resultConfig,
final Configuration defConfig)
{
parentInterpolator = new ConfigurationInterpolator();
parentInterpolator.addDefaultLookup(new ConfigurationLookup(
resultConfig));
final ConfigurationInterpolator defInterpolator = defConfig.getInterpolator();
if (defInterpolator != null)
{
defInterpolator.setParentInterpolator(parentInterpolator);
}
}
/**
* Initializes the default base path for all file-based child configuration
* sources. The base path can be explicitly defined in the parameters of
* this builder. Otherwise, if the definition builder is a file-based
* builder, it is obtained from there.
*
* @throws ConfigurationException if an error occurs
*/
private void initDefaultBasePath() throws ConfigurationException
{
assert currentParameters != null : "Current parameters undefined!";
if (currentParameters.getBasePath() != null)
{
currentXMLParameters.setBasePath(currentParameters.getBasePath());
}
else
{
final ConfigurationBuilder<? extends HierarchicalConfiguration<?>> defBuilder =
getDefinitionBuilder();
if (defBuilder instanceof FileBasedConfigurationBuilder)
{
@SuppressWarnings("rawtypes")
final
FileBasedConfigurationBuilder fileBuilder =
(FileBasedConfigurationBuilder) defBuilder;
final URL url = fileBuilder.getFileHandler().getURL();
currentXMLParameters.setBasePath(url != null ? url
.toExternalForm() : fileBuilder.getFileHandler()
.getBasePath());
}
}
}
/**
* Executes the {@link org.apache.commons.configuration2.builder.DefaultParametersManager
* DefaultParametersManager} stored in the current
* parameters on the passed in parameters object. If default handlers have been
* registered for this type of parameters, an initialization is now
* performed. This method is called before the parameters object is
* initialized from the configuration definition file. So default values
* can be overridden later with concrete property definitions.
*
* @param params the parameters to be initialized
* @throws org.apache.commons.configuration2.ex.ConfigurationRuntimeException if an error
* occurs when copying properties
*/
private void initDefaultChildParameters(final BuilderParameters params)
{
currentParameters.getChildDefaultParametersManager()
.initializeParameters(params);
}
/**
* Initializes basic builder parameters for a child configuration with
* default settings set for this builder. This implementation ensures that
* all {@code Lookup} objects are propagated to child configurations and
* interpolation is setup correctly.
*
* @param params the parameters object
*/
private void initChildBasicParameters(final BasicBuilderParameters params)
{
params.setPrefixLookups(fetchPrefixLookups());
params.setParentInterpolator(parentInterpolator);
if (currentParameters.isInheritSettings())
{
params.inheritFrom(getParameters());
}
}
/**
* Initializes a parameters object for a file-based configuration with
* properties already set for this parent builder. This method handles
* properties like a default file system or a base path.
*
* @param params the parameters object
*/
private void initChildFileBasedParameters(
final FileBasedBuilderProperties<?> params)
{
params.setBasePath(getBasePath());
params.setFileSystem(currentXMLParameters.getFileHandler()
.getFileSystem());
}
/**
* Initializes a parameters object for an XML configuration with properties
* already set for this parent builder.
*
* @param params the parameters object
*/
private void initChildXMLParameters(final XMLBuilderProperties<?> params)
{
params.setEntityResolver(currentXMLParameters.getEntityResolver());
}
/**
* Initializes a parameters object for a combined configuration builder with
* properties already set for this parent builder. This implementation deals
* only with a subset of properties. Other properties are already handled by
* the specialized builder provider.
*
* @param params the parameters object
*/
private void initChildCombinedParameters(
final CombinedBuilderParametersImpl params)
{
params.registerMissingProviders(currentParameters);
params.setBasePath(getBasePath());
}
/**
* Obtains the data object for the configuration sources and the
* corresponding builders. This object is created on first access and reset
* when the definition builder sends a change event. This method is called
* in a synchronized block.
*
* @return the object with information about configuration sources
* @throws ConfigurationException if an error occurs
*/
private ConfigurationSourceData getSourceData()
throws ConfigurationException
{
if (sourceData == null)
{
if (currentParameters == null)
{
setUpCurrentParameters();
setUpCurrentXMLParameters();
}
sourceData = createSourceData();
}
return sourceData;
}
/**
* Creates the data object for configuration sources and the corresponding
* builders.
*
* @return the newly created data object
* @throws ConfigurationException if an error occurs
*/
private ConfigurationSourceData createSourceData()
throws ConfigurationException
{
final ConfigurationSourceData result = new ConfigurationSourceData();
result.initFromDefinitionConfiguration(getDefinitionConfiguration());
return result;
}
/**
* Returns the current base path of this configuration builder. This is used
* for instance by all file-based child configurations.
*
* @return the base path
*/
private String getBasePath()
{
return currentXMLParameters.getFileHandler().getBasePath();
}
/**
* Registers providers defined in the configuration.
*
* @param defConfig the definition configuration
* @throws ConfigurationException if an error occurs
*/
private void registerConfiguredProviders(final HierarchicalConfiguration<?> defConfig)
throws ConfigurationException
{
final List<? extends HierarchicalConfiguration<?>> nodes =
defConfig.configurationsAt(KEY_CONFIGURATION_PROVIDERS);
for (final HierarchicalConfiguration<?> config : nodes)
{
final XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
final String key = config.getString(KEY_PROVIDER_KEY);
currentParameters.registerProvider(key,
(ConfigurationBuilderProvider) fetchBeanHelper().createBean(decl));
}
}
/**
* Adds a listener at the given definition builder which resets this builder
* when a reset of the definition builder happens. This way it is ensured
* that this builder produces a new combined configuration when its
* definition configuration changes.
*
* @param defBuilder the definition builder
*/
private void addDefinitionBuilderChangeListener(
final ConfigurationBuilder<? extends HierarchicalConfiguration<?>> defBuilder)
{
defBuilder.addEventListener(ConfigurationBuilderEvent.RESET,
event -> {
synchronized (CombinedConfigurationBuilder.this)
{
reset();
definitionBuilder = defBuilder;
}
});
}
/**
* Returns a map with the current prefix lookup objects. This map is
* obtained from the {@code ConfigurationInterpolator} of the configuration
* under construction.
*
* @return the map with current prefix lookups (may be <b>null</b>)
*/
private Map<String, ? extends Lookup> fetchPrefixLookups()
{
final CombinedConfiguration cc = getConfigurationUnderConstruction();
return cc != null ? cc.getInterpolator().getLookups() : null;
}
/**
* Creates {@code ConfigurationDeclaration} objects for the specified
* configurations.
*
* @param configs the list with configurations
* @return a collection with corresponding declarations
*/
private Collection<ConfigurationDeclaration> createDeclarations(
final Collection<? extends HierarchicalConfiguration<?>> configs)
{
final Collection<ConfigurationDeclaration> declarations =
new ArrayList<>(configs.size());
for (final HierarchicalConfiguration<?> c : configs)
{
declarations.add(new ConfigurationDeclaration(this, c));
}
return declarations;
}
/**
* Initializes the list nodes of the node combiner for the given combined
* configuration. This information can be set in the header section of the
* configuration definition file for both the override and the union
* combiners.
*
* @param cc the combined configuration to initialize
* @param defConfig the definition configuration
* @param key the key for the list nodes
*/
private static void initNodeCombinerListNodes(final CombinedConfiguration cc,
final HierarchicalConfiguration<?> defConfig, final String key)
{
final List<Object> listNodes = defConfig.getList(key);
for (final Object listNode : listNodes)
{
cc.getNodeCombiner().addListNode((String) listNode);
}
}
/**
* Creates the map with the default configuration builder providers.
*
* @return the map with default providers
*/
private static Map<String, ConfigurationBuilderProvider> createDefaultProviders()
{
final Map<String, ConfigurationBuilderProvider> providers =
new HashMap<>();
for (int i = 0; i < DEFAULT_TAGS.length; i++)
{
providers.put(DEFAULT_TAGS[i], DEFAULT_PROVIDERS[i]);
}
return providers;
}
static
{
DEFAULT_PROVIDERS_MAP = createDefaultProviders();
}
/**
* A data class for storing information about all configuration sources
* defined for a combined builder.
*/
private class ConfigurationSourceData
{
/** A list with data for all builders for override configurations. */
private final List<ConfigurationDeclaration> overrideDeclarations;
/** A list with data for all builders for union configurations. */
private final List<ConfigurationDeclaration> unionDeclarations;
/** A list with the builders for override configurations. */
private final List<ConfigurationBuilder<? extends Configuration>> overrideBuilders;
/** A list with the builders for union configurations. */
private final List<ConfigurationBuilder<? extends Configuration>> unionBuilders;
/** A map for direct access to a builder by its name. */
private final Map<String, ConfigurationBuilder<? extends Configuration>> namedBuilders;
/** A collection with all child builders. */
private final Collection<ConfigurationBuilder<? extends Configuration>> allBuilders;
/** A listener for reacting on changes of sub builders. */
private final EventListener<ConfigurationBuilderEvent> changeListener;
/**
* Creates a new instance of {@code ConfigurationSourceData}.
*/
public ConfigurationSourceData()
{
overrideDeclarations = new ArrayList<>();
unionDeclarations = new ArrayList<>();
overrideBuilders = new ArrayList<>();
unionBuilders = new ArrayList<>();
namedBuilders = new HashMap<>();
allBuilders = new LinkedList<>();
changeListener = createBuilderChangeListener();
}
/**
* Initializes this object from the specified definition configuration.
*
* @param config the definition configuration
* @throws ConfigurationException if an error occurs
*/
public void initFromDefinitionConfiguration(
final HierarchicalConfiguration<?> config) throws ConfigurationException
{
overrideDeclarations.addAll(createDeclarations(fetchTopLevelOverrideConfigs(config)));
overrideDeclarations.addAll(createDeclarations(config.childConfigurationsAt(KEY_OVERRIDE)));
unionDeclarations.addAll(createDeclarations(config.childConfigurationsAt(KEY_UNION)));
}
/**
* Processes the declaration of configuration builder providers, creates
* the corresponding builder if necessary, obtains configurations, and
* adds them to the specified result configuration.
*
* @param ccResult the result configuration
* @param srcDecl the collection with the declarations of configuration
* sources to process
* @return a list with configuration builders
* @throws ConfigurationException if an error occurs
*/
public List<ConfigurationBuilder<? extends Configuration>> createAndAddConfigurations(
final CombinedConfiguration ccResult,
final List<ConfigurationDeclaration> srcDecl,
final List<ConfigurationBuilder<? extends Configuration>> builders)
throws ConfigurationException
{
final boolean createBuilders = builders.isEmpty();
List<ConfigurationBuilder<? extends Configuration>> newBuilders;
if (createBuilders)
{
newBuilders = new ArrayList<>(srcDecl.size());
}
else
{
newBuilders = builders;
}
for (int i = 0; i < srcDecl.size(); i++)
{
ConfigurationBuilder<? extends Configuration> b;
if (createBuilders)
{
b = createConfigurationBuilder(srcDecl.get(i));
newBuilders.add(b);
}
else
{
b = builders.get(i);
}
addChildConfiguration(ccResult, srcDecl.get(i), b);
}
return newBuilders;
}
/**
* Frees resources used by this object and performs clean up. This
* method is called when the owning builder is reset.
*/
public void cleanUp()
{
for (final ConfigurationBuilder<?> b : getChildBuilders())
{
b.removeEventListener(ConfigurationBuilderEvent.RESET,
changeListener);
}
namedBuilders.clear();
}
/**
* Returns a collection containing the builders for all child
* configuration sources.
*
* @return the child configuration builders
*/
public Collection<ConfigurationBuilder<? extends Configuration>> getChildBuilders()
{
return allBuilders;
}
/**
* Returns a collection with all configuration source declarations
* defined in the override section.
*
* @return the override configuration builders
*/
public List<ConfigurationDeclaration> getOverrideSources()
{
return overrideDeclarations;
}
/**
* Returns a collection with all configuration source declarations
* defined in the union section.
*
* @return the union configuration builders
*/
public List<ConfigurationDeclaration> getUnionSources()
{
return unionDeclarations;
}
/**
* Returns the {@code ConfigurationBuilder} with the given name. If no
* such builder is defined in the definition configuration, result is
* <b>null</b>.
*
* @param name the name of the builder in question
* @return the builder with this name or <b>null</b>
*/
public ConfigurationBuilder<? extends Configuration> getNamedBuilder(
final String name)
{
return namedBuilders.get(name);
}
/**
* Returns a set with the names of all known named builders.
*
* @return the names of the available sub builders
*/
public Set<String> builderNames()
{
return namedBuilders.keySet();
}
/**
* Creates a configuration builder based on a source declaration in the
* definition configuration.
*
* @param decl the current {@code ConfigurationDeclaration}
* @return the newly created builder
* @throws ConfigurationException if an error occurs
*/
private ConfigurationBuilder<? extends Configuration> createConfigurationBuilder(
final ConfigurationDeclaration decl) throws ConfigurationException
{
final ConfigurationBuilderProvider provider =
providerForTag(decl.getConfiguration().getRootElementName());
if (provider == null)
{
throw new ConfigurationException(
"Unsupported configuration source: "
+ decl.getConfiguration().getRootElementName());
}
final ConfigurationBuilder<? extends Configuration> builder =
provider.getConfigurationBuilder(decl);
if (decl.getName() != null)
{
namedBuilders.put(decl.getName(), builder);
}
allBuilders.add(builder);
builder.addEventListener(ConfigurationBuilderEvent.RESET,
changeListener);
return builder;
}
/**
* Creates a new configuration using the specified builder and adds it
* to the resulting combined configuration.
*
* @param ccResult the resulting combined configuration
* @param decl the current {@code ConfigurationDeclaration}
* @param builder the configuration builder
* @throws ConfigurationException if an error occurs
*/
private void addChildConfiguration(final CombinedConfiguration ccResult,
final ConfigurationDeclaration decl,
final ConfigurationBuilder<? extends Configuration> builder)
throws ConfigurationException
{
try
{
ccResult.addConfiguration(
builder.getConfiguration(),
decl.getName(), decl.getAt());
}
catch (final ConfigurationException cex)
{
// ignore exceptions for optional configurations
if (!decl.isOptional())
{
throw cex;
}
}
}
/**
* Creates a listener for builder change events. This listener is
* registered at all builders for child configurations.
*/
private EventListener<ConfigurationBuilderEvent> createBuilderChangeListener()
{
return event -> resetResult();
}
/**
* Finds the override configurations that are defined as top level
* elements in the configuration definition file. This method fetches
* the child elements of the root node and removes the nodes that
* represent other configuration sections. The remaining nodes are
* treated as definitions for override configurations.
*
* @param config the definition configuration
* @return a list with sub configurations for the top level override
* configurations
*/
private List<? extends HierarchicalConfiguration<?>> fetchTopLevelOverrideConfigs(
final HierarchicalConfiguration<?> config)
{
final List<? extends HierarchicalConfiguration<?>> configs =
config.childConfigurationsAt(null);
for (final Iterator<? extends HierarchicalConfiguration<?>> it =
configs.iterator(); it.hasNext();)
{
final String nodeName = it.next().getRootElementName();
for (final String element : CONFIG_SECTIONS)
{
if (element.equals(nodeName))
{
it.remove();
break;
}
}
}
return configs;
}
}
}