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}
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.java 1366930 2012-07-29 20:05:36Z 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}
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    }