Coverage Report - org.apache.commons.configuration.DefaultConfigurationBuilder
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultConfigurationBuilder
100%
92/92
100%
17/17
1,691
DefaultConfigurationBuilder$ConfigurationBeanFactory
100%
15/15
100%
4/4
1,691
DefaultConfigurationBuilder$ConfigurationBuilderProvider
100%
6/6
100%
1/1
1,691
DefaultConfigurationBuilder$ConfigurationDeclaration
100%
17/17
100%
4/4
1,691
DefaultConfigurationBuilder$ConfigurationProvider
70%
14/20
100%
2/2
1,691
DefaultConfigurationBuilder$FileConfigurationProvider
86%
12/14
N/A
1,691
DefaultConfigurationBuilder$FileExtensionConfigurationProvider
90%
18/20
100%
3/3
1,691
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.apache.commons.configuration;
 18  
 
 19  
 import java.io.File;
 20  
 import java.net.URL;
 21  
 import java.util.ArrayList;
 22  
 import java.util.Collections;
 23  
 import java.util.HashMap;
 24  
 import java.util.Iterator;
 25  
 import java.util.List;
 26  
 import java.util.Map;
 27  
 
 28  
 import org.apache.commons.configuration.beanutils.BeanDeclaration;
 29  
 import org.apache.commons.configuration.beanutils.BeanFactory;
 30  
 import org.apache.commons.configuration.beanutils.BeanHelper;
 31  
 import org.apache.commons.configuration.beanutils.DefaultBeanFactory;
 32  
 import org.apache.commons.configuration.beanutils.XMLBeanDeclaration;
 33  
 import org.apache.commons.configuration.tree.ConfigurationNode;
 34  
 import org.apache.commons.configuration.tree.DefaultExpressionEngine;
 35  
 import org.apache.commons.configuration.tree.OverrideCombiner;
 36  
 import org.apache.commons.configuration.tree.UnionCombiner;
 37  
 
 38  
 /**
 39  
  * <p>
 40  
  * A factory class that creates a composite configuration from an XML based
 41  
  * <em>configuration definition file</em>.
 42  
  * </p>
 43  
  * <p>
 44  
  * This class provides an easy and flexible means for loading multiple
 45  
  * configuration sources and combining the results into a single configuration
 46  
  * object. The sources to be loaded are defined in an XML document that can
 47  
  * contain certain tags representing the different supported configuration
 48  
  * classes. If such a tag is found, the corresponding <code>Configuration</code>
 49  
  * class is instantiated and initialized using the classes of the
 50  
  * <code>beanutils</code> package (namely
 51  
  * <code>{@link org.apache.commons.configuration.beanutils.XMLBeanDeclaration XMLBeanDeclaration}</code>
 52  
  * will be used to extract the configuration's initialization parameters, which
 53  
  * allows for complex initialization szenarios).
 54  
  * </p>
 55  
  * <p>
 56  
  * It is also possible to add custom tags to the configuration definition file.
 57  
  * For this purpose register your own <code>ConfigurationProvider</code>
 58  
  * implementation for your tag using the <code>addConfigurationProvider()</code>
 59  
  * method. This provider will then be called when the corresponding custom tag
 60  
  * is detected. For the default configuration classes providers are already
 61  
  * registered.
 62  
  * </p>
 63  
  * <p>
 64  
  * The configuration definition file has the following basic structure:
 65  
  * </p>
 66  
  * <p>
 67  
  *
 68  
  * <pre>
 69  
  * &lt;configuration&gt;
 70  
  *   &lt;header&gt;
 71  
  *     &lt;!-- Optional meta information about the composite configuration --&gt;
 72  
  *   &lt;/header&gt;
 73  
  *   &lt;override&gt;
 74  
  *     &lt;!-- Declarations for override configurations --&gt;
 75  
  *   &lt;/override&gt;
 76  
  *   &lt;additional&gt;
 77  
  *     &lt;!-- Declarations for union configurations --&gt;
 78  
  *   &lt;/additional&gt;
 79  
  * &lt;/configuration&gt;
 80  
  * </pre>
 81  
  *
 82  
  * </p>
 83  
  * <p>
 84  
  * The name of the root element (here <code>configuration</code>) is
 85  
  * arbitrary. There are two sections (both of them are optional) for declaring
 86  
  * <em>override</em> and <em>additional</em> configurations. Configurations
 87  
  * in the former section are evaluated in the order of their declaration, and
 88  
  * properties of configurations declared earlier hide those of configurations
 89  
  * declared later. Configurations in the latter section are combined to a union
 90  
  * configuration, i.e. all of their properties are added to a large hierarchical
 91  
  * configuration. Configuration declarations that occur as direct children of
 92  
  * the root element are treated as override declarations.
 93  
  * </p>
 94  
  * <p>
 95  
  * Each configuration declaration consists of a tag whose name is associated
 96  
  * with a <code>ConfigurationProvider</code>. This can be one of the
 97  
  * pre-defined tags like <code>properties</code>, or <code>xml</code>, or
 98  
  * a custom tag, for which a configuration provider was registered. Attributes
 99  
  * and sub elements with specific initialization parameters can be added. There
 100  
  * are some reserved attributes with a special meaning that can be used in every
 101  
  * configuration declaration:
 102  
  * </p>
 103  
  * <p>
 104  
  * <table border="1">
 105  
  * <tr>
 106  
  * <th>Attribute</th>
 107  
  * <th>Meaning</th>
 108  
  * </tr>
 109  
  * <tr>
 110  
  * <td valign="top"><code>config-name</code></td>
 111  
  * <td>Allows to specify a name for this configuration. This name can be used
 112  
  * to obtain a reference to the configuration from the resulting combined
 113  
  * configuration (see below).</td>
 114  
  * </tr>
 115  
  * <tr>
 116  
  * <td valign="top"><code>config-at</code></td>
 117  
  * <td>With this attribute an optional prefix can be specified for the
 118  
  * properties of the corresponding configuration.</td>
 119  
  * </tr>
 120  
  * <tr>
 121  
  * <td valign="top"><code>config-optional</code></td>
 122  
  * <td>Declares a configuration as optional. This means that errors that occur
 123  
  * when creating the configuration are silently ignored.</td>
 124  
  * </tr>
 125  
  * </table>
 126  
  * </p>
 127  
  * <p>
 128  
  * The optional <em>header</em> section can contain some meta data about the
 129  
  * created configuration itself. For instance, it is possible to set further
 130  
  * properties of the <code>NodeCombiner</code> objects used for constructing
 131  
  * the resulting configuration.
 132  
  * </p>
 133  
  * <p>
 134  
  * The configuration object returned by this builder is an instance of the
 135  
  * <code>{@link CombinedConfiguration}</code> class. The return value of the
 136  
  * <code>getConfiguration()</code> method can be casted to this type, and the
 137  
  * <code>getConfiguration(boolean)</code> method directly declares
 138  
  * <code>CombinedConfiguration</code> as return type. This allows for
 139  
  * convenient access to the configuration objects maintained by the combined
 140  
  * configuration (e.g. for updates of single configuration objects). It has also
 141  
  * the advantage that the properties stored in all declared configuration
 142  
  * objects are collected and transformed into a single hierarchical structure,
 143  
  * which can be accessed using different expression engines.
 144  
  * </p>
 145  
  * <p>
 146  
  * All declared override configurations are directly added to the resulting
 147  
  * combined configuration. If they are given names (using the
 148  
  * <code>config-name</code> attribute), they can directly be accessed using
 149  
  * the <code>getConfiguration(String)</code> method of
 150  
  * <code>CombinedConfiguration</code>. The additional configurations are
 151  
  * alltogether added to another combined configuration, which uses a union
 152  
  * combiner. Then this union configuration is added to the resulting combined
 153  
  * configuration under the name defined by the <code>ADDITIONAL_NAME</code>
 154  
  * constant.
 155  
  * </p>
 156  
  * <p>
 157  
  * Implementation note: This class is not thread-safe. Especially the
 158  
  * <code>getConfiguration()</code> methods should be called by a single thread
 159  
  * only.
 160  
  * </p>
 161  
  *
 162  
  * @since 1.3
 163  
  * @author <a
 164  
  * href="http://jakarta.apache.org/commons/configuration/team-list.html">Commons
 165  
  * Configuration team</a>
 166  
  * @version $Id: DefaultConfigurationBuilder.java 507219 2007-02-13 21:02:09Z oheger $
 167  
  */
 168  14
 public class DefaultConfigurationBuilder extends XMLConfiguration implements
 169  
         ConfigurationBuilder
 170  
 {
 171  
     /**
 172  
      * Constant for the name of the additional configuration. If the
 173  
      * configuration definition file contains an <code>additional</code>
 174  
      * section, a special union configuration is created and added under this
 175  
      * name to the resulting combined configuration.
 176  
      */
 177  4
     public static final String ADDITIONAL_NAME = DefaultConfigurationBuilder.class
 178  
             .getName()
 179  
             + "/ADDITIONAL_CONFIG";
 180  
 
 181  
     /** Constant for the name of the configuration bean factory. */
 182  2
     static final String CONFIG_BEAN_FACTORY_NAME = DefaultConfigurationBuilder.class
 183  
             .getName()
 184  
             + ".CONFIG_BEAN_FACTORY_NAME";
 185  
 
 186  
     /** Constant for the reserved name attribute. */
 187  
     static final String ATTR_NAME = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
 188  
             + XMLBeanDeclaration.RESERVED_PREFIX
 189  
             + "name"
 190  
             + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
 191  
 
 192  
     /** Constant for the name of the at attribute. */
 193  
     static final String ATTR_ATNAME = "at";
 194  
 
 195  
     /** Constant for the reserved at attribute. */
 196  
     static final String ATTR_AT_RES = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
 197  
             + XMLBeanDeclaration.RESERVED_PREFIX
 198  
             + ATTR_ATNAME
 199  
             + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
 200  
 
 201  
     /** Constant for the at attribute without the reserved prefix. */
 202  
     static final String ATTR_AT = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
 203  
             + ATTR_ATNAME + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
 204  
 
 205  
     /** Constant for the name of the optional attribute. */
 206  
     static final String ATTR_OPTIONALNAME = "optional";
 207  
 
 208  
     /** Constant for the reserved optional attribute. */
 209  
     static final String ATTR_OPTIONAL_RES = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
 210  
             + XMLBeanDeclaration.RESERVED_PREFIX
 211  
             + ATTR_OPTIONALNAME
 212  
             + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
 213  
 
 214  
     /** Constant for the optional attribute without the reserved prefix. */
 215  
     static final String ATTR_OPTIONAL = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
 216  
             + ATTR_OPTIONALNAME + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
 217  
 
 218  
     /** Constant for the file name attribute. */
 219  
     static final String ATTR_FILENAME = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
 220  
             + "fileName" + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
 221  
 
 222  
     /** Constant for the forceCreate attribute. */
 223  
     static final String ATTR_FORCECREATE = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
 224  
             + XMLBeanDeclaration.RESERVED_PREFIX
 225  
             + "forceCreate"
 226  
             + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
 227  
 
 228  
     /** Constant for the name of the header section. */
 229  
     static final String SEC_HEADER = "header";
 230  
 
 231  
     /** Constant for an expression that selects the union configurations. */
 232  
     static final String KEY_UNION = "additional";
 233  
 
 234  
     /** An array with the names of top level configuration sections.*/
 235  2
     static final String[] CONFIG_SECTIONS = {
 236  
         "additional", "override", SEC_HEADER
 237  
     };
 238  
 
 239  
     /**
 240  
      * Constant for an expression that selects override configurations in the
 241  
      * override section.
 242  
      */
 243  
     static final String KEY_OVERRIDE = "override";
 244  
 
 245  
     /**
 246  
      * Constant for the key that points to the list nodes definition of the
 247  
      * override combiner.
 248  
      */
 249  
     static final String KEY_OVERRIDE_LIST = SEC_HEADER
 250  
             + ".combiner.override.list-nodes.node";
 251  
 
 252  
     /**
 253  
      * Constant for the key that points to the list nodes definition of the
 254  
      * additional combiner.
 255  
      */
 256  
     static final String KEY_ADDITIONAL_LIST = SEC_HEADER
 257  
             + ".combiner.additional.list-nodes.node";
 258  
 
 259  
     /**
 260  
      * Constant for the key of the result declaration. This key can point to a
 261  
      * bean declaration, which defines properties of the resulting combined
 262  
      * configuration.
 263  
      */
 264  
     static final String KEY_RESULT = SEC_HEADER + ".result";
 265  
 
 266  
     /** Constant for the key of the combiner in the result declaration.*/
 267  
     static final String KEY_COMBINER = KEY_RESULT + ".nodeCombiner";
 268  
 
 269  
     /** Constant for the XML file extension. */
 270  
     static final String EXT_XML = ".xml";
 271  
 
 272  
     /** Constant for the provider for properties files. */
 273  2
     private static final ConfigurationProvider PROPERTIES_PROVIDER = new FileExtensionConfigurationProvider(
 274  
             XMLPropertiesConfiguration.class, PropertiesConfiguration.class,
 275  
             EXT_XML);
 276  
 
 277  
     /** Constant for the provider for XML files. */
 278  2
     private static final ConfigurationProvider XML_PROVIDER = new FileConfigurationProvider(
 279  
             XMLConfiguration.class);
 280  
 
 281  
     /** Constant for the provider for JNDI sources. */
 282  2
     private static final ConfigurationProvider JNDI_PROVIDER = new ConfigurationProvider(
 283  
             JNDIConfiguration.class);
 284  
 
 285  
     /** Constant for the provider for system properties. */
 286  2
     private static final ConfigurationProvider SYSTEM_PROVIDER = new ConfigurationProvider(
 287  
             SystemConfiguration.class);
 288  
 
 289  
     /** Constant for the provider for plist files. */
 290  2
     private static final ConfigurationProvider PLIST_PROVIDER = new FileExtensionConfigurationProvider(
 291  
             "org.apache.commons.configuration.plist.XMLPropertyListConfiguration",
 292  
             "org.apache.commons.configuration.plist.PropertyListConfiguration",
 293  
             EXT_XML);
 294  
 
 295  
     /** Constant for the provider for configuration definition files.*/
 296  2
     private static final ConfigurationProvider BUILDER_PROVIDER = new ConfigurationBuilderProvider();
 297  
 
 298  
     /** An array with the names of the default tags. */
 299  2
     private static final String[] DEFAULT_TAGS =
 300  
     {"properties", "xml", "hierarchicalXml", "jndi", "system", "plist", "configuration"};
 301  
 
 302  
     /** An array with the providers for the default tags. */
 303  2
     private static final ConfigurationProvider[] DEFAULT_PROVIDERS =
 304  
     {PROPERTIES_PROVIDER, XML_PROVIDER, XML_PROVIDER, JNDI_PROVIDER,
 305  
             SYSTEM_PROVIDER, PLIST_PROVIDER, BUILDER_PROVIDER};
 306  
 
 307  
     /**
 308  
      * The serial version UID.
 309  
      */
 310  
     private static final long serialVersionUID = -3113777854714492123L;
 311  
 
 312  
     /** Stores the configuration that is currently constructed.*/
 313  
     private CombinedConfiguration constructedConfiguration;
 314  
 
 315  
     /** Stores a map with the registered configuration providers. */
 316  
     private Map providers;
 317  
 
 318  
     /** Stores the base path to the configuration sources to load. */
 319  
     private String configurationBasePath;
 320  
 
 321  
     /**
 322  
      * Creates a new instance of <code>DefaultConfigurationBuilder</code>. A
 323  
      * configuration definition file is not yet loaded. Use the diverse setter
 324  
      * methods provided by file based configurations to specify the
 325  
      * configuration definition file.
 326  
      */
 327  
     public DefaultConfigurationBuilder()
 328  
     {
 329  36
         super();
 330  36
         providers = new HashMap();
 331  36
         registerDefaultProviders();
 332  36
     }
 333  
 
 334  
     /**
 335  
      * Creates a new instance of <code>DefaultConfigurationBuilder</code> and
 336  
      * sets the specified configuration definition file.
 337  
      *
 338  
      * @param file the configuration definition file
 339  
      */
 340  
     public DefaultConfigurationBuilder(File file)
 341  
     {
 342  1
         this();
 343  1
         setFile(file);
 344  1
     }
 345  
 
 346  
     /**
 347  
      * Creates a new instance of <code>DefaultConfigurationBuilder</code> and
 348  
      * sets the specified configuration definition file.
 349  
      *
 350  
      * @param fileName the name of the configuration definition file
 351  
      * @throws ConfigurationException if an error occurs when the file is loaded
 352  
      */
 353  
     public DefaultConfigurationBuilder(String fileName)
 354  
             throws ConfigurationException
 355  
     {
 356  2
         this();
 357  2
         setFileName(fileName);
 358  2
     }
 359  
 
 360  
     /**
 361  
      * Creates a new instance of <code>DefaultConfigurationBuilder</code> and
 362  
      * sets the specified configuration definition file.
 363  
      *
 364  
      * @param url the URL to the configuration definition file
 365  
      * @throws ConfigurationException if an error occurs when the file is loaded
 366  
      */
 367  
     public DefaultConfigurationBuilder(URL url) throws ConfigurationException
 368  
     {
 369  1
         this();
 370  1
         setURL(url);
 371  1
     }
 372  
 
 373  
     /**
 374  
      * Returns the base path for the configuration sources to load. This path is
 375  
      * used to resolve relative paths in the configuration definition file.
 376  
      *
 377  
      * @return the base path for configuration sources
 378  
      */
 379  
     public String getConfigurationBasePath()
 380  
     {
 381  52
         return (configurationBasePath != null) ? configurationBasePath
 382  
                 : getBasePath();
 383  
     }
 384  
 
 385  
     /**
 386  
      * Sets the base path for the configuration sources to load. Normally a base
 387  
      * path need not to be set because it is determined by the location of the
 388  
      * configuration definition file to load. All relative pathes in this file
 389  
      * are resolved relative to this file. Setting a base path makes sense if
 390  
      * such relative pathes should be otherwise resolved, e.g. if the
 391  
      * configuration file is loaded from the class path and all sub
 392  
      * configurations it refers to are stored in a special config directory.
 393  
      *
 394  
      * @param configurationBasePath the new base path to set
 395  
      */
 396  
     public void setConfigurationBasePath(String configurationBasePath)
 397  
     {
 398  1
         this.configurationBasePath = configurationBasePath;
 399  1
     }
 400  
 
 401  
     /**
 402  
      * Adds a configuration provider for the specified tag. Whenever this tag is
 403  
      * encountered in the configuration definition file this provider will be
 404  
      * called to create the configuration object.
 405  
      *
 406  
      * @param tagName the name of the tag in the configuration definition file
 407  
      * @param provider the provider for this tag
 408  
      */
 409  
     public void addConfigurationProvider(String tagName,
 410  
             ConfigurationProvider provider)
 411  
     {
 412  258
         if (tagName == null)
 413  
         {
 414  1
             throw new IllegalArgumentException("Tag name must not be null!");
 415  
         }
 416  257
         if (provider == null)
 417  
         {
 418  1
             throw new IllegalArgumentException("Provider must not be null!");
 419  
         }
 420  
 
 421  256
         providers.put(tagName, provider);
 422  256
     }
 423  
 
 424  
     /**
 425  
      * Removes the configuration provider for the specified tag name.
 426  
      *
 427  
      * @param tagName the tag name
 428  
      * @return the removed configuration provider or <b>null</b> if none was
 429  
      * registered for that tag
 430  
      */
 431  
     public ConfigurationProvider removeConfigurationProvider(String tagName)
 432  
     {
 433  3
         return (ConfigurationProvider) providers.remove(tagName);
 434  
     }
 435  
 
 436  
     /**
 437  
      * Returns the configuration provider for the given tag.
 438  
      *
 439  
      * @param tagName the name of the tag
 440  
      * @return the provider that was registered for this tag or <b>null</b> if
 441  
      * there is none
 442  
      */
 443  
     public ConfigurationProvider providerForTag(String tagName)
 444  
     {
 445  65
         return (ConfigurationProvider) providers.get(tagName);
 446  
     }
 447  
 
 448  
     /**
 449  
      * Returns the configuration provided by this builder. Loads and parses the
 450  
      * configuration definition file and creates instances for the declared
 451  
      * configurations.
 452  
      *
 453  
      * @return the configuration
 454  
      * @throws ConfigurationException if an error occurs
 455  
      */
 456  
     public Configuration getConfiguration() throws ConfigurationException
 457  
     {
 458  10
         return getConfiguration(true);
 459  
     }
 460  
 
 461  
     /**
 462  
      * Returns the configuration provided by this builder. If the boolean
 463  
      * parameter is <b>true</b>, the configuration definition file will be
 464  
      * loaded. It will then be parsed, and instances for the declared
 465  
      * configurations will be created.
 466  
      *
 467  
      * @param load a flag whether the configuration definition file should be
 468  
      * loaded; a value of <b>false</b> would make sense if the file has already
 469  
      * been created or its content was manipulated using some of the property
 470  
      * accessor methods
 471  
      * @return the configuration
 472  
      * @throws ConfigurationException if an error occurs
 473  
      */
 474  
     public CombinedConfiguration getConfiguration(boolean load)
 475  
             throws ConfigurationException
 476  
     {
 477  23
         if (load)
 478  
         {
 479  17
             load();
 480  
         }
 481  
 
 482  20
         CombinedConfiguration result = createResultConfiguration();
 483  20
         constructedConfiguration = result;
 484  
 
 485  20
         List overrides = fetchTopLevelOverrideConfigs();
 486  20
         overrides.addAll(fetchChildConfigs(KEY_OVERRIDE));
 487  20
         initCombinedConfiguration(result, overrides, KEY_OVERRIDE_LIST);
 488  
 
 489  19
         List additionals = fetchChildConfigs(KEY_UNION);
 490  19
         if (!additionals.isEmpty())
 491  
         {
 492  6
             CombinedConfiguration addConfig = new CombinedConfiguration(
 493  
                     new UnionCombiner());
 494  6
             result.addConfiguration(addConfig, ADDITIONAL_NAME);
 495  6
             initCombinedConfiguration(addConfig, additionals,
 496  
                     KEY_ADDITIONAL_LIST);
 497  
         }
 498  
 
 499  19
         return result;
 500  
     }
 501  
 
 502  
     /**
 503  
      * Creates the resulting combined configuration. This method is called by
 504  
      * <code>getConfiguration()</code>. It checks whether the
 505  
      * <code>header</code> section of the configuration definition file
 506  
      * contains a <code>result</code> element. If this is the case, it will be
 507  
      * used to initialize the properties of the newly created configuration
 508  
      * object.
 509  
      *
 510  
      * @return the resulting configuration object
 511  
      * @throws ConfigurationException if an error occurs
 512  
      */
 513  
     protected CombinedConfiguration createResultConfiguration()
 514  
             throws ConfigurationException
 515  
     {
 516  20
         XMLBeanDeclaration decl = new XMLBeanDeclaration(this, KEY_RESULT, true);
 517  20
         CombinedConfiguration result = (CombinedConfiguration) BeanHelper
 518  
                 .createBean(decl, CombinedConfiguration.class);
 519  
 
 520  20
         if (getMaxIndex(KEY_COMBINER) < 0)
 521  
         {
 522  
             // No combiner defined => set default
 523  17
             result.setNodeCombiner(new OverrideCombiner());
 524  
         }
 525  
 
 526  20
         return result;
 527  
     }
 528  
 
 529  
     /**
 530  
      * Initializes a combined configuration for the configurations of a specific
 531  
      * section. This method is called for the override and for the additional
 532  
      * section (if it exists).
 533  
      *
 534  
      * @param config the configuration to be initialized
 535  
      * @param containedConfigs the list with the declaratinos of the contained
 536  
      * configurations
 537  
      * @param keyListNodes a list with the declaration of list nodes
 538  
      * @throws ConfigurationException if an error occurs
 539  
      */
 540  
     protected void initCombinedConfiguration(CombinedConfiguration config,
 541  
             List containedConfigs, String keyListNodes) throws ConfigurationException
 542  
     {
 543  26
         List listNodes = getList(keyListNodes);
 544  58
         for (Iterator it = listNodes.iterator(); it.hasNext();)
 545  
         {
 546  6
             config.getNodeCombiner().addListNode((String) it.next());
 547  
         }
 548  
 
 549  111
         for (Iterator it = containedConfigs.iterator(); it.hasNext();)
 550  
         {
 551  60
             HierarchicalConfiguration conf = (HierarchicalConfiguration) it
 552  
                     .next();
 553  60
             ConfigurationDeclaration decl = new ConfigurationDeclaration(this,
 554  
                     conf);
 555  60
             AbstractConfiguration newConf = createConfigurationAt(decl);
 556  59
             if (newConf != null)
 557  
             {
 558  50
                 config.addConfiguration(newConf, decl.getConfiguration()
 559  
                         .getString(ATTR_NAME), decl.getAt());
 560  
             }
 561  
         }
 562  25
     }
 563  
 
 564  
     /**
 565  
      * Registers the default configuration providers supported by this class.
 566  
      * This method will be called during initialization. It registers
 567  
      * configuration providers for the tags that are supported by default.
 568  
      */
 569  
     protected void registerDefaultProviders()
 570  
     {
 571  288
         for (int i = 0; i < DEFAULT_TAGS.length; i++)
 572  
         {
 573  252
             addConfigurationProvider(DEFAULT_TAGS[i], DEFAULT_PROVIDERS[i]);
 574  
         }
 575  36
     }
 576  
 
 577  
     /**
 578  
      * Performs interpolation. This method will not only take this configuration
 579  
      * instance into account (which is the one that loaded the configuration
 580  
      * definition file), but also the so far constructed combined configuration.
 581  
      * So variables can be used that point to properties that are defined in
 582  
      * configuration sources loaded by this builder.
 583  
      *
 584  
      * @param value the value to be interpolated
 585  
      * @return the interpolated value
 586  
      */
 587  
     protected Object interpolate(Object value)
 588  
     {
 589  331
         Object result = super.interpolate(value);
 590  331
         if (constructedConfiguration != null)
 591  
         {
 592  268
             result = constructedConfiguration.interpolate(result);
 593  
         }
 594  331
         return result;
 595  
     }
 596  
 
 597  
     /**
 598  
      * Creates a configuration object from the specified configuration
 599  
      * declaration.
 600  
      *
 601  
      * @param decl the configuration declaration
 602  
      * @return the new configuration object
 603  
      * @throws ConfigurationException if an error occurs
 604  
      */
 605  
     private AbstractConfiguration createConfigurationAt(
 606  
             ConfigurationDeclaration decl) throws ConfigurationException
 607  
     {
 608  
         try
 609  
         {
 610  60
             return (AbstractConfiguration) BeanHelper.createBean(decl);
 611  
         }
 612  
         catch (Exception ex)
 613  
         {
 614  
             // redirect to configuration exceptions
 615  1
             throw new ConfigurationException(ex);
 616  
         }
 617  
     }
 618  
 
 619  
     /**
 620  
      * Returns a list with <code>SubnodeConfiguration</code> objects for the
 621  
      * child nodes of the specified configuration node.
 622  
      *
 623  
      * @param node the start node
 624  
      * @return a list with subnode configurations for the node's children
 625  
      */
 626  
     private List fetchChildConfigs(ConfigurationNode node)
 627  
     {
 628  32
         List children = node.getChildren();
 629  32
         List result = new ArrayList(children.size());
 630  139
         for (Iterator it = children.iterator(); it.hasNext();)
 631  
         {
 632  75
             result.add(createSubnodeConfiguration((Node) it.next()));
 633  
         }
 634  32
         return result;
 635  
     }
 636  
 
 637  
     /**
 638  
      * Returns a list with <code>SubnodeConfiguration</code> objects for the
 639  
      * child nodes of the node specified by the given key.
 640  
      *
 641  
      * @param key the key (must define exactly one node)
 642  
      * @return a list with subnode configurations for the node's children
 643  
      */
 644  
     private List fetchChildConfigs(String key)
 645  
     {
 646  39
         List nodes = fetchNodeList(key);
 647  39
         if (nodes.size() > 0)
 648  
         {
 649  12
             return fetchChildConfigs((ConfigurationNode) nodes.get(0));
 650  
         }
 651  
         else
 652  
         {
 653  27
             return Collections.EMPTY_LIST;
 654  
         }
 655  
     }
 656  
 
 657  
     /**
 658  
      * Finds the override configurations that are defined as top level elements
 659  
      * in the configuration definition file. This method will fetch the child
 660  
      * elements of the root node and remove the nodes that represent other
 661  
      * configuration sections. The remaining nodes are treated as definitions
 662  
      * for override configurations.
 663  
      *
 664  
      * @return a list with subnode configurations for the top level override
 665  
      * configurations
 666  
      */
 667  
     private List fetchTopLevelOverrideConfigs()
 668  
     {
 669  20
         List configs = fetchChildConfigs(getRootNode());
 670  92
         for (Iterator it = configs.iterator(); it.hasNext();)
 671  
         {
 672  52
             String nodeName = ((SubnodeConfiguration) it.next()).getRootNode()
 673  
                     .getName();
 674  175
             for (int i = 0; i < CONFIG_SECTIONS.length; i++)
 675  
             {
 676  138
                 if (CONFIG_SECTIONS[i].equals(nodeName))
 677  
                 {
 678  15
                     it.remove();
 679  15
                     break;
 680  
                 }
 681  
             }
 682  
         }
 683  20
         return configs;
 684  
     }
 685  
 
 686  
     /**
 687  
      * <p>
 688  
      * A base class for creating and initializing configuration sources.
 689  
      * </p>
 690  
      * <p>
 691  
      * Concrete sub classes of this base class are responsible for creating
 692  
      * specific <code>Configuration</code> objects for the tags in the
 693  
      * configuration definition file. The configuration factory will parse the
 694  
      * definition file and try to find a matching
 695  
      * <code>ConfigurationProvider</code> for each encountered tag. This
 696  
      * provider is then asked to create a corresponding
 697  
      * <code>Configuration</code> object. It is up to a concrete
 698  
      * implementation how this object is created and initialized.
 699  
      * </p>
 700  
      * <p>
 701  
      * Note that at the moment only configuration classes derived from
 702  
      * <code>{@link AbstractConfiguration}</code> are supported.
 703  
      * </p>
 704  
      */
 705  
     public static class ConfigurationProvider extends DefaultBeanFactory
 706  
     {
 707  
         /** Stores the class of the configuration to be created. */
 708  
         private Class configurationClass;
 709  
 
 710  
         /** Stores the name of the configuration class to be created.*/
 711  
         private String configurationClassName;
 712  
 
 713  
         /**
 714  
          * Creates a new uninitialized instance of
 715  
          * <code>ConfigurationProvider</code>.
 716  
          */
 717  
         public ConfigurationProvider()
 718  
         {
 719  7
             this((Class) null);
 720  7
         }
 721  
 
 722  
         /**
 723  
          * Creates a new instance of <code>ConfigurationProvider</code> and
 724  
          * sets the class of the configuration created by this provider.
 725  
          *
 726  
          * @param configClass the configuration class
 727  
          */
 728  
         public ConfigurationProvider(Class configClass)
 729  17
         {
 730  17
             setConfigurationClass(configClass);
 731  17
         }
 732  
 
 733  
         /**
 734  
          * Creates a new instance of <code>ConfigurationProvider</code> and
 735  
          * sets the name of the class of the configuration created by this
 736  
          * provider.
 737  
          *
 738  
          * @param configClassName the name of the configuration class
 739  
          * @since 1.4
 740  
          */
 741  
         public ConfigurationProvider(String configClassName)
 742  0
         {
 743  0
             setConfigurationClassName(configClassName);
 744  0
         }
 745  
 
 746  
         /**
 747  
          * Returns the class of the configuration returned by this provider.
 748  
          *
 749  
          * @return the class of the provided configuration
 750  
          */
 751  
         public Class getConfigurationClass()
 752  
         {
 753  124
             return configurationClass;
 754  
         }
 755  
 
 756  
         /**
 757  
          * Sets the class of the configuration returned by this provider.
 758  
          *
 759  
          * @param configurationClass the configuration class
 760  
          */
 761  
         public void setConfigurationClass(Class configurationClass)
 762  
         {
 763  43
             this.configurationClass = configurationClass;
 764  43
         }
 765  
 
 766  
         /**
 767  
          * Returns the name of the configuration class returned by this
 768  
          * provider.
 769  
          *
 770  
          * @return the configuration class name
 771  
          * @since 1.4
 772  
          */
 773  
         public String getConfigurationClassName()
 774  
         {
 775  26
             return configurationClassName;
 776  
         }
 777  
 
 778  
         /**
 779  
          * Sets the name of the configuration class returned by this provider.
 780  
          *
 781  
          * @param configurationClassName the name of the configuration class
 782  
          * @since 1.4
 783  
          */
 784  
         public void setConfigurationClassName(String configurationClassName)
 785  
         {
 786  0
             this.configurationClassName = configurationClassName;
 787  0
         }
 788  
 
 789  
         /**
 790  
          * Returns the configuration. This method is called to fetch the
 791  
          * configuration from the provider. This implementation will call the
 792  
          * inherited <code>{@link
 793  
          * org.apache.commons.configuration.beanutils.DefaultBeanFactory#createBean(Class, BeanDeclaration, Object)
 794  
          * createBean()}</code> method to create a new instance of the
 795  
          * configuration class.
 796  
          *
 797  
          * @param decl the bean declaration with initialization parameters for
 798  
          * the configuration
 799  
          * @return the new configuration object
 800  
          * @throws Exception if an error occurs
 801  
          */
 802  
         public AbstractConfiguration getConfiguration(
 803  
                 ConfigurationDeclaration decl) throws Exception
 804  
         {
 805  62
             return (AbstractConfiguration) createBean(fetchConfigurationClass(),
 806  
                     decl, null);
 807  
         }
 808  
 
 809  
         /**
 810  
          * Returns an uninitialized configuration of the represented type. This
 811  
          * method will be called for optional configurations when the
 812  
          * <code>getConfiguration()</code> method caused an error and the
 813  
          * <code>forceCreate</code> attribute is set. A concrete sub class can
 814  
          * here try to create an uninitialized, empty configuration, which may
 815  
          * be possible if the error was created during initialization. This base
 816  
          * implementation just returns <b>null</b>.
 817  
          *
 818  
          * @param decl the bean declaration with initialization parameters for
 819  
          * the configuration
 820  
          * @return the new configuration object
 821  
          * @throws Exception if an error occurs
 822  
          * @since 1.4
 823  
          */
 824  
         public AbstractConfiguration getEmptyConfiguration(
 825  
                 ConfigurationDeclaration decl) throws Exception
 826  
         {
 827  0
             return null;
 828  
         }
 829  
 
 830  
         /**
 831  
          * Returns the configuration class supported by this provider. If a
 832  
          * class object was set, it is returned. Otherwise the method tries to
 833  
          * resolve the class name.
 834  
          *
 835  
          * @return the class of the configuration to be created
 836  
          * @since 1.4
 837  
          */
 838  
         protected synchronized Class fetchConfigurationClass() throws Exception
 839  
         {
 840  62
             if (getConfigurationClass() == null)
 841  
             {
 842  26
                 setConfigurationClass(loadClass(getConfigurationClassName()));
 843  
             }
 844  62
             return getConfigurationClass();
 845  
         }
 846  
 
 847  
         /**
 848  
          * Loads the class with the specified name dynamically. If the class's
 849  
          * name is <b>null</b>, <b>null</b> will also be returned.
 850  
          *
 851  
          * @param className the name of the class to be loaded
 852  
          * @return the class object
 853  
          * @throws ClassNotFoundException if class loading fails
 854  
          * @since 1.4
 855  
          */
 856  
         protected Class loadClass(String className)
 857  
                 throws ClassNotFoundException
 858  
         {
 859  26
             return (className != null) ? Class.forName(className, true,
 860  
                     getClass().getClassLoader()) : null;
 861  
         }
 862  
     }
 863  
 
 864  
     /**
 865  
      * <p>
 866  
      * A specialized <code>BeanDeclaration</code> implementation that
 867  
      * represents the declaration of a configuration source.
 868  
      * </p>
 869  
      * <p>
 870  
      * Instances of this class are able to extract all information about a
 871  
      * configuration source from the configuration definition file. The
 872  
      * declaration of a configuration source is very similar to a bean
 873  
      * declaration processed by <code>XMLBeanDeclaration</code>. There are
 874  
      * very few differences, e.g. some reserved attributes like
 875  
      * <code>optional</code> and <code>at</code> and the fact that a bean
 876  
      * factory is never needed.
 877  
      * </p>
 878  
      */
 879  2
     public static class ConfigurationDeclaration extends XMLBeanDeclaration
 880  
     {
 881  
         /** Stores a reference to the associated configuration builder. */
 882  
         private DefaultConfigurationBuilder configurationBuilder;
 883  
 
 884  
         /**
 885  
          * Creates a new instance of <code>ConfigurationDeclaration</code> and
 886  
          * initializes it.
 887  
          *
 888  
          * @param builder the associated configuration builder
 889  
          * @param config the configuration this declaration is based onto
 890  
          */
 891  
         public ConfigurationDeclaration(DefaultConfigurationBuilder builder,
 892  
                 HierarchicalConfiguration config)
 893  
         {
 894  66
             super(config);
 895  66
             configurationBuilder = builder;
 896  66
         }
 897  
 
 898  
         /**
 899  
          * Returns the associated configuration builder.
 900  
          *
 901  
          * @return the configuration builder
 902  
          */
 903  
         public DefaultConfigurationBuilder getConfigurationBuilder()
 904  
         {
 905  175
             return configurationBuilder;
 906  
         }
 907  
 
 908  
         /**
 909  
          * Returns the value of the <code>at</code> attribute.
 910  
          *
 911  
          * @return the value of the <code>at</code> attribute (can be <b>null</b>)
 912  
          */
 913  
         public String getAt()
 914  
         {
 915  54
             String result = this.getConfiguration().getString(ATTR_AT_RES);
 916  54
             return (result == null) ? this.getConfiguration().getString(ATTR_AT)
 917  
                     : result;
 918  
         }
 919  
 
 920  
         /**
 921  
          * Returns a flag whether this is an optional configuration.
 922  
          *
 923  
          * @return a flag if this declaration points to an optional
 924  
          * configuration
 925  
          */
 926  
         public boolean isOptional()
 927  
         {
 928  17
             Boolean value = this.getConfiguration().getBoolean(ATTR_OPTIONAL_RES,
 929  
                     null);
 930  17
             if (value == null)
 931  
             {
 932  11
                 value = this.getConfiguration().getBoolean(ATTR_OPTIONAL,
 933  
                         Boolean.FALSE);
 934  
             }
 935  16
             return value.booleanValue();
 936  
         }
 937  
 
 938  
         /**
 939  
          * Returns a flag whether this configuration should always be created
 940  
          * and added to the resulting combined configuration. This flag is
 941  
          * evaluated only for optional configurations whose normal creation has
 942  
          * caused an error. If for such a configuration the
 943  
          * <code>forceCreate</code> attribute is set and the corresponding
 944  
          * configuration provider supports this mode, an empty configuration
 945  
          * will be created and added to the resulting combined configuration.
 946  
          *
 947  
          * @return the value of the <code>forceCreate</code> attribute
 948  
          * @since 1.4
 949  
          */
 950  
         public boolean isForceCreate()
 951  
         {
 952  11
             return this.getConfiguration().getBoolean(ATTR_FORCECREATE, false);
 953  
         }
 954  
 
 955  
         /**
 956  
          * Returns the name of the bean factory. For configuration source
 957  
          * declarations always a reserved factory is used. This factory's name
 958  
          * is returned by this implementation.
 959  
          *
 960  
          * @return the name of the bean factory
 961  
          */
 962  
         public String getBeanFactoryName()
 963  
         {
 964  62
             return CONFIG_BEAN_FACTORY_NAME;
 965  
         }
 966  
 
 967  
         /**
 968  
          * Returns the bean's class name. This implementation will always return
 969  
          * <b>null</b>.
 970  
          *
 971  
          * @return the name of the bean's class
 972  
          */
 973  
         public String getBeanClassName()
 974  
         {
 975  62
             return null;
 976  
         }
 977  
 
 978  
         /**
 979  
          * Checks whether the given node is reserved. This method will take
 980  
          * further reserved attributes into account
 981  
          *
 982  
          * @param nd the node
 983  
          * @return a flag whether this node is reserved
 984  
          */
 985  
         protected boolean isReservedNode(ConfigurationNode nd)
 986  
         {
 987  118
             if (super.isReservedNode(nd))
 988  
             {
 989  33
                 return true;
 990  
             }
 991  
 
 992  85
             return nd.isAttribute()
 993  
                     && ((ATTR_ATNAME.equals(nd.getName()) && nd.getParentNode()
 994  
                             .getAttributeCount(RESERVED_PREFIX + ATTR_ATNAME) == 0) || (ATTR_OPTIONALNAME
 995  
                             .equals(nd.getName()) && nd.getParentNode()
 996  
                             .getAttributeCount(RESERVED_PREFIX + ATTR_OPTIONALNAME) == 0));
 997  
         }
 998  
 
 999  
         /**
 1000  
          * Performs interpolation. This implementation will delegate
 1001  
          * interpolation to the configuration builder, which takes care that the
 1002  
          * currently constructed configuration is taken into account, too.
 1003  
          *
 1004  
          * @param value the value to be interpolated
 1005  
          * @return the interpolated value
 1006  
          */
 1007  
         protected Object interpolate(Object value)
 1008  
         {
 1009  61
             return getConfigurationBuilder().interpolate(value);
 1010  
         }
 1011  
     }
 1012  
 
 1013  
     /**
 1014  
      * A specialized <code>BeanFactory</code> implementation that handles
 1015  
      * configuration declarations. This class will retrieve the correct
 1016  
      * configuration provider and delegate the task of creating the
 1017  
      * configuration to this object.
 1018  
      */
 1019  2
     static class ConfigurationBeanFactory implements BeanFactory
 1020  
     {
 1021  
         /**
 1022  
          * Creates an instance of a bean class. This implementation expects that
 1023  
          * the passed in bean declaration is a declaration for a configuration.
 1024  
          * It will determine the responsible configuration provider and delegate
 1025  
          * the call to this instance. If creation of the configuration fails
 1026  
          * and the <code>optional</code> attribute is set, the exception will
 1027  
          * be ignored. If the <code>forceCreate</code> attribute is set, too,
 1028  
          * the provider is asked to create an empty configuration. A return
 1029  
          * value of <b>null</b> means that no configuration could be created.
 1030  
          *
 1031  
          * @param beanClass the bean class (will be ignored)
 1032  
          * @param data the declaration
 1033  
          * @param param an additional parameter (will be ignored)
 1034  
          * @return the newly created configuration
 1035  
          * @throws Exception if an error occurs
 1036  
          */
 1037  
         public Object createBean(Class beanClass, BeanDeclaration data,
 1038  
                 Object param) throws Exception
 1039  
         {
 1040  62
             ConfigurationDeclaration decl = (ConfigurationDeclaration) data;
 1041  62
             String tagName = decl.getNode().getName();
 1042  62
             ConfigurationProvider provider = decl.getConfigurationBuilder()
 1043  
                     .providerForTag(tagName);
 1044  62
             if (provider == null)
 1045  
             {
 1046  1
                 throw new ConfigurationRuntimeException(
 1047  
                         "No ConfigurationProvider registered for tag "
 1048  
                                 + tagName);
 1049  
             }
 1050  
 
 1051  
             try
 1052  
             {
 1053  61
                 return provider.getConfiguration(decl);
 1054  
             }
 1055  
             catch (Exception ex)
 1056  
             {
 1057  
                 // If this is an optional configuration, ignore the exception
 1058  12
                 if (!decl.isOptional())
 1059  
                 {
 1060  1
                     throw ex;
 1061  
                 }
 1062  
                 else
 1063  
                 {
 1064  11
                     if (decl.isForceCreate())
 1065  
                     {
 1066  
                         try
 1067  
                         {
 1068  3
                             return provider.getEmptyConfiguration(decl);
 1069  
                         }
 1070  
                         catch (Exception ex2)
 1071  
                         {
 1072  
                             // Ignore exception, return null in this case
 1073  1
                             ;
 1074  
                         }
 1075  
                     }
 1076  9
                     return null;
 1077  
                 }
 1078  
             }
 1079  
         }
 1080  
 
 1081  
         /**
 1082  
          * Returns the default class for this bean factory.
 1083  
          *
 1084  
          * @return the default class
 1085  
          */
 1086  
         public Class getDefaultBeanClass()
 1087  
         {
 1088  
             // Here some valid class must be returned, otherwise BeanHelper
 1089  
             // will complain that the bean's class cannot be determined
 1090  62
             return Configuration.class;
 1091  
         }
 1092  
     }
 1093  
 
 1094  
     /**
 1095  
      * A specialized provider implementation that deals with file based
 1096  
      * configurations. Ensures that the base path is correctly set and that the
 1097  
      * load() method gets called.
 1098  
      */
 1099  
     public static class FileConfigurationProvider extends ConfigurationProvider
 1100  
     {
 1101  
         /**
 1102  
          * Creates a new instance of <code>FileConfigurationProvider</code>.
 1103  
          */
 1104  
         public FileConfigurationProvider()
 1105  
         {
 1106  4
             super();
 1107  4
         }
 1108  
 
 1109  
         /**
 1110  
          * Creates a new instance of <code>FileConfigurationProvider</code>
 1111  
          * and sets the configuration class.
 1112  
          *
 1113  
          * @param configClass the class for the configurations to be created
 1114  
          */
 1115  
         public FileConfigurationProvider(Class configClass)
 1116  
         {
 1117  2
             super(configClass);
 1118  2
         }
 1119  
 
 1120  
         /**
 1121  
          * Creates a new instance of <code>FileConfigurationProvider</code>
 1122  
          * and sets the configuration class name.
 1123  
          *
 1124  
          * @param configClassName the name of the configuration to be created
 1125  
          * @since 1.4
 1126  
          */
 1127  
         public FileConfigurationProvider(String configClassName)
 1128  
         {
 1129  0
             super(configClassName);
 1130  0
         }
 1131  
 
 1132  
         /**
 1133  
          * Creates the configuration. After that <code>load()</code> will be
 1134  
          * called. If this configuration is marked as optional, exceptions will
 1135  
          * be ignored.
 1136  
          *
 1137  
          * @param decl the declaration
 1138  
          * @return the new configuration
 1139  
          * @throws Exception if an error occurs
 1140  
          */
 1141  
         public AbstractConfiguration getConfiguration(
 1142  
                 ConfigurationDeclaration decl) throws Exception
 1143  
         {
 1144  51
             AbstractConfiguration result = getEmptyConfiguration(decl);
 1145  51
             ((FileConfiguration) result).load();
 1146  42
             return result;
 1147  
         }
 1148  
 
 1149  
         /**
 1150  
          * Returns an uninitialized file configuration. This method will be
 1151  
          * called for optional configurations when the
 1152  
          * <code>getConfiguration()</code> method caused an error and the
 1153  
          * <code>forceCreate</code> attribute is set. It will create the
 1154  
          * configuration of the represented type, but the <code>load()</code>
 1155  
          * method won't be called. This way non-existing configuration files can
 1156  
          * be handled gracefully: If loading a the file fails, an empty
 1157  
          * configuration will be created that is already configured with the
 1158  
          * correct file name.
 1159  
          *
 1160  
          * @param decl the bean declaration with initialization parameters for
 1161  
          * the configuration
 1162  
          * @return the new configuration object
 1163  
          * @throws Exception if an error occurs
 1164  
          * @since 1.4
 1165  
          */
 1166  
         public AbstractConfiguration getEmptyConfiguration(
 1167  
                 ConfigurationDeclaration decl) throws Exception
 1168  
         {
 1169  52
             return super.getConfiguration(decl);
 1170  
         }
 1171  
 
 1172  
         /**
 1173  
          * Initializes the bean instance. Ensures that the file configuration's
 1174  
          * base path will be initialized with the base path of the factory so
 1175  
          * that relative path names can be correctly resolved.
 1176  
          *
 1177  
          * @param bean the bean to be initialized
 1178  
          * @param data the declaration
 1179  
          * @throws Exception if an error occurs
 1180  
          */
 1181  
         protected void initBeanInstance(Object bean, BeanDeclaration data)
 1182  
                 throws Exception
 1183  
         {
 1184  52
             FileConfiguration config = (FileConfiguration) bean;
 1185  52
             config.setBasePath(((ConfigurationDeclaration) data)
 1186  
                     .getConfigurationBuilder().getConfigurationBasePath());
 1187  52
             super.initBeanInstance(bean, data);
 1188  52
         }
 1189  
     }
 1190  
 
 1191  
     /**
 1192  
      * A specialized configuration provider for file based configurations that
 1193  
      * can handle configuration sources whose concrete type depends on the
 1194  
      * extension of the file to be loaded. One example is the
 1195  
      * <code>properties</code> tag: if the file ends with ".xml" a
 1196  
      * XMLPropertiesConfiguration object must be created, otherwise a
 1197  
      * PropertiesConfiguration object.
 1198  
      */
 1199  2
     static class FileExtensionConfigurationProvider extends
 1200  
             FileConfigurationProvider
 1201  
     {
 1202  
         /**
 1203  
          * Stores the class to be created when the file extension matches.
 1204  
          */
 1205  
         private Class matchingClass;
 1206  
 
 1207  
         /**
 1208  
          * Stores the name of the class to be created when the file extension
 1209  
          * matches.
 1210  
          */
 1211  
         private String matchingClassName;
 1212  
 
 1213  
         /**
 1214  
          * Stores the class to be created when the file extension does not
 1215  
          * match.
 1216  
          */
 1217  
         private Class defaultClass;
 1218  
 
 1219  
         /**
 1220  
          * Stores the name of the class to be created when the file extension
 1221  
          * does not match.
 1222  
          */
 1223  
         private String defaultClassName;
 1224  
 
 1225  
         /** Stores the file extension to be checked against. */
 1226  
         private String fileExtension;
 1227  
 
 1228  
         /**
 1229  
          * Creates a new instance of
 1230  
          * <code>FileExtensionConfigurationProvider</code> and initializes it.
 1231  
          *
 1232  
          * @param matchingClass the class to be created when the file extension
 1233  
          * matches
 1234  
          * @param defaultClass the class to be created when the file extension
 1235  
          * does not match
 1236  
          * @param extension the file extension to be checked agains
 1237  
          */
 1238  
         public FileExtensionConfigurationProvider(Class matchingClass,
 1239  
                 Class defaultClass, String extension)
 1240  2
         {
 1241  2
             this.matchingClass = matchingClass;
 1242  2
             this.defaultClass = defaultClass;
 1243  2
             fileExtension = extension;
 1244  2
         }
 1245  
 
 1246  
         /**
 1247  
          * Creates a new instance of
 1248  
          * <code>FileExtensionConfigurationProvider</code> and initializes it
 1249  
          * with the names of the classes to be created.
 1250  
          *
 1251  
          * @param matchingClassName the name of the class to be created when the
 1252  
          * file extension matches
 1253  
          * @param defaultClassName the name of the class to be created when the
 1254  
          * file extension does not match
 1255  
          * @param extension the file extension to be checked agains
 1256  
          * @since 1.4
 1257  
          */
 1258  
         public FileExtensionConfigurationProvider(String matchingClassName,
 1259  
                 String defaultClassName, String extension)
 1260  2
         {
 1261  2
             this.matchingClassName = matchingClassName;
 1262  2
             this.defaultClassName = defaultClassName;
 1263  2
             fileExtension = extension;
 1264  2
         }
 1265  
 
 1266  
         /**
 1267  
          * Returns the matching class object, no matter whether it was defined
 1268  
          * as a class or as a class name.
 1269  
          *
 1270  
          * @return the matching class object
 1271  
          * @throws Exception if an error occurs
 1272  
          * @since 1.4
 1273  
          */
 1274  
         protected synchronized Class fetchMatchingClass() throws Exception
 1275  
         {
 1276  6
             if (matchingClass == null)
 1277  
             {
 1278  0
                 matchingClass = loadClass(matchingClassName);
 1279  
             }
 1280  6
             return matchingClass;
 1281  
         }
 1282  
 
 1283  
         /**
 1284  
          * Returns the default class object, no matter whether it was defined as
 1285  
          * a class or as a class name.
 1286  
          *
 1287  
          * @return the default class object
 1288  
          * @throws Exception if an error occurs
 1289  
          * @since 1.4
 1290  
          */
 1291  
         protected synchronized Class fetchDefaultClass() throws Exception
 1292  
         {
 1293  20
             if (defaultClass == null)
 1294  
             {
 1295  0
                 defaultClass = loadClass(defaultClassName);
 1296  
             }
 1297  20
             return defaultClass;
 1298  
         }
 1299  
 
 1300  
         /**
 1301  
          * Creates the configuration object. The class is determined by the file
 1302  
          * name's extension.
 1303  
          *
 1304  
          * @param beanClass the class
 1305  
          * @param data the bean declaration
 1306  
          * @return the new bean
 1307  
          * @throws Exception if an error occurs
 1308  
          */
 1309  
         protected Object createBeanInstance(Class beanClass,
 1310  
                 BeanDeclaration data) throws Exception
 1311  
         {
 1312  26
             String fileName = ((ConfigurationDeclaration) data)
 1313  
                     .getConfiguration().getString(ATTR_FILENAME);
 1314  26
             if (fileName != null
 1315  
                     && fileName.toLowerCase().trim().endsWith(fileExtension))
 1316  
             {
 1317  6
                 return super.createBeanInstance(fetchMatchingClass(), data);
 1318  
             }
 1319  
             else
 1320  
             {
 1321  20
                 return super.createBeanInstance(fetchDefaultClass(), data);
 1322  
             }
 1323  
         }
 1324  
     }
 1325  
 
 1326  
     /**
 1327  
      * A specialized configuration provider class that allows to include other
 1328  
      * configuration definition files.
 1329  
      */
 1330  
     static class ConfigurationBuilderProvider extends ConfigurationProvider
 1331  
     {
 1332  
         /**
 1333  
          * Creates a new instance of <code>ConfigurationBuilderProvider</code>.
 1334  
          */
 1335  
         public ConfigurationBuilderProvider()
 1336  
         {
 1337  3
             super(DefaultConfigurationBuilder.class);
 1338  3
         }
 1339  
 
 1340  
         /**
 1341  
          * Creates the configuration. First creates a configuration builder
 1342  
          * object. Then returns the configuration created by this builder.
 1343  
          *
 1344  
          * @param decl the configuration declaration
 1345  
          * @return the configuration
 1346  
          * @exception Exception if an error occurs
 1347  
          */
 1348  
         public AbstractConfiguration getConfiguration(
 1349  
                 ConfigurationDeclaration decl) throws Exception
 1350  
         {
 1351  4
             DefaultConfigurationBuilder builder = (DefaultConfigurationBuilder) super
 1352  
                     .getConfiguration(decl);
 1353  4
             return builder.getConfiguration(true);
 1354  
         }
 1355  
 
 1356  
         /**
 1357  
          * Returns an empty configuration in case of an optional configuration
 1358  
          * could not be created. This implementation returns an empty combined
 1359  
          * configuration.
 1360  
          *
 1361  
          * @param decl the configuration declaration
 1362  
          * @return the configuration
 1363  
          * @exception Exception if an error occurs
 1364  
          * @since 1.4
 1365  
          */
 1366  
         public AbstractConfiguration getEmptyConfiguration(
 1367  
                 ConfigurationDeclaration decl) throws Exception
 1368  
         {
 1369  1
             return new CombinedConfiguration();
 1370  
         }
 1371  
     }
 1372  
 
 1373  
     static
 1374  
     {
 1375  
         // register the configuration bean factory
 1376  2
         BeanHelper.registerBeanFactory(CONFIG_BEAN_FACTORY_NAME,
 1377  
                 new ConfigurationBeanFactory());
 1378  
     }
 1379  
 }