001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.configuration;
018
019import java.io.File;
020import java.net.URL;
021import java.util.ArrayList;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.Iterator;
025import java.util.List;
026import java.util.Map;
027
028import org.apache.commons.configuration.beanutils.BeanDeclaration;
029import org.apache.commons.configuration.beanutils.BeanFactory;
030import org.apache.commons.configuration.beanutils.BeanHelper;
031import org.apache.commons.configuration.beanutils.DefaultBeanFactory;
032import org.apache.commons.configuration.beanutils.XMLBeanDeclaration;
033import org.apache.commons.configuration.event.ConfigurationErrorListener;
034import org.apache.commons.configuration.event.ConfigurationListener;
035import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
036import org.apache.commons.configuration.resolver.CatalogResolver;
037import org.apache.commons.configuration.resolver.EntityRegistry;
038import org.apache.commons.configuration.resolver.EntityResolverSupport;
039import org.apache.commons.configuration.tree.ConfigurationNode;
040import org.apache.commons.configuration.tree.DefaultExpressionEngine;
041import org.apache.commons.configuration.tree.OverrideCombiner;
042import org.apache.commons.configuration.tree.UnionCombiner;
043import org.apache.commons.lang.text.StrLookup;
044import org.apache.commons.logging.Log;
045import org.apache.commons.logging.LogFactory;
046import org.xml.sax.EntityResolver;
047
048/**
049 * <p>
050 * A factory class that creates a composite configuration from an XML based
051 * <em>configuration definition file</em>.
052 * </p>
053 * <p>
054 * This class provides an easy and flexible means for loading multiple
055 * configuration sources and combining the results into a single configuration
056 * object. The sources to be loaded are defined in an XML document that can
057 * contain certain tags representing the different supported configuration
058 * classes. If such a tag is found, the corresponding {@code Configuration}
059 * class is instantiated and initialized using the classes of the
060 * {@code beanutils} package (namely
061 * {@link org.apache.commons.configuration.beanutils.XMLBeanDeclaration XMLBeanDeclaration}
062 * will be used to extract the configuration's initialization parameters, which
063 * allows for complex initialization scenarios).
064 * </p>
065 * <p>
066 * It is also possible to add custom tags to the configuration definition file.
067 * For this purpose register your own {@code ConfigurationProvider}
068 * implementation for your tag using the {@code addConfigurationProvider()}
069 * method. This provider will then be called when the corresponding custom tag
070 * is detected. For the default configuration classes providers are already
071 * registered.
072 * </p>
073 * <p>
074 * The configuration definition file has the following basic structure:
075 * </p>
076 * <p>
077 *
078 * <pre>
079 * &lt;configuration systemProperties="properties file name"&gt;
080 *   &lt;header&gt;
081 *     &lt;!-- Optional meta information about the composite configuration --&gt;
082 *   &lt;/header&gt;
083 *   &lt;override&gt;
084 *     &lt;!-- Declarations for override configurations --&gt;
085 *   &lt;/override&gt;
086 *   &lt;additional&gt;
087 *     &lt;!-- Declarations for union configurations --&gt;
088 *   &lt;/additional&gt;
089 * &lt;/configuration&gt;
090 * </pre>
091 *
092 * </p>
093 * <p>
094 * The name of the root element (here {@code configuration}) is
095 * arbitrary. The optional systemProperties attribute identifies the path to
096 * a property file containing properties that should be added to the system
097 * properties. If specified on the root element, the system properties are
098 * set before the rest of the configuration is processed.
099 * </p>
100 * <p>
101 * There are two sections (both of them are optional) for declaring
102 * <em>override</em> and <em>additional</em> configurations. Configurations
103 * in the former section are evaluated in the order of their declaration, and
104 * properties of configurations declared earlier hide those of configurations
105 * declared later. Configurations in the latter section are combined to a union
106 * configuration, i.e. all of their properties are added to a large hierarchical
107 * configuration. Configuration declarations that occur as direct children of
108 * the root element are treated as override declarations.
109 * </p>
110 * <p>
111 * Each configuration declaration consists of a tag whose name is associated
112 * with a {@code ConfigurationProvider}. This can be one of the
113 * predefined tags like {@code properties}, or {@code xml}, or
114 * a custom tag, for which a configuration provider was registered. Attributes
115 * and sub elements with specific initialization parameters can be added. There
116 * are some reserved attributes with a special meaning that can be used in every
117 * configuration declaration:
118 * </p>
119 * <p>
120 * <table border="1">
121 * <tr>
122 * <th>Attribute</th>
123 * <th>Meaning</th>
124 * </tr>
125 * <tr>
126 * <td valign="top">{@code config-name}</td>
127 * <td>Allows to specify a name for this configuration. This name can be used
128 * to obtain a reference to the configuration from the resulting combined
129 * configuration (see below).</td>
130 * </tr>
131 * <tr>
132 * <td valign="top">{@code config-at}</td>
133 * <td>With this attribute an optional prefix can be specified for the
134 * properties of the corresponding configuration.</td>
135 * </tr>
136 * <tr>
137 * <td valign="top">{@code config-optional}</td>
138 * <td>Declares a configuration as optional. This means that errors that occur
139 * when creating the configuration are ignored. (However
140 * {@link org.apache.commons.configuration.event.ConfigurationErrorListener}s
141 * registered at the builder instance will get notified about this error: they
142 * receive an event of type {@code EVENT_ERR_LOAD_OPTIONAL}. The key
143 * property of this event contains the name of the optional configuration source
144 * that caused this problem.)</td>
145 * </tr>
146 * </table>
147 * </p>
148 * <p>
149 * The optional <em>header</em> section can contain some meta data about the
150 * created configuration itself. For instance, it is possible to set further
151 * properties of the {@code NodeCombiner} objects used for constructing
152 * the resulting configuration.
153 * </p>
154 * <p>
155 * The default configuration object returned by this builder is an instance of the
156 * {@link CombinedConfiguration} class. The return value of the
157 * {@code getConfiguration()} method can be casted to this type, and the
158 * {@code getConfiguration(boolean)} method directly declares
159 * {@code CombinedConfiguration} as return type. This allows for
160 * convenient access to the configuration objects maintained by the combined
161 * configuration (e.g. for updates of single configuration objects). It has also
162 * the advantage that the properties stored in all declared configuration
163 * objects are collected and transformed into a single hierarchical structure,
164 * which can be accessed using different expression engines. The actual CombinedConfiguration
165 * implementation can be overridden by specifying the class in the <em>config-class</em>
166 * attribute of the result element.
167 * </p>
168 * <p>
169 * A custom EntityResolver can be used for all XMLConfigurations by adding
170 * <pre>
171 * &lt;entity-resolver config-class="EntityResolver fully qualified class name"&gt;
172 * </pre>
173 * The CatalogResolver can be used for all XMLConfiguration by adding
174 * <pre>
175 * &lt;entity-resolver catalogFiles="comma separated list of catalog files"&gt;
176 * </pre>
177 * </p>
178 * <p>
179 * Additional ConfigurationProviders can be added by configuring them in the <em>header</em>
180 * section.
181 * <pre>
182 * &lt;providers&gt;
183 *   &lt;provider config-tag="tag name" config-class="provider fully qualified class name"/&gt;
184 * &lt;/providers&gt;
185 * </pre>
186 * </p>
187 * <p>
188 * Additional variable resolvers can be added by configuring them in the <em>header</em>
189 * section.
190 * <pre>
191 * &lt;lookups&gt;
192 *   &lt;lookup config-prefix="prefix" config-class="StrLookup fully qualified class name"/&gt;
193 * &lt;/lookups&gt;
194 * </pre>
195 * </p>
196 * <p>
197 * All declared override configurations are directly added to the resulting
198 * combined configuration. If they are given names (using the
199 * {@code config-name} attribute), they can directly be accessed using
200 * the {@code getConfiguration(String)} method of
201 * {@code CombinedConfiguration}. The additional configurations are
202 * altogether added to another combined configuration, which uses a union
203 * combiner. Then this union configuration is added to the resulting combined
204 * configuration under the name defined by the {@code ADDITIONAL_NAME}
205 * constant.
206 * </p>
207 * <p>
208 * Implementation note: This class is not thread-safe. Especially the
209 * {@code getConfiguration()} methods should be called by a single thread
210 * only.
211 * </p>
212 *
213 * @since 1.3
214 * @author <a
215 * href="http://commons.apache.org/configuration/team-list.html">Commons
216 * Configuration team</a>
217 * @version $Id: DefaultConfigurationBuilder.ConfigurationDeclaration.html 901729 2014-03-15 20:24:09Z oheger $
218 */
219public class DefaultConfigurationBuilder extends XMLConfiguration implements
220        ConfigurationBuilder
221{
222    /**
223     * Constant for the name of the additional configuration. If the
224     * configuration definition file contains an {@code additional}
225     * section, a special union configuration is created and added under this
226     * name to the resulting combined configuration.
227     */
228    public static final String ADDITIONAL_NAME = DefaultConfigurationBuilder.class
229            .getName()
230            + "/ADDITIONAL_CONFIG";
231
232    /**
233     * Constant for the type of error events caused by optional configurations
234     * that cannot be loaded.
235     */
236    public static final int EVENT_ERR_LOAD_OPTIONAL = 51;
237
238    /** Constant for the name of the configuration bean factory. */
239    static final String CONFIG_BEAN_FACTORY_NAME = DefaultConfigurationBuilder.class
240            .getName()
241            + ".CONFIG_BEAN_FACTORY_NAME";
242
243    /** Constant for the reserved name attribute. */
244    static final String ATTR_NAME = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
245            + XMLBeanDeclaration.RESERVED_PREFIX
246            + "name"
247            + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
248
249    /** Constant for the name of the at attribute. */
250    static final String ATTR_ATNAME = "at";
251
252    /** Constant for the reserved at attribute. */
253    static final String ATTR_AT_RES = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
254            + XMLBeanDeclaration.RESERVED_PREFIX
255            + ATTR_ATNAME
256            + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
257
258    /** Constant for the at attribute without the reserved prefix. */
259    static final String ATTR_AT = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
260            + ATTR_ATNAME + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
261
262    /** Constant for the name of the optional attribute. */
263    static final String ATTR_OPTIONALNAME = "optional";
264
265    /** Constant for the reserved optional attribute. */
266    static final String ATTR_OPTIONAL_RES = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
267            + XMLBeanDeclaration.RESERVED_PREFIX
268            + ATTR_OPTIONALNAME
269            + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
270
271    /** Constant for the optional attribute without the reserved prefix. */
272    static final String ATTR_OPTIONAL = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
273            + ATTR_OPTIONALNAME + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
274
275    /** Constant for the file name attribute. */
276    static final String ATTR_FILENAME = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
277            + "fileName" + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
278
279    /** Constant for the forceCreate attribute. */
280    static final String ATTR_FORCECREATE = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
281            + XMLBeanDeclaration.RESERVED_PREFIX
282            + "forceCreate"
283            + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
284
285    /**
286     * Constant for the tag attribute for providers.
287     */
288    static final String KEY_SYSTEM_PROPS = "[@systemProperties]";
289
290    /** Constant for the name of the header section. */
291    static final String SEC_HEADER = "header";
292
293    /** Constant for an expression that selects the union configurations. */
294    static final String KEY_UNION = "additional";
295
296    /** An array with the names of top level configuration sections.*/
297    static final String[] CONFIG_SECTIONS = {
298        "additional", "override", SEC_HEADER
299    };
300
301    /**
302     * Constant for an expression that selects override configurations in the
303     * override section.
304     */
305    static final String KEY_OVERRIDE = "override";
306
307    /**
308     * Constant for the key that points to the list nodes definition of the
309     * override combiner.
310     */
311    static final String KEY_OVERRIDE_LIST = SEC_HEADER
312            + ".combiner.override.list-nodes.node";
313
314    /**
315     * Constant for the key that points to the list nodes definition of the
316     * additional combiner.
317     */
318    static final String KEY_ADDITIONAL_LIST = SEC_HEADER
319            + ".combiner.additional.list-nodes.node";
320
321    /**
322     * Constant for the key for defining providers in the configuration file.
323     */
324    static final String KEY_CONFIGURATION_PROVIDERS = SEC_HEADER
325            + ".providers.provider";
326
327    /**
328     * Constant for the tag attribute for providers.
329     */
330    static final String KEY_PROVIDER_KEY = XMLBeanDeclaration.ATTR_PREFIX + "tag]";
331
332    /**
333     * Constant for the key for defining variable resolvers
334     */
335    static final String KEY_CONFIGURATION_LOOKUPS = SEC_HEADER
336            + ".lookups.lookup";
337
338    /**
339     * Constant for the key for defining entity resolvers
340     */
341    static final String KEY_ENTITY_RESOLVER = SEC_HEADER + ".entity-resolver";
342
343    /**
344     * Constant for the prefix attribute for lookups.
345     */
346    static final String KEY_LOOKUP_KEY = XMLBeanDeclaration.ATTR_PREFIX + "prefix]";
347
348    /**
349     * Constance for the FileSystem.
350     */
351    static final String FILE_SYSTEM = SEC_HEADER + ".fileSystem";
352
353    /**
354     * Constant for the key of the result declaration. This key can point to a
355     * bean declaration, which defines properties of the resulting combined
356     * configuration.
357     */
358    static final String KEY_RESULT = SEC_HEADER + ".result";
359
360    /** Constant for the key of the combiner in the result declaration.*/
361    static final String KEY_COMBINER = KEY_RESULT + ".nodeCombiner";
362
363    /** Constant for the XML file extension. */
364    static final String EXT_XML = ".xml";
365
366    /** Constant for the provider for properties files. */
367    private static final ConfigurationProvider PROPERTIES_PROVIDER = new FileExtensionConfigurationProvider(
368            XMLPropertiesConfiguration.class, PropertiesConfiguration.class,
369            EXT_XML);
370
371    /** Constant for the provider for XML files. */
372    private static final ConfigurationProvider XML_PROVIDER = new XMLConfigurationProvider();
373
374    /** Constant for the provider for JNDI sources. */
375    private static final ConfigurationProvider JNDI_PROVIDER = new ConfigurationProvider(
376            JNDIConfiguration.class);
377
378    /** Constant for the provider for system properties. */
379    private static final ConfigurationProvider SYSTEM_PROVIDER = new ConfigurationProvider(
380            SystemConfiguration.class);
381
382    /** Constant for the provider for ini files. */
383    private static final ConfigurationProvider INI_PROVIDER =
384            new FileConfigurationProvider(HierarchicalINIConfiguration.class);
385
386    /** Constant for the provider for environment properties. */
387    private static final ConfigurationProvider ENV_PROVIDER =
388            new ConfigurationProvider(EnvironmentConfiguration.class);
389
390    /** Constant for the provider for plist files. */
391    private static final ConfigurationProvider PLIST_PROVIDER = new FileExtensionConfigurationProvider(
392            "org.apache.commons.configuration.plist.XMLPropertyListConfiguration",
393            "org.apache.commons.configuration.plist.PropertyListConfiguration",
394            EXT_XML);
395
396    /** Constant for the provider for configuration definition files.*/
397    private static final ConfigurationProvider BUILDER_PROVIDER = new ConfigurationBuilderProvider();
398
399    /** An array with the names of the default tags. */
400    private static final String[] DEFAULT_TAGS = {
401            "properties", "xml", "hierarchicalXml", "jndi", "system", "plist",
402            "configuration", "ini", "env"
403    };
404
405    /** An array with the providers for the default tags. */
406    private static final ConfigurationProvider[] DEFAULT_PROVIDERS = {
407            PROPERTIES_PROVIDER, XML_PROVIDER, XML_PROVIDER, JNDI_PROVIDER,
408            SYSTEM_PROVIDER, PLIST_PROVIDER, BUILDER_PROVIDER, INI_PROVIDER,
409            ENV_PROVIDER
410    };
411
412    /**
413     * The serial version UID.
414     */
415    private static final long serialVersionUID = -3113777854714492123L;
416
417    /**
418     * A specialized {@code StrLookup} object which operates on the combined
419     * configuration constructed by this builder. This object is used as
420     * default lookup for {@code ConfigurationInterpolator} objects assigned to
421     * newly created configuration objects.
422     */
423    private final StrLookup combinedConfigLookup = new StrLookup()
424    {
425        @Override
426        public String lookup(String key)
427        {
428            if (constructedConfiguration != null)
429            {
430                Object value =
431                        constructedConfiguration.resolveContainerStore(key);
432                return (value != null) ? value.toString() : null;
433            }
434            return null;
435        }
436    };
437
438    /** Stores the configuration that is currently constructed.*/
439    private CombinedConfiguration constructedConfiguration;
440
441    /** Stores a map with the registered configuration providers. */
442    private final Map<String, ConfigurationProvider> providers;
443
444    /** Stores the base path to the configuration sources to load. */
445    private String configurationBasePath;
446
447    /**
448     * Creates a new instance of {@code DefaultConfigurationBuilder}. A
449     * configuration definition file is not yet loaded. Use the diverse setter
450     * methods provided by file based configurations to specify the
451     * configuration definition file.
452     */
453    public DefaultConfigurationBuilder()
454    {
455        super();
456        providers = new HashMap<String, ConfigurationProvider>();
457        registerDefaultProviders();
458        registerBeanFactory();
459        setLogger(LogFactory.getLog(getClass()));
460        addErrorLogListener();  // log errors per default
461    }
462
463    /**
464     * Creates a new instance of {@code DefaultConfigurationBuilder} and
465     * sets the specified configuration definition file.
466     *
467     * @param file the configuration definition file
468     */
469    public DefaultConfigurationBuilder(File file)
470    {
471        this();
472        setFile(file);
473    }
474
475    /**
476     * Creates a new instance of {@code DefaultConfigurationBuilder} and
477     * sets the specified configuration definition file.
478     *
479     * @param fileName the name of the configuration definition file
480     * @throws ConfigurationException if an error occurs when the file is loaded
481     */
482    public DefaultConfigurationBuilder(String fileName)
483            throws ConfigurationException
484    {
485        this();
486        setFileName(fileName);
487    }
488
489    /**
490     * Creates a new instance of {@code DefaultConfigurationBuilder} and
491     * sets the specified configuration definition file.
492     *
493     * @param url the URL to the configuration definition file
494     * @throws ConfigurationException if an error occurs when the file is loaded
495     */
496    public DefaultConfigurationBuilder(URL url) throws ConfigurationException
497    {
498        this();
499        setURL(url);
500    }
501
502    /**
503     * Returns the base path for the configuration sources to load. This path is
504     * used to resolve relative paths in the configuration definition file.
505     *
506     * @return the base path for configuration sources
507     */
508    public String getConfigurationBasePath()
509    {
510        return (configurationBasePath != null) ? configurationBasePath
511                : getBasePath();
512    }
513
514    /**
515     * Sets the base path for the configuration sources to load. Normally a base
516     * path need not to be set because it is determined by the location of the
517     * configuration definition file to load. All relative paths in this file
518     * are resolved relative to this file. Setting a base path makes sense if
519     * such relative paths should be otherwise resolved, e.g. if the
520     * configuration file is loaded from the class path and all sub
521     * configurations it refers to are stored in a special config directory.
522     *
523     * @param configurationBasePath the new base path to set
524     */
525    public void setConfigurationBasePath(String configurationBasePath)
526    {
527        this.configurationBasePath = configurationBasePath;
528    }
529
530    /**
531     * Adds a configuration provider for the specified tag. Whenever this tag is
532     * encountered in the configuration definition file this provider will be
533     * called to create the configuration object.
534     *
535     * @param tagName the name of the tag in the configuration definition file
536     * @param provider the provider for this tag
537     */
538    public void addConfigurationProvider(String tagName,
539            ConfigurationProvider provider)
540    {
541        if (tagName == null)
542        {
543            throw new IllegalArgumentException("Tag name must not be null!");
544        }
545        if (provider == null)
546        {
547            throw new IllegalArgumentException("Provider must not be null!");
548        }
549
550        providers.put(tagName, provider);
551    }
552
553    /**
554     * Removes the configuration provider for the specified tag name.
555     *
556     * @param tagName the tag name
557     * @return the removed configuration provider or <b>null</b> if none was
558     * registered for that tag
559     */
560    public ConfigurationProvider removeConfigurationProvider(String tagName)
561    {
562        return providers.remove(tagName);
563    }
564
565    /**
566     * Returns the configuration provider for the given tag.
567     *
568     * @param tagName the name of the tag
569     * @return the provider that was registered for this tag or <b>null</b> if
570     * there is none
571     */
572    public ConfigurationProvider providerForTag(String tagName)
573    {
574        return providers.get(tagName);
575    }
576
577    /**
578     * Returns the configuration provided by this builder. Loads and parses the
579     * configuration definition file and creates instances for the declared
580     * configurations.
581     *
582     * @return the configuration
583     * @throws ConfigurationException if an error occurs
584     */
585    public Configuration getConfiguration() throws ConfigurationException
586    {
587        return getConfiguration(true);
588    }
589
590    /**
591     * Returns the configuration provided by this builder. If the boolean
592     * parameter is <b>true</b>, the configuration definition file will be
593     * loaded. It will then be parsed, and instances for the declared
594     * configurations will be created.
595     *
596     * @param load a flag whether the configuration definition file should be
597     * loaded; a value of <b>false</b> would make sense if the file has already
598     * been created or its content was manipulated using some of the property
599     * accessor methods
600     * @return the configuration
601     * @throws ConfigurationException if an error occurs
602     */
603    public CombinedConfiguration getConfiguration(boolean load)
604            throws ConfigurationException
605    {
606        if (load)
607        {
608            load();
609        }
610
611        initFileSystem();
612        initSystemProperties();
613        configureEntityResolver();
614        registerConfiguredProviders();
615        registerConfiguredLookups();
616
617        CombinedConfiguration result = createResultConfiguration();
618        constructedConfiguration = result;
619
620        List<SubnodeConfiguration> overrides = fetchTopLevelOverrideConfigs();
621        overrides.addAll(fetchChildConfigs(KEY_OVERRIDE));
622        initCombinedConfiguration(result, overrides, KEY_OVERRIDE_LIST);
623
624        List<SubnodeConfiguration> additionals = fetchChildConfigs(KEY_UNION);
625        if (!additionals.isEmpty())
626        {
627            CombinedConfiguration addConfig = createAdditionalsConfiguration(result);
628            result.addConfiguration(addConfig, ADDITIONAL_NAME);
629            initCombinedConfiguration(addConfig, additionals,
630                    KEY_ADDITIONAL_LIST);
631        }
632
633        return result;
634    }
635
636    /**
637     * Creates the resulting combined configuration. This method is called by
638     * {@code getConfiguration()}. It checks whether the
639     * {@code header} section of the configuration definition file
640     * contains a {@code result} element. If this is the case, it will be
641     * used to initialize the properties of the newly created configuration
642     * object.
643     *
644     * @return the resulting configuration object
645     * @throws ConfigurationException if an error occurs
646     */
647    protected CombinedConfiguration createResultConfiguration()
648            throws ConfigurationException
649    {
650        XMLBeanDeclaration decl = new XMLBeanDeclaration(this, KEY_RESULT, true);
651        CombinedConfiguration result = (CombinedConfiguration) BeanHelper
652                .createBean(decl, CombinedConfiguration.class);
653
654        if (getMaxIndex(KEY_COMBINER) < 0)
655        {
656            // No combiner defined => set default
657            result.setNodeCombiner(new OverrideCombiner());
658        }
659
660        return result;
661    }
662
663    /**
664     * Creates the {@code CombinedConfiguration} for the configuration
665     * sources in the <code>&lt;additional&gt;</code> section. This method is
666     * called when the builder constructs the final configuration. It creates a
667     * new {@code CombinedConfiguration} and initializes some properties
668     * from the result configuration.
669     *
670     * @param resultConfig the result configuration (this is the configuration
671     *        that will be returned by the builder)
672     * @return the {@code CombinedConfiguration} for the additional
673     *         configuration sources
674     * @since 1.7
675     */
676    protected CombinedConfiguration createAdditionalsConfiguration(
677            CombinedConfiguration resultConfig)
678    {
679        CombinedConfiguration addConfig =
680                new CombinedConfiguration(new UnionCombiner());
681        addConfig.setDelimiterParsingDisabled(resultConfig
682                .isDelimiterParsingDisabled());
683        addConfig.setForceReloadCheck(resultConfig.isForceReloadCheck());
684        addConfig.setIgnoreReloadExceptions(resultConfig
685                .isIgnoreReloadExceptions());
686        return addConfig;
687    }
688
689    /**
690     * Initializes a combined configuration for the configurations of a specific
691     * section. This method is called for the override and for the additional
692     * section (if it exists).
693     *
694     * @param config the configuration to be initialized
695     * @param containedConfigs the list with the declarations of the contained
696     * configurations
697     * @param keyListNodes a list with the declaration of list nodes
698     * @throws ConfigurationException if an error occurs
699     */
700    protected void initCombinedConfiguration(CombinedConfiguration config,
701            List<? extends HierarchicalConfiguration> containedConfigs,
702            String keyListNodes) throws ConfigurationException
703    {
704        List<Object> listNodes = getList(keyListNodes);
705        for (Object listNode : listNodes)
706        {
707            config.getNodeCombiner().addListNode((String) listNode);
708        }
709
710        for (HierarchicalConfiguration conf : containedConfigs)
711        {
712            ConfigurationDeclaration decl = new ConfigurationDeclaration(this,
713                    conf);
714            if (getLogger().isDebugEnabled())
715            {
716                getLogger().debug("Creating configuration " + decl.getBeanClassName() + " with name "
717                    + decl.getConfiguration().getString(ATTR_NAME));
718            }
719            AbstractConfiguration newConf = createConfigurationAt(decl);
720            if (newConf != null)
721            {
722                config.addConfiguration(newConf, decl.getConfiguration()
723                        .getString(ATTR_NAME), decl.getAt());
724            }
725        }
726    }
727
728    /**
729     * Registers the default configuration providers supported by this class.
730     * This method will be called during initialization. It registers
731     * configuration providers for the tags that are supported by default.
732     */
733    protected void registerDefaultProviders()
734    {
735        for (int i = 0; i < DEFAULT_TAGS.length; i++)
736        {
737            addConfigurationProvider(DEFAULT_TAGS[i], DEFAULT_PROVIDERS[i]);
738        }
739    }
740
741    /**
742     * Registers providers defined in the configuration.
743     *
744     * @throws ConfigurationException if an error occurs
745     */
746    protected void registerConfiguredProviders() throws ConfigurationException
747    {
748        List<HierarchicalConfiguration> nodes = configurationsAt(KEY_CONFIGURATION_PROVIDERS);
749        for (HierarchicalConfiguration config : nodes)
750        {
751            XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
752            String key = config.getString(KEY_PROVIDER_KEY);
753            addConfigurationProvider(key, (ConfigurationProvider) BeanHelper
754                    .createBean(decl));
755        }
756    }
757
758    /**
759     * Registers StrLookups defined in the configuration.
760     *
761     * @throws ConfigurationException if an error occurs
762     */
763    protected void registerConfiguredLookups() throws ConfigurationException
764    {
765        List<HierarchicalConfiguration> nodes = configurationsAt(KEY_CONFIGURATION_LOOKUPS);
766        for (HierarchicalConfiguration config : nodes)
767        {
768            XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
769            String key = config.getString(KEY_LOOKUP_KEY);
770            StrLookup lookup = (StrLookup) BeanHelper.createBean(decl);
771            BeanHelper.setProperty(lookup, "configuration", this);
772            ConfigurationInterpolator.registerGlobalLookup(key, lookup);
773            this.getInterpolator().registerLookup(key, lookup);
774        }
775    }
776
777    protected void initFileSystem() throws ConfigurationException
778    {
779        if (getMaxIndex(FILE_SYSTEM) == 0)
780        {
781            HierarchicalConfiguration config = configurationAt(FILE_SYSTEM);
782            XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
783            setFileSystem((FileSystem) BeanHelper.createBean(decl));
784        }
785    }
786
787    /**
788     * If a property file is configured add the properties to the System properties.
789     * @throws ConfigurationException if an error occurs.
790     */
791    protected void initSystemProperties() throws ConfigurationException
792    {
793        String fileName = getString(KEY_SYSTEM_PROPS);
794        if (fileName != null)
795        {
796            try
797            {
798               SystemConfiguration.setSystemProperties(getConfigurationBasePath(), fileName);
799            }
800            catch (Exception ex)
801            {
802                throw new ConfigurationException("Error setting system properties from " + fileName, ex);
803            }
804
805        }
806    }
807
808    protected void configureEntityResolver() throws ConfigurationException
809    {
810        if (getMaxIndex(KEY_ENTITY_RESOLVER) == 0)
811        {
812            XMLBeanDeclaration decl = new XMLBeanDeclaration(this, KEY_ENTITY_RESOLVER, true);
813            EntityResolver resolver = (EntityResolver) BeanHelper.createBean(decl, CatalogResolver.class);
814            BeanHelper.setProperty(resolver, "fileSystem", getFileSystem());
815            BeanHelper.setProperty(resolver, "baseDir", getBasePath());
816            BeanHelper.setProperty(resolver, "substitutor", getSubstitutor());
817            setEntityResolver(resolver);
818        }
819    }
820
821    /**
822     * Performs interpolation. This method will not only take this configuration
823     * instance into account (which is the one that loaded the configuration
824     * definition file), but also the so far constructed combined configuration.
825     * So variables can be used that point to properties that are defined in
826     * configuration sources loaded by this builder.
827     *
828     * @param value the value to be interpolated
829     * @return the interpolated value
830     */
831    @Override
832    protected Object interpolate(Object value)
833    {
834        Object result = super.interpolate(value);
835        if (constructedConfiguration != null)
836        {
837            result = constructedConfiguration.interpolate(result);
838        }
839        return result;
840    }
841
842    /**
843     * Creates a configuration object from the specified configuration
844     * declaration.
845     *
846     * @param decl the configuration declaration
847     * @return the new configuration object
848     * @throws ConfigurationException if an error occurs
849     */
850    private AbstractConfiguration createConfigurationAt(
851            ConfigurationDeclaration decl) throws ConfigurationException
852    {
853        try
854        {
855            return (AbstractConfiguration) BeanHelper.createBean(decl);
856        }
857        catch (Exception ex)
858        {
859            // redirect to configuration exceptions
860            throw new ConfigurationException(ex);
861        }
862    }
863
864    /**
865     * Returns a list with {@code SubnodeConfiguration} objects for the
866     * child nodes of the specified configuration node.
867     *
868     * @param node the start node
869     * @return a list with subnode configurations for the node's children
870     */
871    private List<SubnodeConfiguration> fetchChildConfigs(ConfigurationNode node)
872    {
873        List<ConfigurationNode> children = node.getChildren();
874        List<SubnodeConfiguration> result = new ArrayList<SubnodeConfiguration>(children.size());
875        for (ConfigurationNode child : children)
876        {
877            result.add(createSubnodeConfiguration(child));
878        }
879        return result;
880    }
881
882    /**
883     * Returns a list with {@code SubnodeConfiguration} objects for the
884     * child nodes of the node specified by the given key.
885     *
886     * @param key the key (must define exactly one node)
887     * @return a list with subnode configurations for the node's children
888     */
889    private List<SubnodeConfiguration> fetchChildConfigs(String key)
890    {
891        List<ConfigurationNode> nodes = fetchNodeList(key);
892        if (nodes.size() > 0)
893        {
894            return fetchChildConfigs(nodes.get(0));
895        }
896        else
897        {
898            return Collections.emptyList();
899        }
900    }
901
902    /**
903     * Finds the override configurations that are defined as top level elements
904     * in the configuration definition file. This method will fetch the child
905     * elements of the root node and remove the nodes that represent other
906     * configuration sections. The remaining nodes are treated as definitions
907     * for override configurations.
908     *
909     * @return a list with subnode configurations for the top level override
910     * configurations
911     */
912    private List<SubnodeConfiguration> fetchTopLevelOverrideConfigs()
913    {
914        List<SubnodeConfiguration> configs = fetchChildConfigs(getRootNode());
915        for (Iterator<SubnodeConfiguration> it = configs.iterator(); it.hasNext();)
916        {
917            String nodeName = it.next().getRootNode().getName();
918            for (int i = 0; i < CONFIG_SECTIONS.length; i++)
919            {
920                if (CONFIG_SECTIONS[i].equals(nodeName))
921                {
922                    it.remove();
923                    break;
924                }
925            }
926        }
927        return configs;
928    }
929
930    /**
931     * Registers the bean factory used by this class if necessary. This method
932     * is called by the constructor to ensure that the required bean factory is
933     * available.
934     */
935    private void registerBeanFactory()
936    {
937        synchronized (DefaultConfigurationBuilder.class)
938        {
939            if (!BeanHelper.registeredFactoryNames().contains(
940                    CONFIG_BEAN_FACTORY_NAME))
941            {
942                BeanHelper.registerBeanFactory(CONFIG_BEAN_FACTORY_NAME,
943                        new ConfigurationBeanFactory());
944            }
945        }
946    }
947
948    /**
949     * <p>
950     * A base class for creating and initializing configuration sources.
951     * </p>
952     * <p>
953     * Concrete sub classes of this base class are responsible for creating
954     * specific {@code Configuration} objects for the tags in the
955     * configuration definition file. The configuration factory will parse the
956     * definition file and try to find a matching
957     * {@code ConfigurationProvider} for each encountered tag. This
958     * provider is then asked to create a corresponding
959     * {@code Configuration} object. It is up to a concrete
960     * implementation how this object is created and initialized.
961     * </p>
962     * <p>
963     * Note that at the moment only configuration classes derived from
964     * {@link AbstractConfiguration} are supported.
965     * </p>
966     */
967    public static class ConfigurationProvider extends DefaultBeanFactory
968    {
969        /** Stores the class of the configuration to be created. */
970        private Class<?> configurationClass;
971
972        /** Stores the name of the configuration class to be created.*/
973        private String configurationClassName;
974
975        /**
976         * Creates a new uninitialized instance of {@code ConfigurationProvider}.
977         */
978        public ConfigurationProvider()
979        {
980            this((Class<?>) null);
981        }
982
983        /**
984         * Creates a new instance of {@code ConfigurationProvider} and
985         * sets the class of the configuration created by this provider.
986         *
987         * @param configClass the configuration class
988         */
989        public ConfigurationProvider(Class<?> configClass)
990        {
991            setConfigurationClass(configClass);
992        }
993
994        /**
995         * Creates a new instance of {@code ConfigurationProvider} and
996         * sets the name of the class of the configuration created by this
997         * provider.
998         *
999         * @param configClassName the name of the configuration class
1000         * @since 1.4
1001         */
1002        public ConfigurationProvider(String configClassName)
1003        {
1004            setConfigurationClassName(configClassName);
1005        }
1006
1007        /**
1008         * Returns the class of the configuration returned by this provider.
1009         *
1010         * @return the class of the provided configuration
1011         */
1012        public Class<?> getConfigurationClass()
1013        {
1014            return configurationClass;
1015        }
1016
1017        /**
1018         * Sets the class of the configuration returned by this provider.
1019         *
1020         * @param configurationClass the configuration class
1021         */
1022        public void setConfigurationClass(Class<?> configurationClass)
1023        {
1024            this.configurationClass = configurationClass;
1025        }
1026
1027        /**
1028         * Returns the name of the configuration class returned by this
1029         * provider.
1030         *
1031         * @return the configuration class name
1032         * @since 1.4
1033         */
1034        public String getConfigurationClassName()
1035        {
1036            return configurationClassName;
1037        }
1038
1039        /**
1040         * Sets the name of the configuration class returned by this provider.
1041         *
1042         * @param configurationClassName the name of the configuration class
1043         * @since 1.4
1044         */
1045        public void setConfigurationClassName(String configurationClassName)
1046        {
1047            this.configurationClassName = configurationClassName;
1048        }
1049
1050        /**
1051         * Returns the configuration. This method is called to fetch the
1052         * configuration from the provider. This implementation will call the
1053         * inherited {@link
1054         * org.apache.commons.configuration.beanutils.DefaultBeanFactory#createBean(Class, BeanDeclaration, Object)
1055         * createBean()} method to create a new instance of the
1056         * configuration class.
1057         *
1058         * @param decl the bean declaration with initialization parameters for
1059         * the configuration
1060         * @return the new configuration object
1061         * @throws Exception if an error occurs
1062         */
1063        public AbstractConfiguration getConfiguration(
1064                ConfigurationDeclaration decl) throws Exception
1065        {
1066            return (AbstractConfiguration) createBean(fetchConfigurationClass(),
1067                    decl, null);
1068        }
1069
1070        /**
1071         * Returns an uninitialized configuration of the represented type. This
1072         * method will be called for optional configurations when the
1073         * {@code getConfiguration()} method caused an error and the
1074         * {@code forceCreate} attribute is set. A concrete sub class can
1075         * here try to create an uninitialized, empty configuration, which may
1076         * be possible if the error was created during initialization. This base
1077         * implementation just returns <b>null</b>.
1078         *
1079         * @param decl the bean declaration with initialization parameters for
1080         * the configuration
1081         * @return the new configuration object
1082         * @throws Exception if an error occurs
1083         * @since 1.4
1084         */
1085        public AbstractConfiguration getEmptyConfiguration(
1086                ConfigurationDeclaration decl) throws Exception
1087        {
1088            return null;
1089        }
1090
1091        /**
1092         * Returns the configuration class supported by this provider. If a
1093         * class object was set, it is returned. Otherwise the method tries to
1094         * resolve the class name.
1095         *
1096         * @return the class of the configuration to be created
1097         * @since 1.4
1098         */
1099        protected synchronized Class<?> fetchConfigurationClass() throws Exception
1100        {
1101            if (getConfigurationClass() == null)
1102            {
1103                setConfigurationClass(loadClass(getConfigurationClassName()));
1104            }
1105            return getConfigurationClass();
1106        }
1107
1108        /**
1109         * Loads the class with the specified name dynamically. If the class's
1110         * name is <b>null</b>, <b>null</b> will also be returned.
1111         *
1112         * @param className the name of the class to be loaded
1113         * @return the class object
1114         * @throws ClassNotFoundException if class loading fails
1115         * @since 1.4
1116         */
1117        protected Class<?> loadClass(String className)
1118                throws ClassNotFoundException
1119        {
1120            return (className != null) ? Class.forName(className, true,
1121                    getClass().getClassLoader()) : null;
1122        }
1123    }
1124
1125    /**
1126     * <p>
1127     * A specialized {@code BeanDeclaration} implementation that
1128     * represents the declaration of a configuration source.
1129     * </p>
1130     * <p>
1131     * Instances of this class are able to extract all information about a
1132     * configuration source from the configuration definition file. The
1133     * declaration of a configuration source is very similar to a bean
1134     * declaration processed by {@code XMLBeanDeclaration}. There are
1135     * very few differences, e.g. some reserved attributes like
1136     * {@code optional} and {@code at} and the fact that a bean
1137     * factory is never needed.
1138     * </p>
1139     */
1140    public static class ConfigurationDeclaration extends XMLBeanDeclaration
1141    {
1142        /** Stores a reference to the associated configuration builder. */
1143        private DefaultConfigurationBuilder configurationBuilder;
1144
1145        /**
1146         * Creates a new instance of {@code ConfigurationDeclaration} and
1147         * initializes it.
1148         *
1149         * @param builder the associated configuration builder
1150         * @param config the configuration this declaration is based onto
1151         */
1152        public ConfigurationDeclaration(DefaultConfigurationBuilder builder,
1153                HierarchicalConfiguration config)
1154        {
1155            super(config);
1156            configurationBuilder = builder;
1157        }
1158
1159        /**
1160         * Returns the associated configuration builder.
1161         *
1162         * @return the configuration builder
1163         */
1164        public DefaultConfigurationBuilder getConfigurationBuilder()
1165        {
1166            return configurationBuilder;
1167        }
1168
1169        /**
1170         * Returns the value of the {@code at} attribute.
1171         *
1172         * @return the value of the {@code at} attribute (can be <b>null</b>)
1173         */
1174        public String getAt()
1175        {
1176            String result = this.getConfiguration().getString(ATTR_AT_RES);
1177            return (result == null) ? this.getConfiguration().getString(ATTR_AT)
1178                    : result;
1179        }
1180
1181        /**
1182         * Returns a flag whether this is an optional configuration.
1183         *
1184         * @return a flag if this declaration points to an optional
1185         * configuration
1186         */
1187        public boolean isOptional()
1188        {
1189            Boolean value = this.getConfiguration().getBoolean(ATTR_OPTIONAL_RES,
1190                    null);
1191            if (value == null)
1192            {
1193                value = this.getConfiguration().getBoolean(ATTR_OPTIONAL,
1194                        Boolean.FALSE);
1195            }
1196            return value.booleanValue();
1197        }
1198
1199        /**
1200         * Returns a flag whether this configuration should always be created
1201         * and added to the resulting combined configuration. This flag is
1202         * evaluated only for optional configurations whose normal creation has
1203         * caused an error. If for such a configuration the
1204         * {@code forceCreate} attribute is set and the corresponding
1205         * configuration provider supports this mode, an empty configuration
1206         * will be created and added to the resulting combined configuration.
1207         *
1208         * @return the value of the {@code forceCreate} attribute
1209         * @since 1.4
1210         */
1211        public boolean isForceCreate()
1212        {
1213            return this.getConfiguration().getBoolean(ATTR_FORCECREATE, false);
1214        }
1215
1216        /**
1217         * Returns the name of the bean factory. For configuration source
1218         * declarations always a reserved factory is used. This factory's name
1219         * is returned by this implementation.
1220         *
1221         * @return the name of the bean factory
1222         */
1223        @Override
1224        public String getBeanFactoryName()
1225        {
1226            return CONFIG_BEAN_FACTORY_NAME;
1227        }
1228
1229        /**
1230         * Returns the bean's class name. This implementation will always return
1231         * <b>null</b>.
1232         *
1233         * @return the name of the bean's class
1234         */
1235        @Override
1236        public String getBeanClassName()
1237        {
1238            return null;
1239        }
1240
1241        /**
1242         * Checks whether the given node is reserved. This method will take
1243         * further reserved attributes into account
1244         *
1245         * @param nd the node
1246         * @return a flag whether this node is reserved
1247         */
1248        @Override
1249        protected boolean isReservedNode(ConfigurationNode nd)
1250        {
1251            if (super.isReservedNode(nd))
1252            {
1253                return true;
1254            }
1255
1256            return nd.isAttribute()
1257                    && ((ATTR_ATNAME.equals(nd.getName()) && nd.getParentNode()
1258                            .getAttributeCount(RESERVED_PREFIX + ATTR_ATNAME) == 0) || (ATTR_OPTIONALNAME
1259                            .equals(nd.getName()) && nd.getParentNode()
1260                            .getAttributeCount(RESERVED_PREFIX + ATTR_OPTIONALNAME) == 0));
1261        }
1262
1263        /**
1264         * Performs interpolation. This implementation will delegate
1265         * interpolation to the configuration builder, which takes care that the
1266         * currently constructed configuration is taken into account, too.
1267         *
1268         * @param value the value to be interpolated
1269         * @return the interpolated value
1270         */
1271        @Override
1272        protected Object interpolate(Object value)
1273        {
1274            return getConfigurationBuilder().interpolate(value);
1275        }
1276    }
1277
1278    /**
1279     * A specialized {@code BeanFactory} implementation that handles
1280     * configuration declarations. This class will retrieve the correct
1281     * configuration provider and delegate the task of creating the
1282     * configuration to this object.
1283     */
1284    static class ConfigurationBeanFactory implements BeanFactory
1285    {
1286        /** The logger. */
1287        private Log logger = LogFactory.getLog(DefaultConfigurationBuilder.class);
1288
1289        /**
1290         * Creates an instance of a bean class. This implementation expects that
1291         * the passed in bean declaration is a declaration for a configuration.
1292         * It will determine the responsible configuration provider and delegate
1293         * the call to this instance. If creation of the configuration fails
1294         * and the {@code optional} attribute is set, the exception will
1295         * be ignored. If the {@code forceCreate} attribute is set, too,
1296         * the provider is asked to create an empty configuration. A return
1297         * value of <b>null</b> means that no configuration could be created.
1298         *
1299         * @param beanClass the bean class (will be ignored)
1300         * @param data the declaration
1301         * @param param an additional parameter (will be ignored)
1302         * @return the newly created configuration
1303         * @throws Exception if an error occurs
1304         */
1305        public Object createBean(Class<?> beanClass, BeanDeclaration data,
1306                Object param) throws Exception
1307        {
1308            ConfigurationDeclaration decl = (ConfigurationDeclaration) data;
1309            String tagName = decl.getNode().getName();
1310            ConfigurationProvider provider = decl.getConfigurationBuilder()
1311                    .providerForTag(tagName);
1312            if (provider == null)
1313            {
1314                throw new ConfigurationRuntimeException(
1315                        "No ConfigurationProvider registered for tag "
1316                                + tagName);
1317            }
1318
1319            try
1320            {
1321                AbstractConfiguration config = provider.getConfiguration(decl);
1322                installInterpolator(decl, config);
1323                return config;
1324            }
1325            catch (Exception ex)
1326            {
1327                // If this is an optional configuration, ignore the exception
1328                if (!decl.isOptional())
1329                {
1330                    throw ex;
1331                }
1332                else
1333                {
1334                    if (logger.isDebugEnabled())
1335                    {
1336                        logger.debug("Load failed for optional configuration " + tagName + ": "
1337                            + ex.getMessage());
1338                    }
1339                    // Notify registered error listeners
1340                    decl.getConfigurationBuilder().fireError(
1341                            EVENT_ERR_LOAD_OPTIONAL,
1342                            decl.getConfiguration().getString(ATTR_NAME), null,
1343                            ex);
1344
1345                    if (decl.isForceCreate())
1346                    {
1347                        try
1348                        {
1349                            return provider.getEmptyConfiguration(decl);
1350                        }
1351                        catch (Exception ex2)
1352                        {
1353                            // Ignore exception, return null in this case
1354                            logger.warn("Could not create instance of optional configuration "
1355                                    + tagName, ex2);
1356                        }
1357                    }
1358                    return null;
1359                }
1360            }
1361        }
1362
1363        /**
1364         * Returns the default class for this bean factory.
1365         *
1366         * @return the default class
1367         */
1368        public Class<?> getDefaultBeanClass()
1369        {
1370            // Here some valid class must be returned, otherwise BeanHelper
1371            // will complain that the bean's class cannot be determined
1372            return Configuration.class;
1373        }
1374
1375        /**
1376         * Installs a specialized {@code ConfigurationInterpolator} on a newly
1377         * created configuration which also takes the combined configuration
1378         * created by the builder into account. With this
1379         * {@code ConfigurationInterpolator} the interpolation facilities of
1380         * this child configuration are extended to include all other
1381         * configurations created by this builder.
1382         *
1383         * @param decl the {@code ConfigurationDeclaration}
1384         * @param config the newly created configuration instance
1385         */
1386        private void installInterpolator(ConfigurationDeclaration decl,
1387                AbstractConfiguration config)
1388        {
1389            ConfigurationInterpolator parent = new ConfigurationInterpolator();
1390            parent.setDefaultLookup(decl.getConfigurationBuilder().combinedConfigLookup);
1391            config.getInterpolator().setParentInterpolator(parent);
1392        }
1393    }
1394
1395    /**
1396     * A specialized provider implementation that deals with file based
1397     * configurations. Ensures that the base path is correctly set and that the
1398     * load() method gets called.
1399     */
1400    public static class FileConfigurationProvider extends ConfigurationProvider
1401    {
1402        /**
1403         * Creates a new instance of {@code FileConfigurationProvider}.
1404         */
1405        public FileConfigurationProvider()
1406        {
1407            super();
1408        }
1409
1410        /**
1411         * Creates a new instance of {@code FileConfigurationProvider}
1412         * and sets the configuration class.
1413         *
1414         * @param configClass the class for the configurations to be created
1415         */
1416        public FileConfigurationProvider(Class<?> configClass)
1417        {
1418            super(configClass);
1419        }
1420
1421        /**
1422         * Creates a new instance of {@code FileConfigurationProvider}
1423         * and sets the configuration class name.
1424         *
1425         * @param configClassName the name of the configuration to be created
1426         * @since 1.4
1427         */
1428        public FileConfigurationProvider(String configClassName)
1429        {
1430            super(configClassName);
1431        }
1432
1433        /**
1434         * Creates the configuration. After that {@code load()} will be
1435         * called. If this configuration is marked as optional, exceptions will
1436         * be ignored.
1437         *
1438         * @param decl the declaration
1439         * @return the new configuration
1440         * @throws Exception if an error occurs
1441         */
1442        @Override
1443        public AbstractConfiguration getConfiguration(
1444                ConfigurationDeclaration decl) throws Exception
1445        {
1446            AbstractConfiguration result = getEmptyConfiguration(decl);
1447            if (result instanceof FileSystemBased)
1448            {
1449                DefaultConfigurationBuilder builder = decl.getConfigurationBuilder();
1450                if (builder.getFileSystem() != null)
1451                {
1452                    ((FileSystemBased) result).setFileSystem(builder.getFileSystem());
1453                }
1454            }
1455            ((FileConfiguration) result).load();
1456            return result;
1457        }
1458
1459        /**
1460         * Returns an uninitialized file configuration. This method will be
1461         * called for optional configurations when the
1462         * {@code getConfiguration()} method caused an error and the
1463         * {@code forceCreate} attribute is set. It will create the
1464         * configuration of the represented type, but the {@code load()}
1465         * method won't be called. This way non-existing configuration files can
1466         * be handled gracefully: If loading a the file fails, an empty
1467         * configuration will be created that is already configured with the
1468         * correct file name.
1469         *
1470         * @param decl the bean declaration with initialization parameters for
1471         * the configuration
1472         * @return the new configuration object
1473         * @throws Exception if an error occurs
1474         * @since 1.4
1475         */
1476        @Override
1477        public AbstractConfiguration getEmptyConfiguration(
1478                ConfigurationDeclaration decl) throws Exception
1479        {
1480            AbstractConfiguration config = super.getConfiguration(decl);
1481
1482            /**
1483             * Some wrapper classes may need to pass the EntityResolver to XMLConfigurations
1484             * they construct buy may not be an XMLConfiguration.
1485             */
1486            if (config instanceof EntityResolverSupport)
1487            {
1488                DefaultConfigurationBuilder builder = decl.getConfigurationBuilder();
1489                EntityResolver resolver = builder.getEntityResolver();
1490                ((EntityResolverSupport) config).setEntityResolver(resolver);
1491            }
1492
1493            return config;
1494        }
1495
1496        /**
1497         * Initializes the bean instance. Ensures that the file configuration's
1498         * base path will be initialized with the base path of the factory so
1499         * that relative path names can be correctly resolved.
1500         *
1501         * @param bean the bean to be initialized
1502         * @param data the declaration
1503         * @throws Exception if an error occurs
1504         */
1505        @Override
1506        protected void initBeanInstance(Object bean, BeanDeclaration data)
1507                throws Exception
1508        {
1509            FileConfiguration config = (FileConfiguration) bean;
1510            config.setBasePath(((ConfigurationDeclaration) data)
1511                    .getConfigurationBuilder().getConfigurationBasePath());
1512            super.initBeanInstance(bean, data);
1513        }
1514    }
1515
1516    /**
1517     * A specialized configuration provider for XML configurations. This
1518     * implementation acts like a {@code FileConfigurationProvider}, but
1519     * it will copy all entity IDs that have been registered for the
1520     * configuration builder to the new XML configuration before it is loaded.
1521     *
1522     * @since 1.6
1523     */
1524    public static class XMLConfigurationProvider extends FileConfigurationProvider
1525    {
1526        /**
1527         * Creates a new instance of {@code XMLConfigurationProvider}.
1528         */
1529        public XMLConfigurationProvider()
1530        {
1531            super(XMLConfiguration.class);
1532        }
1533
1534        /**
1535         * Returns a new empty configuration instance. This implementation
1536         * performs some additional initialization specific to XML
1537         * configurations.
1538         *
1539         * @param decl the configuration declaration
1540         * @return the new configuration
1541         * @throws Exception if an error occurs
1542         */
1543        @Override
1544        public AbstractConfiguration getEmptyConfiguration(
1545                ConfigurationDeclaration decl) throws Exception
1546        {
1547            XMLConfiguration config = (XMLConfiguration) super
1548                    .getEmptyConfiguration(decl);
1549
1550            DefaultConfigurationBuilder builder = decl
1551                    .getConfigurationBuilder();
1552            EntityResolver resolver = builder.getEntityResolver();
1553            if (resolver instanceof EntityRegistry)
1554            {
1555                // copy the registered entities
1556                config.getRegisteredEntities().putAll(
1557                    builder.getRegisteredEntities());
1558            }
1559            else
1560            {
1561                config.setEntityResolver(resolver);
1562            }
1563            return config;
1564        }
1565    }
1566
1567    /**
1568     * A specialized configuration provider for file based configurations that
1569     * can handle configuration sources whose concrete type depends on the
1570     * extension of the file to be loaded. One example is the
1571     * {@code properties} tag: if the file ends with ".xml" a
1572     * XMLPropertiesConfiguration object must be created, otherwise a
1573     * PropertiesConfiguration object.
1574     */
1575    static class FileExtensionConfigurationProvider extends
1576            FileConfigurationProvider
1577    {
1578        /**
1579         * Stores the class to be created when the file extension matches.
1580         */
1581        private Class<?> matchingClass;
1582
1583        /**
1584         * Stores the name of the class to be created when the file extension
1585         * matches.
1586         */
1587        private String matchingClassName;
1588
1589        /**
1590         * Stores the class to be created when the file extension does not
1591         * match.
1592         */
1593        private Class<?> defaultClass;
1594
1595        /**
1596         * Stores the name of the class to be created when the file extension
1597         * does not match.
1598         */
1599        private String defaultClassName;
1600
1601        /** Stores the file extension to be checked against. */
1602        private String fileExtension;
1603
1604        /**
1605         * Creates a new instance of
1606         * {@code FileExtensionConfigurationProvider} and initializes it.
1607         *
1608         * @param matchingClass the class to be created when the file extension
1609         * matches
1610         * @param defaultClass the class to be created when the file extension
1611         * does not match
1612         * @param extension the file extension to be checked against
1613         */
1614        public FileExtensionConfigurationProvider(Class<?> matchingClass,
1615                Class<?> defaultClass, String extension)
1616        {
1617            this.matchingClass = matchingClass;
1618            this.defaultClass = defaultClass;
1619            fileExtension = extension;
1620        }
1621
1622        /**
1623         * Creates a new instance of
1624         * {@code FileExtensionConfigurationProvider} and initializes it
1625         * with the names of the classes to be created.
1626         *
1627         * @param matchingClassName the name of the class to be created when the
1628         * file extension matches
1629         * @param defaultClassName the name of the class to be created when the
1630         * file extension does not match
1631         * @param extension the file extension to be checked against
1632         * @since 1.4
1633         */
1634        public FileExtensionConfigurationProvider(String matchingClassName,
1635                String defaultClassName, String extension)
1636        {
1637            this.matchingClassName = matchingClassName;
1638            this.defaultClassName = defaultClassName;
1639            fileExtension = extension;
1640        }
1641
1642        /**
1643         * Returns the matching class object, no matter whether it was defined
1644         * as a class or as a class name.
1645         *
1646         * @return the matching class object
1647         * @throws Exception if an error occurs
1648         * @since 1.4
1649         */
1650        protected synchronized Class<?> fetchMatchingClass() throws Exception
1651        {
1652            if (matchingClass == null)
1653            {
1654                matchingClass = loadClass(matchingClassName);
1655            }
1656            return matchingClass;
1657        }
1658
1659        /**
1660         * Returns the default class object, no matter whether it was defined as
1661         * a class or as a class name.
1662         *
1663         * @return the default class object
1664         * @throws Exception if an error occurs
1665         * @since 1.4
1666         */
1667        protected synchronized Class<?> fetchDefaultClass() throws Exception
1668        {
1669            if (defaultClass == null)
1670            {
1671                defaultClass = loadClass(defaultClassName);
1672            }
1673            return defaultClass;
1674        }
1675
1676        /**
1677         * Creates the configuration object. The class is determined by the file
1678         * name's extension.
1679         *
1680         * @param beanClass the class
1681         * @param data the bean declaration
1682         * @return the new bean
1683         * @throws Exception if an error occurs
1684         */
1685        @Override
1686        protected Object createBeanInstance(Class<?> beanClass,
1687                BeanDeclaration data) throws Exception
1688        {
1689            String fileName = ((ConfigurationDeclaration) data)
1690                    .getConfiguration().getString(ATTR_FILENAME);
1691            if (fileName != null
1692                    && fileName.toLowerCase().trim().endsWith(fileExtension))
1693            {
1694                return super.createBeanInstance(fetchMatchingClass(), data);
1695            }
1696            else
1697            {
1698                return super.createBeanInstance(fetchDefaultClass(), data);
1699            }
1700        }
1701    }
1702
1703    /**
1704     * A specialized configuration provider class that allows to include other
1705     * configuration definition files.
1706     */
1707    static class ConfigurationBuilderProvider extends ConfigurationProvider
1708    {
1709        /**
1710         * Creates a new instance of {@code ConfigurationBuilderProvider}.
1711         */
1712        public ConfigurationBuilderProvider()
1713        {
1714            super(DefaultConfigurationBuilder.class);
1715        }
1716
1717        /**
1718         * Creates the configuration. First creates a configuration builder
1719         * object. Then returns the configuration created by this builder.
1720         *
1721         * @param decl the configuration declaration
1722         * @return the configuration
1723         * @exception Exception if an error occurs
1724         */
1725        @Override
1726        public AbstractConfiguration getConfiguration(
1727                ConfigurationDeclaration decl) throws Exception
1728        {
1729            DefaultConfigurationBuilder builder = (DefaultConfigurationBuilder) super
1730                    .getConfiguration(decl);
1731            return builder.getConfiguration(true);
1732        }
1733
1734        /**
1735         * Returns an empty configuration in case of an optional configuration
1736         * could not be created. This implementation returns an empty combined
1737         * configuration.
1738         *
1739         * @param decl the configuration declaration
1740         * @return the configuration
1741         * @exception Exception if an error occurs
1742         * @since 1.4
1743         */
1744        @Override
1745        public AbstractConfiguration getEmptyConfiguration(
1746                ConfigurationDeclaration decl) throws Exception
1747        {
1748            return new CombinedConfiguration();
1749        }
1750
1751        /**
1752         * {@inheritDoc} This implementation ensures that the configuration
1753         * builder created by this provider inherits the properties from the
1754         * current configuration builder.
1755         */
1756        @Override
1757        protected void initBeanInstance(Object bean, BeanDeclaration data)
1758                throws Exception
1759        {
1760            ConfigurationDeclaration decl = (ConfigurationDeclaration) data;
1761            initChildBuilder(decl.getConfigurationBuilder(),
1762                    (DefaultConfigurationBuilder) bean);
1763            super.initBeanInstance(bean, data);
1764        }
1765
1766        /**
1767         * Initializes the given child configuration builder from its parent
1768         * builder. This method copies the values of some properties from the
1769         * parent builder to the child builder so that the child inherits
1770         * properties from its parent.
1771         *
1772         * @param parent the parent builder
1773         * @param child the child builder
1774         */
1775        private static void initChildBuilder(
1776                DefaultConfigurationBuilder parent,
1777                DefaultConfigurationBuilder child)
1778        {
1779            child.setAttributeSplittingDisabled(parent
1780                    .isAttributeSplittingDisabled());
1781            child.setBasePath(parent.getBasePath());
1782            child.setDelimiterParsingDisabled(parent
1783                    .isDelimiterParsingDisabled());
1784            child.setListDelimiter(parent.getListDelimiter());
1785            child.setThrowExceptionOnMissing(parent.isThrowExceptionOnMissing());
1786            child.setLogger(parent.getLogger());
1787
1788            child.clearConfigurationListeners();
1789            for (ConfigurationListener l : parent.getConfigurationListeners())
1790            {
1791                child.addConfigurationListener(l);
1792            }
1793            child.clearErrorListeners();
1794            for (ConfigurationErrorListener l : parent.getErrorListeners())
1795            {
1796                child.addErrorListener(l);
1797            }
1798        }
1799    }
1800}