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     */
017    package org.apache.commons.configuration;
018    
019    import java.io.File;
020    import java.net.URL;
021    import java.util.ArrayList;
022    import java.util.Collections;
023    import java.util.HashMap;
024    import java.util.Iterator;
025    import java.util.List;
026    import java.util.Map;
027    
028    import org.apache.commons.configuration.beanutils.BeanDeclaration;
029    import org.apache.commons.configuration.beanutils.BeanFactory;
030    import org.apache.commons.configuration.beanutils.BeanHelper;
031    import org.apache.commons.configuration.beanutils.DefaultBeanFactory;
032    import org.apache.commons.configuration.beanutils.XMLBeanDeclaration;
033    import org.apache.commons.configuration.event.ConfigurationErrorListener;
034    import org.apache.commons.configuration.event.ConfigurationListener;
035    import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
036    import org.apache.commons.configuration.resolver.CatalogResolver;
037    import org.apache.commons.configuration.resolver.EntityRegistry;
038    import org.apache.commons.configuration.resolver.EntityResolverSupport;
039    import org.apache.commons.configuration.tree.ConfigurationNode;
040    import org.apache.commons.configuration.tree.DefaultExpressionEngine;
041    import org.apache.commons.configuration.tree.OverrideCombiner;
042    import org.apache.commons.configuration.tree.UnionCombiner;
043    import org.apache.commons.lang.text.StrLookup;
044    import org.apache.commons.logging.Log;
045    import org.apache.commons.logging.LogFactory;
046    import 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</code>
059     * class is instantiated and initialized using the classes of the
060     * <code>beanutils</code> package (namely
061     * <code>{@link org.apache.commons.configuration.beanutils.XMLBeanDeclaration XMLBeanDeclaration}</code>
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</code>
068     * implementation for your tag using the <code>addConfigurationProvider()</code>
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</code>) 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</code>. This can be one of the
113     * predefined tags like <code>properties</code>, or <code>xml</code>, 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</code></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</code></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</code></td>
138     * <td>Declares a configuration as optional. This means that errors that occur
139     * when creating the configuration are ignored. (However
140     * <code>{@link org.apache.commons.configuration.event.ConfigurationErrorListener}</code>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</code>. 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</code> 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     * <code>{@link CombinedConfiguration}</code> class. The return value of the
157     * <code>getConfiguration()</code> method can be casted to this type, and the
158     * <code>getConfiguration(boolean)</code> method directly declares
159     * <code>CombinedConfiguration</code> 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</code> attribute), they can directly be accessed using
200     * the <code>getConfiguration(String)</code> method of
201     * <code>CombinedConfiguration</code>. 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</code>
205     * constant.
206     * </p>
207     * <p>
208     * Implementation note: This class is not thread-safe. Especially the
209     * <code>getConfiguration()</code> 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.java 1158119 2011-08-16 06:20:57Z oheger $
218     */
219    public 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</code>
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        /** Stores the configuration that is currently constructed.*/
418        private CombinedConfiguration constructedConfiguration;
419    
420        /** Stores a map with the registered configuration providers. */
421        private Map providers;
422    
423        /** Stores the base path to the configuration sources to load. */
424        private String configurationBasePath;
425    
426        /**
427         * Creates a new instance of <code>DefaultConfigurationBuilder</code>. A
428         * configuration definition file is not yet loaded. Use the diverse setter
429         * methods provided by file based configurations to specify the
430         * configuration definition file.
431         */
432        public DefaultConfigurationBuilder()
433        {
434            super();
435            providers = new HashMap();
436            registerDefaultProviders();
437            registerBeanFactory();
438            setLogger(LogFactory.getLog(getClass()));
439            addErrorLogListener();  // log errors per default
440        }
441    
442        /**
443         * Creates a new instance of <code>DefaultConfigurationBuilder</code> and
444         * sets the specified configuration definition file.
445         *
446         * @param file the configuration definition file
447         */
448        public DefaultConfigurationBuilder(File file)
449        {
450            this();
451            setFile(file);
452        }
453    
454        /**
455         * Creates a new instance of <code>DefaultConfigurationBuilder</code> and
456         * sets the specified configuration definition file.
457         *
458         * @param fileName the name of the configuration definition file
459         * @throws ConfigurationException if an error occurs when the file is loaded
460         */
461        public DefaultConfigurationBuilder(String fileName)
462                throws ConfigurationException
463        {
464            this();
465            setFileName(fileName);
466        }
467    
468        /**
469         * Creates a new instance of <code>DefaultConfigurationBuilder</code> and
470         * sets the specified configuration definition file.
471         *
472         * @param url the URL to the configuration definition file
473         * @throws ConfigurationException if an error occurs when the file is loaded
474         */
475        public DefaultConfigurationBuilder(URL url) throws ConfigurationException
476        {
477            this();
478            setURL(url);
479        }
480    
481        /**
482         * Returns the base path for the configuration sources to load. This path is
483         * used to resolve relative paths in the configuration definition file.
484         *
485         * @return the base path for configuration sources
486         */
487        public String getConfigurationBasePath()
488        {
489            return (configurationBasePath != null) ? configurationBasePath
490                    : getBasePath();
491        }
492    
493        /**
494         * Sets the base path for the configuration sources to load. Normally a base
495         * path need not to be set because it is determined by the location of the
496         * configuration definition file to load. All relative paths in this file
497         * are resolved relative to this file. Setting a base path makes sense if
498         * such relative paths should be otherwise resolved, e.g. if the
499         * configuration file is loaded from the class path and all sub
500         * configurations it refers to are stored in a special config directory.
501         *
502         * @param configurationBasePath the new base path to set
503         */
504        public void setConfigurationBasePath(String configurationBasePath)
505        {
506            this.configurationBasePath = configurationBasePath;
507        }
508    
509        /**
510         * Adds a configuration provider for the specified tag. Whenever this tag is
511         * encountered in the configuration definition file this provider will be
512         * called to create the configuration object.
513         *
514         * @param tagName the name of the tag in the configuration definition file
515         * @param provider the provider for this tag
516         */
517        public void addConfigurationProvider(String tagName,
518                ConfigurationProvider provider)
519        {
520            if (tagName == null)
521            {
522                throw new IllegalArgumentException("Tag name must not be null!");
523            }
524            if (provider == null)
525            {
526                throw new IllegalArgumentException("Provider must not be null!");
527            }
528    
529            providers.put(tagName, provider);
530        }
531    
532        /**
533         * Removes the configuration provider for the specified tag name.
534         *
535         * @param tagName the tag name
536         * @return the removed configuration provider or <b>null</b> if none was
537         * registered for that tag
538         */
539        public ConfigurationProvider removeConfigurationProvider(String tagName)
540        {
541            return (ConfigurationProvider) providers.remove(tagName);
542        }
543    
544        /**
545         * Returns the configuration provider for the given tag.
546         *
547         * @param tagName the name of the tag
548         * @return the provider that was registered for this tag or <b>null</b> if
549         * there is none
550         */
551        public ConfigurationProvider providerForTag(String tagName)
552        {
553            return (ConfigurationProvider) providers.get(tagName);
554        }
555    
556        /**
557         * Returns the configuration provided by this builder. Loads and parses the
558         * configuration definition file and creates instances for the declared
559         * configurations.
560         *
561         * @return the configuration
562         * @throws ConfigurationException if an error occurs
563         */
564        public Configuration getConfiguration() throws ConfigurationException
565        {
566            return getConfiguration(true);
567        }
568    
569        /**
570         * Returns the configuration provided by this builder. If the boolean
571         * parameter is <b>true</b>, the configuration definition file will be
572         * loaded. It will then be parsed, and instances for the declared
573         * configurations will be created.
574         *
575         * @param load a flag whether the configuration definition file should be
576         * loaded; a value of <b>false</b> would make sense if the file has already
577         * been created or its content was manipulated using some of the property
578         * accessor methods
579         * @return the configuration
580         * @throws ConfigurationException if an error occurs
581         */
582        public CombinedConfiguration getConfiguration(boolean load)
583                throws ConfigurationException
584        {
585            if (load)
586            {
587                load();
588            }
589    
590            initFileSystem();
591            initSystemProperties();
592            configureEntityResolver();
593            registerConfiguredProviders();
594            registerConfiguredLookups();
595    
596            CombinedConfiguration result = createResultConfiguration();
597            constructedConfiguration = result;
598    
599            List overrides = fetchTopLevelOverrideConfigs();
600            overrides.addAll(fetchChildConfigs(KEY_OVERRIDE));
601            initCombinedConfiguration(result, overrides, KEY_OVERRIDE_LIST);
602    
603            List additionals = fetchChildConfigs(KEY_UNION);
604            if (!additionals.isEmpty())
605            {
606                CombinedConfiguration addConfig = createAdditionalsConfiguration(result);
607                result.addConfiguration(addConfig, ADDITIONAL_NAME);
608                initCombinedConfiguration(addConfig, additionals,
609                        KEY_ADDITIONAL_LIST);
610            }
611    
612            return result;
613        }
614    
615        /**
616         * Creates the resulting combined configuration. This method is called by
617         * <code>getConfiguration()</code>. It checks whether the
618         * <code>header</code> section of the configuration definition file
619         * contains a <code>result</code> element. If this is the case, it will be
620         * used to initialize the properties of the newly created configuration
621         * object.
622         *
623         * @return the resulting configuration object
624         * @throws ConfigurationException if an error occurs
625         */
626        protected CombinedConfiguration createResultConfiguration()
627                throws ConfigurationException
628        {
629            XMLBeanDeclaration decl = new XMLBeanDeclaration(this, KEY_RESULT, true);
630            CombinedConfiguration result = (CombinedConfiguration) BeanHelper
631                    .createBean(decl, CombinedConfiguration.class);
632    
633            if (getMaxIndex(KEY_COMBINER) < 0)
634            {
635                // No combiner defined => set default
636                result.setNodeCombiner(new OverrideCombiner());
637            }
638    
639            return result;
640        }
641    
642        /**
643         * Creates the <code>CombinedConfiguration</code> for the configuration
644         * sources in the <code>&lt;additional&gt;</code> section. This method is
645         * called when the builder constructs the final configuration. It creates a
646         * new <code>CombinedConfiguration</code> and initializes some properties
647         * from the result configuration.
648         *
649         * @param resultConfig the result configuration (this is the configuration
650         *        that will be returned by the builder)
651         * @return the <code>CombinedConfiguration</code> for the additional
652         *         configuration sources
653         * @since 1.7
654         */
655        protected CombinedConfiguration createAdditionalsConfiguration(
656                CombinedConfiguration resultConfig)
657        {
658            CombinedConfiguration addConfig =
659                    new CombinedConfiguration(new UnionCombiner());
660            addConfig.setDelimiterParsingDisabled(resultConfig
661                    .isDelimiterParsingDisabled());
662            addConfig.setForceReloadCheck(resultConfig.isForceReloadCheck());
663            addConfig.setIgnoreReloadExceptions(resultConfig
664                    .isIgnoreReloadExceptions());
665            return addConfig;
666        }
667    
668        /**
669         * Initializes a combined configuration for the configurations of a specific
670         * section. This method is called for the override and for the additional
671         * section (if it exists).
672         *
673         * @param config the configuration to be initialized
674         * @param containedConfigs the list with the declarations of the contained
675         * configurations
676         * @param keyListNodes a list with the declaration of list nodes
677         * @throws ConfigurationException if an error occurs
678         */
679        protected void initCombinedConfiguration(CombinedConfiguration config,
680                List containedConfigs, String keyListNodes) throws ConfigurationException
681        {
682            List listNodes = getList(keyListNodes);
683            for (Iterator it = listNodes.iterator(); it.hasNext();)
684            {
685                config.getNodeCombiner().addListNode((String) it.next());
686            }
687    
688            for (Iterator it = containedConfigs.iterator(); it.hasNext();)
689            {
690                HierarchicalConfiguration conf = (HierarchicalConfiguration) it
691                        .next();
692                ConfigurationDeclaration decl = new ConfigurationDeclaration(this,
693                        conf);
694                if (getLogger().isDebugEnabled())
695                {
696                    getLogger().debug("Creating configuration " + decl.getBeanClassName() + " with name "
697                        + decl.getConfiguration().getString(ATTR_NAME));
698                }
699                AbstractConfiguration newConf = createConfigurationAt(decl);
700                if (newConf != null)
701                {
702                    config.addConfiguration(newConf, decl.getConfiguration()
703                            .getString(ATTR_NAME), decl.getAt());
704                }
705            }
706        }
707    
708        /**
709         * Registers the default configuration providers supported by this class.
710         * This method will be called during initialization. It registers
711         * configuration providers for the tags that are supported by default.
712         */
713        protected void registerDefaultProviders()
714        {
715            for (int i = 0; i < DEFAULT_TAGS.length; i++)
716            {
717                addConfigurationProvider(DEFAULT_TAGS[i], DEFAULT_PROVIDERS[i]);
718            }
719        }
720    
721        /**
722         * Registers providers defined in the configuration.
723         *
724         * @throws ConfigurationException if an error occurs
725         */
726        protected void registerConfiguredProviders() throws ConfigurationException
727        {
728            List nodes = configurationsAt(KEY_CONFIGURATION_PROVIDERS);
729            for (Iterator it = nodes.iterator(); it.hasNext();)
730            {
731                HierarchicalConfiguration config = (HierarchicalConfiguration) it.next();
732                XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
733                String key = config.getString(KEY_PROVIDER_KEY);
734                addConfigurationProvider(key, (ConfigurationProvider) BeanHelper
735                        .createBean(decl));
736            }
737        }
738    
739        /**
740         * Registers StrLookups defined in the configuration.
741         *
742         * @throws ConfigurationException if an error occurs
743         */
744        protected void registerConfiguredLookups() throws ConfigurationException
745        {
746            List nodes = configurationsAt(KEY_CONFIGURATION_LOOKUPS);
747            for (Iterator it = nodes.iterator(); it.hasNext();)
748            {
749                HierarchicalConfiguration config = (HierarchicalConfiguration) it.next();
750                XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
751                String key = config.getString(KEY_LOOKUP_KEY);
752                StrLookup lookup = (StrLookup) BeanHelper.createBean(decl);
753                BeanHelper.setProperty(lookup, "configuration", this);
754                ConfigurationInterpolator.registerGlobalLookup(key, lookup);
755                this.getInterpolator().registerLookup(key, lookup);
756            }
757        }
758    
759        protected void initFileSystem() throws ConfigurationException
760        {
761            if (getMaxIndex(FILE_SYSTEM) == 0)
762            {
763                HierarchicalConfiguration config = configurationAt(FILE_SYSTEM);
764                XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
765                setFileSystem((FileSystem) BeanHelper.createBean(decl));
766            }
767        }
768    
769        /**
770         * If a property file is configured add the properties to the System properties.
771         * @throws ConfigurationException if an error occurs.
772         */
773        protected void initSystemProperties() throws ConfigurationException
774        {
775            String fileName = getString(KEY_SYSTEM_PROPS);
776            if (fileName != null)
777            {
778                try
779                {
780                   SystemConfiguration.setSystemProperties(getConfigurationBasePath(), fileName);
781                }
782                catch (Exception ex)
783                {
784                    throw new ConfigurationException("Error setting system properties from " + fileName, ex);
785                }
786    
787            }
788        }
789    
790        protected void configureEntityResolver() throws ConfigurationException
791        {
792            if (getMaxIndex(KEY_ENTITY_RESOLVER) == 0)
793            {
794                XMLBeanDeclaration decl = new XMLBeanDeclaration(this, KEY_ENTITY_RESOLVER, true);
795                EntityResolver resolver = (EntityResolver) BeanHelper.createBean(decl, CatalogResolver.class);
796                BeanHelper.setProperty(resolver, "fileSystem", getFileSystem());
797                BeanHelper.setProperty(resolver, "baseDir", getBasePath());
798                BeanHelper.setProperty(resolver, "substitutor", getSubstitutor());
799                setEntityResolver(resolver);
800            }
801        }
802    
803        /**
804         * Performs interpolation. This method will not only take this configuration
805         * instance into account (which is the one that loaded the configuration
806         * definition file), but also the so far constructed combined configuration.
807         * So variables can be used that point to properties that are defined in
808         * configuration sources loaded by this builder.
809         *
810         * @param value the value to be interpolated
811         * @return the interpolated value
812         */
813        protected Object interpolate(Object value)
814        {
815            Object result = super.interpolate(value);
816            if (constructedConfiguration != null)
817            {
818                result = constructedConfiguration.interpolate(result);
819            }
820            return result;
821        }
822    
823        protected void fireError(int type, String propName, Object propValue,
824                Throwable ex)
825        {
826            // This method is only overridden to fix a mysterious MethodNotFound
827            // error in the test cases when run under a JDK 1.3.
828            super.fireError(type, propName, propValue, ex);
829        }
830    
831        /**
832         * Creates a configuration object from the specified configuration
833         * declaration.
834         *
835         * @param decl the configuration declaration
836         * @return the new configuration object
837         * @throws ConfigurationException if an error occurs
838         */
839        private AbstractConfiguration createConfigurationAt(
840                ConfigurationDeclaration decl) throws ConfigurationException
841        {
842            try
843            {
844                return (AbstractConfiguration) BeanHelper.createBean(decl);
845            }
846            catch (Exception ex)
847            {
848                // redirect to configuration exceptions
849                throw new ConfigurationException(ex);
850            }
851        }
852    
853        /**
854         * Returns a list with <code>SubnodeConfiguration</code> objects for the
855         * child nodes of the specified configuration node.
856         *
857         * @param node the start node
858         * @return a list with subnode configurations for the node's children
859         */
860        private List fetchChildConfigs(ConfigurationNode node)
861        {
862            List children = node.getChildren();
863            List result = new ArrayList(children.size());
864            for (Iterator it = children.iterator(); it.hasNext();)
865            {
866                result.add(createSubnodeConfiguration((Node) it.next()));
867            }
868            return result;
869        }
870    
871        /**
872         * Returns a list with <code>SubnodeConfiguration</code> objects for the
873         * child nodes of the node specified by the given key.
874         *
875         * @param key the key (must define exactly one node)
876         * @return a list with subnode configurations for the node's children
877         */
878        private List fetchChildConfigs(String key)
879        {
880            List nodes = fetchNodeList(key);
881            if (nodes.size() > 0)
882            {
883                return fetchChildConfigs((ConfigurationNode) nodes.get(0));
884            }
885            else
886            {
887                return Collections.EMPTY_LIST;
888            }
889        }
890    
891        /**
892         * Finds the override configurations that are defined as top level elements
893         * in the configuration definition file. This method will fetch the child
894         * elements of the root node and remove the nodes that represent other
895         * configuration sections. The remaining nodes are treated as definitions
896         * for override configurations.
897         *
898         * @return a list with subnode configurations for the top level override
899         * configurations
900         */
901        private List fetchTopLevelOverrideConfigs()
902        {
903            List configs = fetchChildConfigs(getRootNode());
904            for (Iterator it = configs.iterator(); it.hasNext();)
905            {
906                String nodeName = ((SubnodeConfiguration) it.next()).getRootNode()
907                        .getName();
908                for (int i = 0; i < CONFIG_SECTIONS.length; i++)
909                {
910                    if (CONFIG_SECTIONS[i].equals(nodeName))
911                    {
912                        it.remove();
913                        break;
914                    }
915                }
916            }
917            return configs;
918        }
919    
920        /**
921         * Registers the bean factory used by this class if necessary. This method
922         * is called by the constructor to ensure that the required bean factory is
923         * available.
924         */
925        private void registerBeanFactory()
926        {
927            synchronized (DefaultConfigurationBuilder.class)
928            {
929                if (!BeanHelper.registeredFactoryNames().contains(
930                        CONFIG_BEAN_FACTORY_NAME))
931                {
932                    BeanHelper.registerBeanFactory(CONFIG_BEAN_FACTORY_NAME,
933                            new ConfigurationBeanFactory());
934                }
935            }
936        }
937    
938        /**
939         * <p>
940         * A base class for creating and initializing configuration sources.
941         * </p>
942         * <p>
943         * Concrete sub classes of this base class are responsible for creating
944         * specific <code>Configuration</code> objects for the tags in the
945         * configuration definition file. The configuration factory will parse the
946         * definition file and try to find a matching
947         * <code>ConfigurationProvider</code> for each encountered tag. This
948         * provider is then asked to create a corresponding
949         * <code>Configuration</code> object. It is up to a concrete
950         * implementation how this object is created and initialized.
951         * </p>
952         * <p>
953         * Note that at the moment only configuration classes derived from
954         * <code>{@link AbstractConfiguration}</code> are supported.
955         * </p>
956         */
957        public static class ConfigurationProvider extends DefaultBeanFactory
958        {
959            /** Stores the class of the configuration to be created. */
960            private Class configurationClass;
961    
962            /** Stores the name of the configuration class to be created.*/
963            private String configurationClassName;
964    
965            /**
966             * Creates a new uninitialized instance of
967             * <code>ConfigurationProvider</code>.
968             */
969            public ConfigurationProvider()
970            {
971                this((Class) null);
972            }
973    
974            /**
975             * Creates a new instance of <code>ConfigurationProvider</code> and
976             * sets the class of the configuration created by this provider.
977             *
978             * @param configClass the configuration class
979             */
980            public ConfigurationProvider(Class configClass)
981            {
982                setConfigurationClass(configClass);
983            }
984    
985            /**
986             * Creates a new instance of <code>ConfigurationProvider</code> and
987             * sets the name of the class of the configuration created by this
988             * provider.
989             *
990             * @param configClassName the name of the configuration class
991             * @since 1.4
992             */
993            public ConfigurationProvider(String configClassName)
994            {
995                setConfigurationClassName(configClassName);
996            }
997    
998            /**
999             * Returns the class of the configuration returned by this provider.
1000             *
1001             * @return the class of the provided configuration
1002             */
1003            public Class getConfigurationClass()
1004            {
1005                return configurationClass;
1006            }
1007    
1008            /**
1009             * Sets the class of the configuration returned by this provider.
1010             *
1011             * @param configurationClass the configuration class
1012             */
1013            public void setConfigurationClass(Class configurationClass)
1014            {
1015                this.configurationClass = configurationClass;
1016            }
1017    
1018            /**
1019             * Returns the name of the configuration class returned by this
1020             * provider.
1021             *
1022             * @return the configuration class name
1023             * @since 1.4
1024             */
1025            public String getConfigurationClassName()
1026            {
1027                return configurationClassName;
1028            }
1029    
1030            /**
1031             * Sets the name of the configuration class returned by this provider.
1032             *
1033             * @param configurationClassName the name of the configuration class
1034             * @since 1.4
1035             */
1036            public void setConfigurationClassName(String configurationClassName)
1037            {
1038                this.configurationClassName = configurationClassName;
1039            }
1040    
1041            /**
1042             * Returns the configuration. This method is called to fetch the
1043             * configuration from the provider. This implementation will call the
1044             * inherited <code>{@link
1045             * org.apache.commons.configuration.beanutils.DefaultBeanFactory#createBean(Class, BeanDeclaration, Object)
1046             * createBean()}</code> method to create a new instance of the
1047             * configuration class.
1048             *
1049             * @param decl the bean declaration with initialization parameters for
1050             * the configuration
1051             * @return the new configuration object
1052             * @throws Exception if an error occurs
1053             */
1054            public AbstractConfiguration getConfiguration(
1055                    ConfigurationDeclaration decl) throws Exception
1056            {
1057                return (AbstractConfiguration) createBean(fetchConfigurationClass(),
1058                        decl, null);
1059            }
1060    
1061            /**
1062             * Returns an uninitialized configuration of the represented type. This
1063             * method will be called for optional configurations when the
1064             * <code>getConfiguration()</code> method caused an error and the
1065             * <code>forceCreate</code> attribute is set. A concrete sub class can
1066             * here try to create an uninitialized, empty configuration, which may
1067             * be possible if the error was created during initialization. This base
1068             * implementation just returns <b>null</b>.
1069             *
1070             * @param decl the bean declaration with initialization parameters for
1071             * the configuration
1072             * @return the new configuration object
1073             * @throws Exception if an error occurs
1074             * @since 1.4
1075             */
1076            public AbstractConfiguration getEmptyConfiguration(
1077                    ConfigurationDeclaration decl) throws Exception
1078            {
1079                return null;
1080            }
1081    
1082            /**
1083             * Returns the configuration class supported by this provider. If a
1084             * class object was set, it is returned. Otherwise the method tries to
1085             * resolve the class name.
1086             *
1087             * @return the class of the configuration to be created
1088             * @since 1.4
1089             */
1090            protected synchronized Class fetchConfigurationClass() throws Exception
1091            {
1092                if (getConfigurationClass() == null)
1093                {
1094                    setConfigurationClass(loadClass(getConfigurationClassName()));
1095                }
1096                return getConfigurationClass();
1097            }
1098    
1099            /**
1100             * Loads the class with the specified name dynamically. If the class's
1101             * name is <b>null</b>, <b>null</b> will also be returned.
1102             *
1103             * @param className the name of the class to be loaded
1104             * @return the class object
1105             * @throws ClassNotFoundException if class loading fails
1106             * @since 1.4
1107             */
1108            protected Class loadClass(String className)
1109                    throws ClassNotFoundException
1110            {
1111                return (className != null) ? Class.forName(className, true,
1112                        getClass().getClassLoader()) : null;
1113            }
1114        }
1115    
1116        /**
1117         * <p>
1118         * A specialized <code>BeanDeclaration</code> implementation that
1119         * represents the declaration of a configuration source.
1120         * </p>
1121         * <p>
1122         * Instances of this class are able to extract all information about a
1123         * configuration source from the configuration definition file. The
1124         * declaration of a configuration source is very similar to a bean
1125         * declaration processed by <code>XMLBeanDeclaration</code>. There are
1126         * very few differences, e.g. some reserved attributes like
1127         * <code>optional</code> and <code>at</code> and the fact that a bean
1128         * factory is never needed.
1129         * </p>
1130         */
1131        public static class ConfigurationDeclaration extends XMLBeanDeclaration
1132        {
1133            /** Stores a reference to the associated configuration builder. */
1134            private DefaultConfigurationBuilder configurationBuilder;
1135    
1136            /**
1137             * Creates a new instance of <code>ConfigurationDeclaration</code> and
1138             * initializes it.
1139             *
1140             * @param builder the associated configuration builder
1141             * @param config the configuration this declaration is based onto
1142             */
1143            public ConfigurationDeclaration(DefaultConfigurationBuilder builder,
1144                    HierarchicalConfiguration config)
1145            {
1146                super(config);
1147                configurationBuilder = builder;
1148            }
1149    
1150            /**
1151             * Returns the associated configuration builder.
1152             *
1153             * @return the configuration builder
1154             */
1155            public DefaultConfigurationBuilder getConfigurationBuilder()
1156            {
1157                return configurationBuilder;
1158            }
1159    
1160            /**
1161             * Returns the value of the <code>at</code> attribute.
1162             *
1163             * @return the value of the <code>at</code> attribute (can be <b>null</b>)
1164             */
1165            public String getAt()
1166            {
1167                String result = this.getConfiguration().getString(ATTR_AT_RES);
1168                return (result == null) ? this.getConfiguration().getString(ATTR_AT)
1169                        : result;
1170            }
1171    
1172            /**
1173             * Returns a flag whether this is an optional configuration.
1174             *
1175             * @return a flag if this declaration points to an optional
1176             * configuration
1177             */
1178            public boolean isOptional()
1179            {
1180                Boolean value = this.getConfiguration().getBoolean(ATTR_OPTIONAL_RES,
1181                        null);
1182                if (value == null)
1183                {
1184                    value = this.getConfiguration().getBoolean(ATTR_OPTIONAL,
1185                            Boolean.FALSE);
1186                }
1187                return value.booleanValue();
1188            }
1189    
1190            /**
1191             * Returns a flag whether this configuration should always be created
1192             * and added to the resulting combined configuration. This flag is
1193             * evaluated only for optional configurations whose normal creation has
1194             * caused an error. If for such a configuration the
1195             * <code>forceCreate</code> attribute is set and the corresponding
1196             * configuration provider supports this mode, an empty configuration
1197             * will be created and added to the resulting combined configuration.
1198             *
1199             * @return the value of the <code>forceCreate</code> attribute
1200             * @since 1.4
1201             */
1202            public boolean isForceCreate()
1203            {
1204                return this.getConfiguration().getBoolean(ATTR_FORCECREATE, false);
1205            }
1206    
1207            /**
1208             * Returns the name of the bean factory. For configuration source
1209             * declarations always a reserved factory is used. This factory's name
1210             * is returned by this implementation.
1211             *
1212             * @return the name of the bean factory
1213             */
1214            public String getBeanFactoryName()
1215            {
1216                return CONFIG_BEAN_FACTORY_NAME;
1217            }
1218    
1219            /**
1220             * Returns the bean's class name. This implementation will always return
1221             * <b>null</b>.
1222             *
1223             * @return the name of the bean's class
1224             */
1225            public String getBeanClassName()
1226            {
1227                return null;
1228            }
1229    
1230            /**
1231             * Checks whether the given node is reserved. This method will take
1232             * further reserved attributes into account
1233             *
1234             * @param nd the node
1235             * @return a flag whether this node is reserved
1236             */
1237            protected boolean isReservedNode(ConfigurationNode nd)
1238            {
1239                if (super.isReservedNode(nd))
1240                {
1241                    return true;
1242                }
1243    
1244                return nd.isAttribute()
1245                        && ((ATTR_ATNAME.equals(nd.getName()) && nd.getParentNode()
1246                                .getAttributeCount(RESERVED_PREFIX + ATTR_ATNAME) == 0) || (ATTR_OPTIONALNAME
1247                                .equals(nd.getName()) && nd.getParentNode()
1248                                .getAttributeCount(RESERVED_PREFIX + ATTR_OPTIONALNAME) == 0));
1249            }
1250    
1251            /**
1252             * Performs interpolation. This implementation will delegate
1253             * interpolation to the configuration builder, which takes care that the
1254             * currently constructed configuration is taken into account, too.
1255             *
1256             * @param value the value to be interpolated
1257             * @return the interpolated value
1258             */
1259            protected Object interpolate(Object value)
1260            {
1261                return getConfigurationBuilder().interpolate(value);
1262            }
1263        }
1264    
1265        /**
1266         * A specialized <code>BeanFactory</code> implementation that handles
1267         * configuration declarations. This class will retrieve the correct
1268         * configuration provider and delegate the task of creating the
1269         * configuration to this object.
1270         */
1271        static class ConfigurationBeanFactory implements BeanFactory
1272        {
1273            /** The logger. */
1274            private Log logger = LogFactory.getLog(DefaultConfigurationBuilder.class);
1275    
1276            /**
1277             * Creates an instance of a bean class. This implementation expects that
1278             * the passed in bean declaration is a declaration for a configuration.
1279             * It will determine the responsible configuration provider and delegate
1280             * the call to this instance. If creation of the configuration fails
1281             * and the <code>optional</code> attribute is set, the exception will
1282             * be ignored. If the <code>forceCreate</code> attribute is set, too,
1283             * the provider is asked to create an empty configuration. A return
1284             * value of <b>null</b> means that no configuration could be created.
1285             *
1286             * @param beanClass the bean class (will be ignored)
1287             * @param data the declaration
1288             * @param param an additional parameter (will be ignored)
1289             * @return the newly created configuration
1290             * @throws Exception if an error occurs
1291             */
1292            public Object createBean(Class beanClass, BeanDeclaration data,
1293                    Object param) throws Exception
1294            {
1295                ConfigurationDeclaration decl = (ConfigurationDeclaration) data;
1296                String tagName = decl.getNode().getName();
1297                ConfigurationProvider provider = decl.getConfigurationBuilder()
1298                        .providerForTag(tagName);
1299                if (provider == null)
1300                {
1301                    throw new ConfigurationRuntimeException(
1302                            "No ConfigurationProvider registered for tag "
1303                                    + tagName);
1304                }
1305    
1306                try
1307                {
1308                    return provider.getConfiguration(decl);
1309                }
1310                catch (Exception ex)
1311                {
1312                    // If this is an optional configuration, ignore the exception
1313                    if (!decl.isOptional())
1314                    {
1315                        throw ex;
1316                    }
1317                    else
1318                    {
1319                        if (logger.isDebugEnabled())
1320                        {
1321                            logger.debug("Load failed for optional configuration " + tagName + ": "
1322                                + ex.getMessage());
1323                        }
1324                        // Notify registered error listeners
1325                        decl.getConfigurationBuilder().fireError(
1326                                EVENT_ERR_LOAD_OPTIONAL,
1327                                decl.getConfiguration().getString(ATTR_NAME), null,
1328                                ex);
1329    
1330                        if (decl.isForceCreate())
1331                        {
1332                            try
1333                            {
1334                                return provider.getEmptyConfiguration(decl);
1335                            }
1336                            catch (Exception ex2)
1337                            {
1338                                // Ignore exception, return null in this case
1339                                ;
1340                            }
1341                        }
1342                        return null;
1343                    }
1344                }
1345            }
1346    
1347            /**
1348             * Returns the default class for this bean factory.
1349             *
1350             * @return the default class
1351             */
1352            public Class getDefaultBeanClass()
1353            {
1354                // Here some valid class must be returned, otherwise BeanHelper
1355                // will complain that the bean's class cannot be determined
1356                return Configuration.class;
1357            }
1358        }
1359    
1360        /**
1361         * A specialized provider implementation that deals with file based
1362         * configurations. Ensures that the base path is correctly set and that the
1363         * load() method gets called.
1364         */
1365        public static class FileConfigurationProvider extends ConfigurationProvider
1366        {
1367            /**
1368             * Creates a new instance of <code>FileConfigurationProvider</code>.
1369             */
1370            public FileConfigurationProvider()
1371            {
1372                super();
1373            }
1374    
1375            /**
1376             * Creates a new instance of <code>FileConfigurationProvider</code>
1377             * and sets the configuration class.
1378             *
1379             * @param configClass the class for the configurations to be created
1380             */
1381            public FileConfigurationProvider(Class configClass)
1382            {
1383                super(configClass);
1384            }
1385    
1386            /**
1387             * Creates a new instance of <code>FileConfigurationProvider</code>
1388             * and sets the configuration class name.
1389             *
1390             * @param configClassName the name of the configuration to be created
1391             * @since 1.4
1392             */
1393            public FileConfigurationProvider(String configClassName)
1394            {
1395                super(configClassName);
1396            }
1397    
1398            /**
1399             * Creates the configuration. After that <code>load()</code> will be
1400             * called. If this configuration is marked as optional, exceptions will
1401             * be ignored.
1402             *
1403             * @param decl the declaration
1404             * @return the new configuration
1405             * @throws Exception if an error occurs
1406             */
1407            public AbstractConfiguration getConfiguration(
1408                    ConfigurationDeclaration decl) throws Exception
1409            {
1410                AbstractConfiguration result = getEmptyConfiguration(decl);
1411                if (result instanceof FileSystemBased)
1412                {
1413                    DefaultConfigurationBuilder builder = decl.getConfigurationBuilder();
1414                    if (builder.getFileSystem() != null)
1415                    {
1416                        ((FileSystemBased) result).setFileSystem(builder.getFileSystem());
1417                    }
1418                }
1419                ((FileConfiguration) result).load();
1420                return result;
1421            }
1422    
1423            /**
1424             * Returns an uninitialized file configuration. This method will be
1425             * called for optional configurations when the
1426             * <code>getConfiguration()</code> method caused an error and the
1427             * <code>forceCreate</code> attribute is set. It will create the
1428             * configuration of the represented type, but the <code>load()</code>
1429             * method won't be called. This way non-existing configuration files can
1430             * be handled gracefully: If loading a the file fails, an empty
1431             * configuration will be created that is already configured with the
1432             * correct file name.
1433             *
1434             * @param decl the bean declaration with initialization parameters for
1435             * the configuration
1436             * @return the new configuration object
1437             * @throws Exception if an error occurs
1438             * @since 1.4
1439             */
1440            public AbstractConfiguration getEmptyConfiguration(
1441                    ConfigurationDeclaration decl) throws Exception
1442            {
1443                AbstractConfiguration config = super.getConfiguration(decl);
1444    
1445                /**
1446                 * Some wrapper classes may need to pass the EntityResolver to XMLConfigurations
1447                 * they construct buy may not be an XMLConfiguration.
1448                 */
1449                if (config instanceof EntityResolverSupport)
1450                {
1451                    DefaultConfigurationBuilder builder = decl.getConfigurationBuilder();
1452                    EntityResolver resolver = builder.getEntityResolver();
1453                    ((EntityResolverSupport) config).setEntityResolver(resolver);
1454                }
1455    
1456                return config;
1457            }
1458    
1459            /**
1460             * Initializes the bean instance. Ensures that the file configuration's
1461             * base path will be initialized with the base path of the factory so
1462             * that relative path names can be correctly resolved.
1463             *
1464             * @param bean the bean to be initialized
1465             * @param data the declaration
1466             * @throws Exception if an error occurs
1467             */
1468            protected void initBeanInstance(Object bean, BeanDeclaration data)
1469                    throws Exception
1470            {
1471                FileConfiguration config = (FileConfiguration) bean;
1472                config.setBasePath(((ConfigurationDeclaration) data)
1473                        .getConfigurationBuilder().getConfigurationBasePath());
1474                super.initBeanInstance(bean, data);
1475            }
1476        }
1477    
1478        /**
1479         * A specialized configuration provider for XML configurations. This
1480         * implementation acts like a <code>FileConfigurationProvider</code>, but
1481         * it will copy all entity IDs that have been registered for the
1482         * configuration builder to the new XML configuration before it is loaded.
1483         *
1484         * @since 1.6
1485         */
1486        public static class XMLConfigurationProvider extends FileConfigurationProvider
1487        {
1488            /**
1489             * Creates a new instance of <code>XMLConfigurationProvider</code>.
1490             */
1491            public XMLConfigurationProvider()
1492            {
1493                super(XMLConfiguration.class);
1494            }
1495    
1496            /**
1497             * Returns a new empty configuration instance. This implementation
1498             * performs some additional initialization specific to XML
1499             * configurations.
1500             *
1501             * @param decl the configuration declaration
1502             * @return the new configuration
1503             * @throws Exception if an error occurs
1504             */
1505            public AbstractConfiguration getEmptyConfiguration(
1506                    ConfigurationDeclaration decl) throws Exception
1507            {
1508                XMLConfiguration config = (XMLConfiguration) super
1509                        .getEmptyConfiguration(decl);
1510    
1511                DefaultConfigurationBuilder builder = decl
1512                        .getConfigurationBuilder();
1513                EntityResolver resolver = builder.getEntityResolver();
1514                if (resolver instanceof EntityRegistry)
1515                {
1516                    // copy the registered entities
1517                    config.getRegisteredEntities().putAll(
1518                        builder.getRegisteredEntities());
1519                }
1520                else
1521                {
1522                    config.setEntityResolver(resolver);
1523                }
1524                return config;
1525            }
1526        }
1527    
1528        /**
1529         * A specialized configuration provider for file based configurations that
1530         * can handle configuration sources whose concrete type depends on the
1531         * extension of the file to be loaded. One example is the
1532         * <code>properties</code> tag: if the file ends with ".xml" a
1533         * XMLPropertiesConfiguration object must be created, otherwise a
1534         * PropertiesConfiguration object.
1535         */
1536        static class FileExtensionConfigurationProvider extends
1537                FileConfigurationProvider
1538        {
1539            /**
1540             * Stores the class to be created when the file extension matches.
1541             */
1542            private Class matchingClass;
1543    
1544            /**
1545             * Stores the name of the class to be created when the file extension
1546             * matches.
1547             */
1548            private String matchingClassName;
1549    
1550            /**
1551             * Stores the class to be created when the file extension does not
1552             * match.
1553             */
1554            private Class defaultClass;
1555    
1556            /**
1557             * Stores the name of the class to be created when the file extension
1558             * does not match.
1559             */
1560            private String defaultClassName;
1561    
1562            /** Stores the file extension to be checked against. */
1563            private String fileExtension;
1564    
1565            /**
1566             * Creates a new instance of
1567             * <code>FileExtensionConfigurationProvider</code> and initializes it.
1568             *
1569             * @param matchingClass the class to be created when the file extension
1570             * matches
1571             * @param defaultClass the class to be created when the file extension
1572             * does not match
1573             * @param extension the file extension to be checked agains
1574             */
1575            public FileExtensionConfigurationProvider(Class matchingClass,
1576                    Class defaultClass, String extension)
1577            {
1578                this.matchingClass = matchingClass;
1579                this.defaultClass = defaultClass;
1580                fileExtension = extension;
1581            }
1582    
1583            /**
1584             * Creates a new instance of
1585             * <code>FileExtensionConfigurationProvider</code> and initializes it
1586             * with the names of the classes to be created.
1587             *
1588             * @param matchingClassName the name of the class to be created when the
1589             * file extension matches
1590             * @param defaultClassName the name of the class to be created when the
1591             * file extension does not match
1592             * @param extension the file extension to be checked against
1593             * @since 1.4
1594             */
1595            public FileExtensionConfigurationProvider(String matchingClassName,
1596                    String defaultClassName, String extension)
1597            {
1598                this.matchingClassName = matchingClassName;
1599                this.defaultClassName = defaultClassName;
1600                fileExtension = extension;
1601            }
1602    
1603            /**
1604             * Returns the matching class object, no matter whether it was defined
1605             * as a class or as a class name.
1606             *
1607             * @return the matching class object
1608             * @throws Exception if an error occurs
1609             * @since 1.4
1610             */
1611            protected synchronized Class fetchMatchingClass() throws Exception
1612            {
1613                if (matchingClass == null)
1614                {
1615                    matchingClass = loadClass(matchingClassName);
1616                }
1617                return matchingClass;
1618            }
1619    
1620            /**
1621             * Returns the default class object, no matter whether it was defined as
1622             * a class or as a class name.
1623             *
1624             * @return the default class object
1625             * @throws Exception if an error occurs
1626             * @since 1.4
1627             */
1628            protected synchronized Class fetchDefaultClass() throws Exception
1629            {
1630                if (defaultClass == null)
1631                {
1632                    defaultClass = loadClass(defaultClassName);
1633                }
1634                return defaultClass;
1635            }
1636    
1637            /**
1638             * Creates the configuration object. The class is determined by the file
1639             * name's extension.
1640             *
1641             * @param beanClass the class
1642             * @param data the bean declaration
1643             * @return the new bean
1644             * @throws Exception if an error occurs
1645             */
1646            protected Object createBeanInstance(Class beanClass,
1647                    BeanDeclaration data) throws Exception
1648            {
1649                String fileName = ((ConfigurationDeclaration) data)
1650                        .getConfiguration().getString(ATTR_FILENAME);
1651                if (fileName != null
1652                        && fileName.toLowerCase().trim().endsWith(fileExtension))
1653                {
1654                    return super.createBeanInstance(fetchMatchingClass(), data);
1655                }
1656                else
1657                {
1658                    return super.createBeanInstance(fetchDefaultClass(), data);
1659                }
1660            }
1661        }
1662    
1663        /**
1664         * A specialized configuration provider class that allows to include other
1665         * configuration definition files.
1666         */
1667        static class ConfigurationBuilderProvider extends ConfigurationProvider
1668        {
1669            /**
1670             * Creates a new instance of <code>ConfigurationBuilderProvider</code>.
1671             */
1672            public ConfigurationBuilderProvider()
1673            {
1674                super(DefaultConfigurationBuilder.class);
1675            }
1676    
1677            /**
1678             * Creates the configuration. First creates a configuration builder
1679             * object. Then returns the configuration created by this builder.
1680             *
1681             * @param decl the configuration declaration
1682             * @return the configuration
1683             * @exception Exception if an error occurs
1684             */
1685            public AbstractConfiguration getConfiguration(
1686                    ConfigurationDeclaration decl) throws Exception
1687            {
1688                DefaultConfigurationBuilder builder = (DefaultConfigurationBuilder) super
1689                        .getConfiguration(decl);
1690                return builder.getConfiguration(true);
1691            }
1692    
1693            /**
1694             * Returns an empty configuration in case of an optional configuration
1695             * could not be created. This implementation returns an empty combined
1696             * configuration.
1697             *
1698             * @param decl the configuration declaration
1699             * @return the configuration
1700             * @exception Exception if an error occurs
1701             * @since 1.4
1702             */
1703            public AbstractConfiguration getEmptyConfiguration(
1704                    ConfigurationDeclaration decl) throws Exception
1705            {
1706                return new CombinedConfiguration();
1707            }
1708    
1709            /**
1710             * {@inheritDoc} This implementation ensures that the configuration
1711             * builder created by this provider inherits the properties from the
1712             * current configuration builder.
1713             */
1714            protected void initBeanInstance(Object bean, BeanDeclaration data)
1715                    throws Exception
1716            {
1717                ConfigurationDeclaration decl = (ConfigurationDeclaration) data;
1718                initChildBuilder(decl.getConfigurationBuilder(),
1719                        (DefaultConfigurationBuilder) bean);
1720                super.initBeanInstance(bean, data);
1721            }
1722    
1723            /**
1724             * Initializes the given child configuration builder from its parent
1725             * builder. This method copies the values of some properties from the
1726             * parent builder to the child builder so that the child inherits
1727             * properties from its parent.
1728             *
1729             * @param parent the parent builder
1730             * @param child the child builder
1731             */
1732            private static void initChildBuilder(
1733                    DefaultConfigurationBuilder parent,
1734                    DefaultConfigurationBuilder child)
1735            {
1736                child.setAttributeSplittingDisabled(parent
1737                        .isAttributeSplittingDisabled());
1738                child.setBasePath(parent.getBasePath());
1739                child.setDelimiterParsingDisabled(parent
1740                        .isDelimiterParsingDisabled());
1741                child.setListDelimiter(parent.getListDelimiter());
1742                child.setThrowExceptionOnMissing(parent.isThrowExceptionOnMissing());
1743                child.setLogger(parent.getLogger());
1744    
1745                child.clearConfigurationListeners();
1746                for (Iterator it = parent.getConfigurationListeners().iterator(); it
1747                        .hasNext();)
1748                {
1749                    child.addConfigurationListener((ConfigurationListener) it
1750                            .next());
1751                }
1752                child.clearErrorListeners();
1753                for (Iterator it = parent.getErrorListeners().iterator(); it
1754                        .hasNext();)
1755                {
1756                    child.addErrorListener((ConfigurationErrorListener) it.next());
1757                }
1758            }
1759        }
1760    }