Coverage Report - org.apache.commons.configuration.DefaultConfigurationBuilder
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultConfigurationBuilder
100%
76/76
100%
12/12
1,667
DefaultConfigurationBuilder$ConfigurationBeanFactory
100%
12/12
100%
3/3
1,667
DefaultConfigurationBuilder$ConfigurationBuilderProvider
100%
5/5
100%
1/1
1,667
DefaultConfigurationBuilder$ConfigurationDeclaration
100%
16/16
100%
4/4
1,667
DefaultConfigurationBuilder$ConfigurationProvider
100%
9/9
N/A
1,667
DefaultConfigurationBuilder$FileConfigurationProvider
100%
11/11
N/A
1,667
DefaultConfigurationBuilder$FileExtensionConfigurationProvider
100%
9/9
100%
1/1
1,667
 
 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.HashMap;
 22  
 import java.util.Iterator;
 23  
 import java.util.List;
 24  
 import java.util.Map;
 25  
 
 26  
 import org.apache.commons.configuration.beanutils.BeanDeclaration;
 27  
 import org.apache.commons.configuration.beanutils.BeanFactory;
 28  
 import org.apache.commons.configuration.beanutils.BeanHelper;
 29  
 import org.apache.commons.configuration.beanutils.DefaultBeanFactory;
 30  
 import org.apache.commons.configuration.beanutils.XMLBeanDeclaration;
 31  
 import org.apache.commons.configuration.plist.PropertyListConfiguration;
 32  
 import org.apache.commons.configuration.plist.XMLPropertyListConfiguration;
 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  
 import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
 38  
 
 39  
 /**
 40  
  * <p>
 41  
  * A factory class that creates a composite configuration from an XML based
 42  
  * <em>configuration definition file</em>.
 43  
  * </p>
 44  
  * <p>
 45  
  * This class provides an easy and flexible means for loading multiple
 46  
  * configuration sources and combining the results into a single configuration
 47  
  * object. The sources to be loaded are defined in an XML document that can
 48  
  * contain certain tags representing the different supported configuration
 49  
  * classes. If such a tag is found, the corresponding <code>Configuration</code>
 50  
  * class is instantiated and initialized using the classes of the
 51  
  * <code>beanutils</code> package (namely
 52  
  * <code>{@link org.apache.commons.configuration.beanutils.XMLBeanDeclaration XMLBeanDeclaration}</code>
 53  
  * will be used to extract the configuration's initialization parameters, which
 54  
  * allows for complex initialization szenarios).
 55  
  * </p>
 56  
  * <p>
 57  
  * It is also possible to add custom tags to the configuration definition file.
 58  
  * For this purpose register your own <code>ConfigurationProvider</code>
 59  
  * implementation for your tag using the <code>addConfigurationProvider()</code>
 60  
  * method. This provider will then be called when the corresponding custom tag
 61  
  * is detected. For the default configuration classes providers are already
 62  
  * registered.
 63  
  * </p>
 64  
  * <p>
 65  
  * The configuration definition file has the following basic structure:
 66  
  * </p>
 67  
  * <p>
 68  
  *
 69  
  * <pre>
 70  
  * &lt;configuration&gt;
 71  
  *   &lt;header&gt;
 72  
  *     &lt;!-- Optional meta information about the composite configuration --&gt;
 73  
  *   &lt;/header&gt;
 74  
  *   &lt;override&gt;
 75  
  *     &lt;!-- Declarations for override configurations --&gt;
 76  
  *   &lt;/override&gt;
 77  
  *   &lt;additional&gt;
 78  
  *     &lt;!-- Declarations for union configurations --&gt;
 79  
  *   &lt;/additional&gt;
 80  
  * &lt;/configuration&gt;
 81  
  * </pre>
 82  
  *
 83  
  * </p>
 84  
  * <p>
 85  
  * The name of the root element (here <code>configuration</code>) is
 86  
  * arbitrary. There are two sections (both of them are optional) for declaring
 87  
  * <em>override</em> and <em>additional</em> configurations. Configurations
 88  
  * in the former section are evaluated in the order of their declaration, and
 89  
  * properties of configurations declared earlier hide those of configurations
 90  
  * declared later. Configurations in the latter section are combined to a union
 91  
  * configuration, i.e. all of their properties are added to a large hierarchical
 92  
  * configuration. Configuration declarations that occur as direct children of
 93  
  * the root element are treated as override declarations.
 94  
  * </p>
 95  
  * <p>
 96  
  * Each configuration declaration consists of a tag whose name is associated
 97  
  * with a <code>ConfigurationProvider</code>. This can be one of the
 98  
  * pre-defined tags like <code>properties</code>, or <code>xml</code>, or
 99  
  * a custom tag, for which a configuration provider was registered. Attributes
 100  
  * and sub elements with specific initialization parameters can be added. There
 101  
  * are some reserved attributes with a special meaning that can be used in every
 102  
  * configuration declaration:
 103  
  * </p>
 104  
  * <p>
 105  
  * <table border="1">
 106  
  * <tr>
 107  
  * <th>Attribute</th>
 108  
  * <th>Meaning</th>
 109  
  * </tr>
 110  
  * <tr>
 111  
  * <td valign="top"><code>config-name</code></td>
 112  
  * <td>Allows to specify a name for this configuration. This name can be used
 113  
  * to obtain a reference to the configuration from the resulting combined
 114  
  * configuration (see below).</td>
 115  
  * </tr>
 116  
  * <tr>
 117  
  * <td valign="top"><code>config-at</code></td>
 118  
  * <td>With this attribute an optional prefix can be specified for the
 119  
  * properties of the corresponding configuration.</td>
 120  
  * </tr>
 121  
  * <tr>
 122  
  * <td valign="top"><code>config-optional</code></td>
 123  
  * <td>Declares a configuration as optional. This means that errors that occur
 124  
  * when creating the configuration are silently ignored.</td>
 125  
  * </tr>
 126  
  * </table>
 127  
  * </p>
 128  
  * <p>
 129  
  * The optional <em>header</em> section can contain some meta data about the
 130  
  * created configuration itself. For instance, it is possible to set further
 131  
  * properties of the <code>NodeCombiner</code> objects used for constructing
 132  
  * the resulting configuration.
 133  
  * </p>
 134  
  * <p>
 135  
  * The configuration object returned by this builder is an instance of the
 136  
  * <code>{@link CombinedConfiguration}</code> class. The return value of the
 137  
  * <code>getConfiguration()</code> method can be casted to this type, and the
 138  
  * <code>getConfiguration(boolean)</code> method directly declares
 139  
  * <code>CombinedConfiguration</code> as return type. This allows for
 140  
  * convenient access to the configuration objects maintained by the combined
 141  
  * configuration (e.g. for updates of single configuration objects). It has also
 142  
  * the advantage that the properties stored in all declared configuration
 143  
  * objects are collected and transformed into a single hierarchical structure,
 144  
  * which can be accessed using different expression engines.
 145  
  * </p>
 146  
  * <p>
 147  
  * All declared override configurations are directly added to the resulting
 148  
  * combined configuration. If they are given names (using the
 149  
  * <code>config-name</code> attribute), they can directly be accessed using
 150  
  * the <code>getConfiguration(String)</code> method of
 151  
  * <code>CombinedConfiguration</code>. The additional configurations are
 152  
  * alltogether added to another combined configuration, which uses a union
 153  
  * combiner. Then this union configuration is added to the resulting combined
 154  
  * configuration under the name defined by the <code>ADDITIONAL_NAME</code>
 155  
  * constant.
 156  
  * </p>
 157  
  * <p>
 158  
  * Implementation note: This class is not thread-safe. Especially the
 159  
  * <code>getConfiguration()</code> methods should be called by a single thread
 160  
  * only.
 161  
  * </p>
 162  
  *
 163  
  * @since 1.3
 164  
  * @author <a
 165  
  * href="http://jakarta.apache.org/commons/configuration/team-list.html">Commons
 166  
  * Configuration team</a>
 167  
  * @version $Id: DefaultConfigurationBuilder.java 384601 2006-03-09 20:22:58Z
 168  
  * oheger $
 169  
  */
 170  18
 public class DefaultConfigurationBuilder extends XMLConfiguration implements
 171  
         ConfigurationBuilder
 172  
 {
 173  
     /**
 174  
      * Constant for the name of the additional configuration. If the
 175  
      * configuration definition file contains an <code>additional</code>
 176  
      * section, a special union configuration is created and added under this
 177  
      * name to the resulting combined configuration.
 178  
      */
 179  4
     public static final String ADDITIONAL_NAME = DefaultConfigurationBuilder.class
 180  
             .getName()
 181  
             + "/ADDITIONAL_CONFIG";
 182  
 
 183  
     /** Constant for the expression engine used by this builder. */
 184  2
     static final XPathExpressionEngine EXPRESSION_ENGINE = new XPathExpressionEngine();
 185  
 
 186  
     /** Constant for the name of the configuration bean factory. */
 187  2
     static final String CONFIG_BEAN_FACTORY_NAME = DefaultConfigurationBuilder.class
 188  
             .getName()
 189  
             + ".CONFIG_BEAN_FACTORY_NAME";
 190  
 
 191  
     /** Constant for the reserved name attribute. */
 192  
     static final String ATTR_NAME = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
 193  
             + XMLBeanDeclaration.RESERVED_PREFIX
 194  
             + "name"
 195  
             + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
 196  
 
 197  
     /** Constant for the name of the at attribute. */
 198  
     static final String ATTR_ATNAME = "at";
 199  
 
 200  
     /** Constant for the reserved at attribute. */
 201  
     static final String ATTR_AT_RES = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
 202  
             + XMLBeanDeclaration.RESERVED_PREFIX
 203  
             + ATTR_ATNAME
 204  
             + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
 205  
 
 206  
     /** Constant for the at attribute without the reserved prefix. */
 207  
     static final String ATTR_AT = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
 208  
             + ATTR_ATNAME + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
 209  
 
 210  
     /** Constant for the name of the optional attribute. */
 211  
     static final String ATTR_OPTIONALNAME = "optional";
 212  
 
 213  
     /** Constant for the reserved optional attribute. */
 214  
     static final String ATTR_OPTIONAL_RES = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
 215  
             + XMLBeanDeclaration.RESERVED_PREFIX
 216  
             + ATTR_OPTIONALNAME
 217  
             + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
 218  
 
 219  
     /** Constant for the optional attribute without the reserved prefix. */
 220  
     static final String ATTR_OPTIONAL = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
 221  
             + ATTR_OPTIONALNAME + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
 222  
 
 223  
     /** Constant for the file name attribute. */
 224  
     static final String ATTR_FILENAME = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
 225  
             + "fileName" + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
 226  
 
 227  
     /** Constant for the name of the header section. */
 228  
     static final String SEC_HEADER = "header";
 229  
 
 230  
     /** Constant for an expression that selects the union configurations. */
 231  
     static final String KEY_UNION = "/additional/*";
 232  
 
 233  
     /** Constant for an expression that selects override configurations. */
 234  
     static final String KEY_OVERRIDE1 = "/*[local-name() != 'additional' and "
 235  
             + "local-name() != 'override' and local-name() != '"
 236  
             + SEC_HEADER + "']";
 237  
 
 238  
     /**
 239  
      * Constant for an expression that selects override configurations in the
 240  
      * override section.
 241  
      */
 242  
     static final String KEY_OVERRIDE2 = "/override/*";
 243  
 
 244  
     /**
 245  
      * Constant for the key that points to the list nodes definition of the
 246  
      * override combiner.
 247  
      */
 248  
     static final String KEY_OVERRIDE_LIST = SEC_HEADER
 249  
             + "/combiner/override/list-nodes/node";
 250  
 
 251  
     /**
 252  
      * Constant for the key that points to the list nodes definition of the
 253  
      * additional combiner.
 254  
      */
 255  
     static final String KEY_ADDITIONAL_LIST = SEC_HEADER
 256  
             + "/combiner/additional/list-nodes/node";
 257  
 
 258  
     /**
 259  
      * Constant for the key of the result declaration. This key can point to a
 260  
      * bean declaration, which defines properties of the resulting combined
 261  
      * configuration.
 262  
      */
 263  
     static final String KEY_RESULT = SEC_HEADER + "/result";
 264  
 
 265  
     /** Constant for the key of the combiner in the result declaration.*/
 266  
     static final String KEY_COMBINER = KEY_RESULT + "/nodeCombiner";
 267  
 
 268  
     /** Constant for the XML file extension. */
 269  
     static final String EXT_XML = ".xml";
 270  
 
 271  
     /** Constant for the provider for properties files. */
 272  2
     private static final ConfigurationProvider PROPERTIES_PROVIDER = new FileExtensionConfigurationProvider(
 273  
             XMLPropertiesConfiguration.class, PropertiesConfiguration.class,
 274  
             EXT_XML);
 275  
 
 276  
     /** Constant for the provider for XML files. */
 277  2
     private static final ConfigurationProvider XML_PROVIDER = new FileConfigurationProvider(
 278  
             XMLConfiguration.class);
 279  
 
 280  
     /** Constant for the provider for JNDI sources. */
 281  2
     private static final ConfigurationProvider JNDI_PROVIDER = new ConfigurationProvider(
 282  
             JNDIConfiguration.class);
 283  
 
 284  
     /** Constant for the provider for system properties. */
 285  2
     private static final ConfigurationProvider SYSTEM_PROVIDER = new ConfigurationProvider(
 286  
             SystemConfiguration.class);
 287  
 
 288  
     /** Constant for the provider for plist files. */
 289  2
     private static final ConfigurationProvider PLIST_PROVIDER = new FileExtensionConfigurationProvider(
 290  
             XMLPropertyListConfiguration.class,
 291  
             PropertyListConfiguration.class, EXT_XML);
 292  
 
 293  
     /** Constant for the provider for configuration definition files.*/
 294  2
     private static final ConfigurationProvider BUILDER_PROVIDER = new ConfigurationBuilderProvider();
 295  
 
 296  
     /** An array with the names of the default tags. */
 297  2
     private static final String[] DEFAULT_TAGS =
 298  
     {"properties", "xml", "hierarchicalXml", "jndi", "system", "plist", "configuration"};
 299  
 
 300  
     /** An array with the providers for the default tags. */
 301  2
     private static final ConfigurationProvider[] DEFAULT_PROVIDERS =
 302  
     {PROPERTIES_PROVIDER, XML_PROVIDER, XML_PROVIDER, JNDI_PROVIDER,
 303  
             SYSTEM_PROVIDER, PLIST_PROVIDER, BUILDER_PROVIDER};
 304  
 
 305  
     /**
 306  
      * The serial version UID.
 307  
      */
 308  
     private static final long serialVersionUID = -3113777854714492123L;
 309  
 
 310  
     /** Stores the configuration that is currently constructed.*/
 311  
     private CombinedConfiguration constructedConfiguration;
 312  
 
 313  
     /** Stores a map with the registered configuration providers. */
 314  
     private Map providers;
 315  
 
 316  
     /** Stores the base path to the configuration sources to load. */
 317  
     private String configurationBasePath;
 318  
 
 319  
     /**
 320  
      * Creates a new instance of <code>DefaultConfigurationBuilder</code>. A
 321  
      * configuration definition file is not yet loaded. Use the diverse setter
 322  
      * methods provided by file based configurations to specify the
 323  
      * configuration definition file.
 324  
      */
 325  
     public DefaultConfigurationBuilder()
 326  
     {
 327  31
         super();
 328  31
         providers = new HashMap();
 329  31
         setExpressionEngine(EXPRESSION_ENGINE);
 330  31
         registerDefaultProviders();
 331  31
     }
 332  
 
 333  
     /**
 334  
      * Creates a new instance of <code>DefaultConfigurationBuilder</code> and
 335  
      * sets the specified configuration definition file.
 336  
      *
 337  
      * @param file the configuration definition file
 338  
      */
 339  
     public DefaultConfigurationBuilder(File file)
 340  
     {
 341  1
         this();
 342  1
         setFile(file);
 343  1
     }
 344  
 
 345  
     /**
 346  
      * Creates a new instance of <code>DefaultConfigurationBuilder</code> and
 347  
      * sets the specified configuration definition file.
 348  
      *
 349  
      * @param fileName the name of the configuration definition file
 350  
      * @throws ConfigurationException if an error occurs when the file is loaded
 351  
      */
 352  
     public DefaultConfigurationBuilder(String fileName)
 353  
             throws ConfigurationException
 354  
     {
 355  2
         this();
 356  2
         setFileName(fileName);
 357  2
     }
 358  
 
 359  
     /**
 360  
      * Creates a new instance of <code>DefaultConfigurationBuilder</code> and
 361  
      * sets the specified configuration definition file.
 362  
      *
 363  
      * @param url the URL to the configuration definition file
 364  
      * @throws ConfigurationException if an error occurs when the file is loaded
 365  
      */
 366  
     public DefaultConfigurationBuilder(URL url) throws ConfigurationException
 367  
     {
 368  1
         this();
 369  1
         setURL(url);
 370  1
     }
 371  
 
 372  
     /**
 373  
      * Returns the base path for the configuration sources to load. This path is
 374  
      * used to resolve relative paths in the configuration definition file.
 375  
      *
 376  
      * @return the base path for configuration sources
 377  
      */
 378  
     public String getConfigurationBasePath()
 379  
     {
 380  50
         return (configurationBasePath != null) ? configurationBasePath
 381  
                 : getBasePath();
 382  
     }
 383  
 
 384  
     /**
 385  
      * Sets the base path for the configuration sources to load. Normally a base
 386  
      * path need not to be set because it is determined by the location of the
 387  
      * configuration definition file to load. All relative pathes in this file
 388  
      * are resolved relative to this file. Setting a base path makes sense if
 389  
      * such relative pathes should be otherwise resolved, e.g. if the
 390  
      * configuration file is loaded from the class path and all sub
 391  
      * configurations it refers to are stored in a special config directory.
 392  
      *
 393  
      * @param configurationBasePath the new base path to set
 394  
      */
 395  
     public void setConfigurationBasePath(String configurationBasePath)
 396  
     {
 397  1
         this.configurationBasePath = configurationBasePath;
 398  1
     }
 399  
 
 400  
     /**
 401  
      * Adds a configuration provider for the specified tag. Whenever this tag is
 402  
      * encountered in the configuration definition file this provider will be
 403  
      * called to create the configuration object.
 404  
      *
 405  
      * @param tagName the name of the tag in the configuration definition file
 406  
      * @param provider the provider for this tag
 407  
      */
 408  
     public void addConfigurationProvider(String tagName,
 409  
             ConfigurationProvider provider)
 410  
     {
 411  222
         if (tagName == null)
 412  
         {
 413  1
             throw new IllegalArgumentException("Tag name must not be null!");
 414  
         }
 415  221
         if (provider == null)
 416  
         {
 417  1
             throw new IllegalArgumentException("Provider must not be null!");
 418  
         }
 419  
 
 420  220
         providers.put(tagName, provider);
 421  220
     }
 422  
 
 423  
     /**
 424  
      * Removes the configuration provider for the specified tag name.
 425  
      *
 426  
      * @param tagName the tag name
 427  
      * @return the removed configuration provider or <b>null</b> if none was
 428  
      * registered for that tag
 429  
      */
 430  
     public ConfigurationProvider removeConfigurationProvider(String tagName)
 431  
     {
 432  3
         return (ConfigurationProvider) providers.remove(tagName);
 433  
     }
 434  
 
 435  
     /**
 436  
      * Returns the configuration provider for the given tag.
 437  
      *
 438  
      * @param tagName the name of the tag
 439  
      * @return the provider that was registered for this tag or <b>null</b> if
 440  
      * there is none
 441  
      */
 442  
     public ConfigurationProvider providerForTag(String tagName)
 443  
     {
 444  62
         return (ConfigurationProvider) providers.get(tagName);
 445  
     }
 446  
 
 447  
     /**
 448  
      * Returns the configuration provided by this builder. Loads and parses the
 449  
      * configuration definition file and creates instances for the declared
 450  
      * configurations.
 451  
      *
 452  
      * @return the configuration
 453  
      * @throws ConfigurationException if an error occurs
 454  
      */
 455  
     public Configuration getConfiguration() throws ConfigurationException
 456  
     {
 457  10
         return getConfiguration(true);
 458  
     }
 459  
 
 460  
     /**
 461  
      * Returns the configuration provided by this builder. If the boolean
 462  
      * parameter is <b>true</b>, the configuration definition file will be
 463  
      * loaded. It will then be parsed, and instances for the declared
 464  
      * configurations will be created.
 465  
      *
 466  
      * @param load a flag whether the configuration definition file should be
 467  
      * loaded; a value of <b>false</b> would make sense if the file has already
 468  
      * been created or its content was manipulated using some of the property
 469  
      * accessor methods
 470  
      * @return the configuration
 471  
      * @throws ConfigurationException if an error occurs
 472  
      */
 473  
     public CombinedConfiguration getConfiguration(boolean load)
 474  
             throws ConfigurationException
 475  
     {
 476  18
         if (load)
 477  
         {
 478  15
             load();
 479  
         }
 480  
 
 481  17
         CombinedConfiguration result = createResultConfiguration();
 482  17
         constructedConfiguration = result;
 483  
 
 484  17
         List overrides = configurationsAt(KEY_OVERRIDE1);
 485  17
         overrides.addAll(configurationsAt(KEY_OVERRIDE2));
 486  17
         initCombinedConfiguration(result, overrides, KEY_OVERRIDE_LIST);
 487  
 
 488  16
         List additionals = configurationsAt(KEY_UNION);
 489  16
         if (!additionals.isEmpty())
 490  
         {
 491  6
             CombinedConfiguration addConfig = new CombinedConfiguration(
 492  
                     new UnionCombiner());
 493  6
             result.addConfiguration(addConfig, ADDITIONAL_NAME);
 494  6
             initCombinedConfiguration(addConfig, additionals,
 495  
                     KEY_ADDITIONAL_LIST);
 496  
         }
 497  
 
 498  16
         return result;
 499  
     }
 500  
 
 501  
     /**
 502  
      * Creates the resulting combined configuration. This method is called by
 503  
      * <code>getConfiguration()</code>. It checks whether the
 504  
      * <code>header</code> section of the configuration definition file
 505  
      * contains a <code>result</code> element. If this is the case, it will be
 506  
      * used to initialize the properties of the newly created configuration
 507  
      * object.
 508  
      *
 509  
      * @return the resulting configuration object
 510  
      * @throws ConfigurationException if an error occurs
 511  
      */
 512  
     protected CombinedConfiguration createResultConfiguration()
 513  
             throws ConfigurationException
 514  
     {
 515  17
         XMLBeanDeclaration decl = new XMLBeanDeclaration(this, KEY_RESULT, true);
 516  17
         CombinedConfiguration result = (CombinedConfiguration) BeanHelper
 517  
                 .createBean(decl, CombinedConfiguration.class);
 518  
 
 519  17
         if (getMaxIndex(KEY_COMBINER) < 0)
 520  
         {
 521  
             // No combiner defined => set default
 522  14
             result.setNodeCombiner(new OverrideCombiner());
 523  
         }
 524  
 
 525  17
         return result;
 526  
     }
 527  
 
 528  
     /**
 529  
      * Initializes a combined configuration for the configurations of a specific
 530  
      * section. This method is called for the override and for the additional
 531  
      * section (if it exists).
 532  
      *
 533  
      * @param config the configuration to be initialized
 534  
      * @param containedConfigs the list with the declaratinos of the contained
 535  
      * configurations
 536  
      * @param keyListNodes a list with the declaration of list nodes
 537  
      * @throws ConfigurationException if an error occurs
 538  
      */
 539  
     protected void initCombinedConfiguration(CombinedConfiguration config,
 540  
             List containedConfigs, String keyListNodes) throws ConfigurationException
 541  
     {
 542  23
         List listNodes = getList(keyListNodes);
 543  52
         for (Iterator it = listNodes.iterator(); it.hasNext();)
 544  
         {
 545  6
             config.getNodeCombiner().addListNode((String) it.next());
 546  
         }
 547  
 
 548  102
         for (Iterator it = containedConfigs.iterator(); it.hasNext();)
 549  
         {
 550  57
             HierarchicalConfiguration conf = (HierarchicalConfiguration) it
 551  
                     .next();
 552  57
             ConfigurationDeclaration decl = new ConfigurationDeclaration(this,
 553  
                     conf);
 554  57
             AbstractConfiguration newConf = createConfigurationAt(decl);
 555  56
             if (newConf != null)
 556  
             {
 557  48
                 config.addConfiguration(newConf, decl.getConfiguration()
 558  
                         .getString(ATTR_NAME), decl.getAt());
 559  
             }
 560  
         }
 561  22
     }
 562  
 
 563  
     /**
 564  
      * Registers the default configuration providers supported by this class.
 565  
      * This method will be called during initialization. It registers
 566  
      * configuration providers for the tags that are supported by default.
 567  
      */
 568  
     protected void registerDefaultProviders()
 569  
     {
 570  248
         for (int i = 0; i < DEFAULT_TAGS.length; i++)
 571  
         {
 572  217
             addConfigurationProvider(DEFAULT_TAGS[i], DEFAULT_PROVIDERS[i]);
 573  
         }
 574  31
     }
 575  
 
 576  
     /**
 577  
      * Performs interpolation. This method will not only take this configuration
 578  
      * instance into account (which is the one that loaded the configuration
 579  
      * definition file), but also the so far constructed combined configuration.
 580  
      * So variables can be used that point to properties that are defined in
 581  
      * configuration sources loaded by this builder.
 582  
      *
 583  
      * @param value the value to be interpolated
 584  
      * @return the interpolated value
 585  
      */
 586  
     protected Object interpolate(Object value)
 587  
     {
 588  309
         Object result = super.interpolate(value);
 589  309
         if (constructedConfiguration != null)
 590  
         {
 591  252
             result = constructedConfiguration.interpolate(result);
 592  
         }
 593  309
         return result;
 594  
     }
 595  
 
 596  
     /**
 597  
      * Creates a configuration object from the specified configuration
 598  
      * declaration.
 599  
      *
 600  
      * @param decl the configuration declaration
 601  
      * @return the new configuration object
 602  
      * @throws ConfigurationException if an error occurs
 603  
      */
 604  
     private AbstractConfiguration createConfigurationAt(
 605  
             ConfigurationDeclaration decl) throws ConfigurationException
 606  
     {
 607  
         try
 608  
         {
 609  57
             return (AbstractConfiguration) BeanHelper.createBean(decl);
 610  
         }
 611  
         catch (Exception ex)
 612  
         {
 613  
             // redirect to configuration exceptions
 614  1
             throw new ConfigurationException(ex);
 615  
         }
 616  
     }
 617  
 
 618  
     /**
 619  
      * <p>
 620  
      * A base class for creating and initializing configuration sources.
 621  
      * </p>
 622  
      * <p>
 623  
      * Concrete sub classes of this base class are responsible for creating
 624  
      * specific <code>Configuration</code> objects for the tags in the
 625  
      * configuration definition file. The configuration factory will parse the
 626  
      * definition file and try to find a matching
 627  
      * <code>ConfigurationProvider</code> for each encountered tag. This
 628  
      * provider is then asked to create a corresponding
 629  
      * <code>Configuration</code> object. It is up to a concrete
 630  
      * implementation how this object is created and initialized.
 631  
      * </p>
 632  
      * <p>
 633  
      * Note that at the moment only configuration classes derived from
 634  
      * <code>{@link AbstractConfiguration}</code> are supported.
 635  
      * </p>
 636  
      */
 637  
     public static class ConfigurationProvider extends DefaultBeanFactory
 638  
     {
 639  
         /** Stores the class of the configuration to be created. */
 640  
         private Class configurationClass;
 641  
 
 642  
         /**
 643  
          * Creates a new uninitialized instance of
 644  
          * <code>ConfigurationProvider</code>.
 645  
          */
 646  
         public ConfigurationProvider()
 647  
         {
 648  7
             this(null);
 649  7
         }
 650  
 
 651  
         /**
 652  
          * Creates a new instance of <code>ConfigurationProvider</code> and
 653  
          * sets the class of the configuration created by this provider.
 654  
          *
 655  
          * @param configClass the configuration class
 656  
          */
 657  
         public ConfigurationProvider(Class configClass)
 658  16
         {
 659  16
             setConfigurationClass(configClass);
 660  16
         }
 661  
 
 662  
         /**
 663  
          * Returns the class of the configuration returned by this provider.
 664  
          *
 665  
          * @return the class of the provided configuration
 666  
          */
 667  
         public Class getConfigurationClass()
 668  
         {
 669  58
             return configurationClass;
 670  
         }
 671  
 
 672  
         /**
 673  
          * Sets the class of the configuration returned by this provider.
 674  
          *
 675  
          * @param configurationClass the configuration class
 676  
          */
 677  
         public void setConfigurationClass(Class configurationClass)
 678  
         {
 679  16
             this.configurationClass = configurationClass;
 680  16
         }
 681  
 
 682  
         /**
 683  
          * Returns the configuration. This method is called to fetch the
 684  
          * configuration from the provider. This implementation will call the
 685  
          * inherited
 686  
          * <code>{@link org.apache.commons.configuration.beanutils.DefaultBeanFactory#createBean(Class, BeanDeclaration, Object) createBean()}</code>
 687  
          * method to create a new instance of the configuration class.
 688  
          *
 689  
          * @param decl the bean declaration with initialization parameters for
 690  
          * the configuration
 691  
          * @return the new configuration object
 692  
          * @throws Exception if an error occurs
 693  
          */
 694  
         public AbstractConfiguration getConfiguration(
 695  
                 ConfigurationDeclaration decl) throws Exception
 696  
         {
 697  58
             return (AbstractConfiguration) createBean(getConfigurationClass(),
 698  
                     decl, null);
 699  
         }
 700  
     }
 701  
 
 702  
     /**
 703  
      * <p>
 704  
      * A specialized <code>BeanDeclaration</code> implementation that
 705  
      * represents the declaration of a configuration source.
 706  
      * </p>
 707  
      * <p>
 708  
      * Instances of this class are able to extract all information about a
 709  
      * configuration source from the configuration definition file. The
 710  
      * declaration of a configuration source is very similar to a bean
 711  
      * declaration processed by <code>XMLBeanDeclaration</code>. There are
 712  
      * very few differences, e.g. the two reserved attributes
 713  
      * <code>optional</code> and <code>at</code> and the fact that a bean
 714  
      * factory is never needed.
 715  
      * </p>
 716  
      */
 717  2
     protected static class ConfigurationDeclaration extends XMLBeanDeclaration
 718  
     {
 719  
         /** Stores a reference to the associated configuration builder. */
 720  
         private DefaultConfigurationBuilder configurationBuilder;
 721  
 
 722  
         /**
 723  
          * Creates a new instance of <code>ConfigurationDeclaration</code> and
 724  
          * initializes it.
 725  
          *
 726  
          * @param builder the associated configuration builder
 727  
          * @param config the configuration this declaration is based onto
 728  
          */
 729  
         public ConfigurationDeclaration(DefaultConfigurationBuilder builder,
 730  
                 HierarchicalConfiguration config)
 731  
         {
 732  63
             super(config);
 733  63
             configurationBuilder = builder;
 734  63
         }
 735  
 
 736  
         /**
 737  
          * Returns the associated configuration builder.
 738  
          *
 739  
          * @return the configuration builder
 740  
          */
 741  
         public DefaultConfigurationBuilder getConfigurationBuilder()
 742  
         {
 743  166
             return configurationBuilder;
 744  
         }
 745  
 
 746  
         /**
 747  
          * Returns the value of the <code>at</code> attribute.
 748  
          *
 749  
          * @return the value of the <code>at</code> attribute (can be <b>null</b>)
 750  
          */
 751  
         public String getAt()
 752  
         {
 753  52
             String result = this.getConfiguration().getString(ATTR_AT_RES);
 754  52
             return (result == null) ? this.getConfiguration().getString(ATTR_AT)
 755  
                     : result;
 756  
         }
 757  
 
 758  
         /**
 759  
          * Returns a flag whether this is an optional configuration.
 760  
          *
 761  
          * @return a flag if this declaration points to an optional
 762  
          * configuration
 763  
          */
 764  
         public boolean isOptional()
 765  
         {
 766  14
             Boolean value = this.getConfiguration().getBoolean(ATTR_OPTIONAL_RES,
 767  
                     null);
 768  14
             if (value == null)
 769  
             {
 770  11
                 value = this.getConfiguration().getBoolean(ATTR_OPTIONAL,
 771  
                         Boolean.FALSE);
 772  
             }
 773  13
             return value.booleanValue();
 774  
         }
 775  
 
 776  
         /**
 777  
          * Returns the name of the bean factory. For configuration source
 778  
          * declarations always a reserved factory is used. This factory's name
 779  
          * is returned by this implementation.
 780  
          *
 781  
          * @return the name of the bean factory
 782  
          */
 783  
         public String getBeanFactoryName()
 784  
         {
 785  59
             return CONFIG_BEAN_FACTORY_NAME;
 786  
         }
 787  
 
 788  
         /**
 789  
          * Returns the bean's class name. This implementation will always return
 790  
          * <b>null</b>.
 791  
          *
 792  
          * @return the name of the bean's class
 793  
          */
 794  
         public String getBeanClassName()
 795  
         {
 796  59
             return null;
 797  
         }
 798  
 
 799  
         /**
 800  
          * Checks whether the given node is reserved. This method will take
 801  
          * further reserved attributes into account
 802  
          *
 803  
          * @param nd the node
 804  
          * @return a flag whether this node is reserved
 805  
          */
 806  
         protected boolean isReservedNode(ConfigurationNode nd)
 807  
         {
 808  102
             if (super.isReservedNode(nd))
 809  
             {
 810  21
                 return true;
 811  
             }
 812  
 
 813  81
             return nd.isAttribute()
 814  
                     && ((ATTR_ATNAME.equals(nd.getName()) && nd.getParentNode()
 815  
                             .getAttributeCount(RESERVED_PREFIX + ATTR_ATNAME) == 0) || (ATTR_OPTIONALNAME
 816  
                             .equals(nd.getName()) && nd.getParentNode()
 817  
                             .getAttributeCount(RESERVED_PREFIX + ATTR_OPTIONALNAME) == 0));
 818  
         }
 819  
 
 820  
         /**
 821  
          * Performs interpolation. This implementation will delegate
 822  
          * interpolation to the configuration builder, which takes care that the
 823  
          * currently constructed configuration is taken into account, too.
 824  
          *
 825  
          * @param value the value to be interpolated
 826  
          * @return the interpolated value
 827  
          */
 828  
         protected Object interpolate(Object value)
 829  
         {
 830  57
             return getConfigurationBuilder().interpolate(value);
 831  
         }
 832  
     }
 833  
 
 834  
     /**
 835  
      * A specialized <code>BeanFactory</code> implementation that handles
 836  
      * configuration declarations. This class will retrieve the correct
 837  
      * configuration provider and delegate the task of creating the
 838  
      * configuration to this object.
 839  
      */
 840  2
     static class ConfigurationBeanFactory implements BeanFactory
 841  
     {
 842  
         /**
 843  
          * Creates an instance of a bean class. This implementation expects that
 844  
          * the passed in bean declaration is a declaration for a configuration.
 845  
          * It will determine the responsible configuration provider and delegate
 846  
          * the call to this instance. If creation of the configuration fails
 847  
          * and the <code>optional</code> attribute is set, the exception will
 848  
          * be ignored and <b>null</b> will be returned.
 849  
          *
 850  
          * @param beanClass the bean class (will be ignored)
 851  
          * @param data the declaration
 852  
          * @param param an additional parameter (will be ignored)
 853  
          * @return the newly created configuration
 854  
          * @throws Exception if an error occurs
 855  
          */
 856  
         public Object createBean(Class beanClass, BeanDeclaration data,
 857  
                 Object param) throws Exception
 858  
         {
 859  59
             ConfigurationDeclaration decl = (ConfigurationDeclaration) data;
 860  59
             String tagName = decl.getNode().getName();
 861  59
             ConfigurationProvider provider = decl.getConfigurationBuilder()
 862  
                     .providerForTag(tagName);
 863  59
             if (provider == null)
 864  
             {
 865  1
                 throw new ConfigurationRuntimeException(
 866  
                         "No ConfigurationProvider registered for tag "
 867  
                                 + tagName);
 868  
             }
 869  
 
 870  
             try
 871  
             {
 872  58
                 return provider.getConfiguration(decl);
 873  
             }
 874  
             catch (Exception ex)
 875  
             {
 876  
                 // If this is an optional configuration, ignore the exception
 877  9
                 if (!decl.isOptional())
 878  
                 {
 879  1
                     throw ex;
 880  
                 }
 881  
                 else
 882  
                 {
 883  8
                     return null;
 884  
                 }
 885  
             }
 886  
         }
 887  
 
 888  
         /**
 889  
          * Returns the default class for this bean factory.
 890  
          *
 891  
          * @return the default class
 892  
          */
 893  
         public Class getDefaultBeanClass()
 894  
         {
 895  
             // Here some valid class must be returned, otherwise BeanHelper
 896  
             // will complain that the bean's class cannot be determined
 897  59
             return Configuration.class;
 898  
         }
 899  
     }
 900  
 
 901  
     /**
 902  
      * A specialized provider implementation that deals with file based
 903  
      * configurations. Ensures that the base path is correctly set and that the
 904  
      * load() method gets called.
 905  
      */
 906  
     public static class FileConfigurationProvider extends ConfigurationProvider
 907  
     {
 908  
         /**
 909  
          * Creates a new instance of <code>FileConfigurationProvider</code>.
 910  
          */
 911  
         public FileConfigurationProvider()
 912  
         {
 913  4
             super();
 914  4
         }
 915  
 
 916  
         /**
 917  
          * Creates a new instance of <code>FileConfigurationProvider</code>
 918  
          * and sets the configuration class.
 919  
          *
 920  
          * @param configClass the class for the configurations to be created
 921  
          */
 922  
         public FileConfigurationProvider(Class configClass)
 923  
         {
 924  2
             super(configClass);
 925  2
         }
 926  
 
 927  
         /**
 928  
          * Creates the configuration. After that <code>load()</code> will be
 929  
          * called. If this configuration is marked as optional, exceptions will
 930  
          * be ignored.
 931  
          *
 932  
          * @param decl the declaration
 933  
          * @return the new configuration
 934  
          * @throws Exception if an error occurs
 935  
          */
 936  
         public AbstractConfiguration getConfiguration(
 937  
                 ConfigurationDeclaration decl) throws Exception
 938  
         {
 939  50
             FileConfiguration config = (FileConfiguration) super
 940  
                     .getConfiguration(decl);
 941  50
             config.load();
 942  42
             return (AbstractConfiguration) config;
 943  
         }
 944  
 
 945  
         /**
 946  
          * Initializes the bean instance. Ensures that the file configuration's
 947  
          * base path will be initialized with the base path of the factory so
 948  
          * that relative path names can be correctly resolved.
 949  
          *
 950  
          * @param bean the bean to be initialized
 951  
          * @param data the declaration
 952  
          * @throws Exception if an error occurs
 953  
          */
 954  
         protected void initBeanInstance(Object bean, BeanDeclaration data)
 955  
                 throws Exception
 956  
         {
 957  50
             FileConfiguration config = (FileConfiguration) bean;
 958  50
             config.setBasePath(((ConfigurationDeclaration) data)
 959  
                     .getConfigurationBuilder().getConfigurationBasePath());
 960  50
             super.initBeanInstance(bean, data);
 961  50
         }
 962  
     }
 963  
 
 964  
     /**
 965  
      * A specialized configuration provider for file based configurations that
 966  
      * can handle configuration sources whose concrete type depends on the
 967  
      * extension of the file to be loaded. One example is the
 968  
      * <code>properties</code> tag: if the file ends with ".xml" a
 969  
      * XMLPropertiesConfiguration object must be created, otherwise a
 970  
      * PropertiesConfiguration object.
 971  
      */
 972  2
     static class FileExtensionConfigurationProvider extends
 973  
             FileConfigurationProvider
 974  
     {
 975  
         /** Stores the class to be created when the file extension matches. */
 976  
         private Class matchingClass;
 977  
 
 978  
         /**
 979  
          * Stores the class to be created when the file extension does not
 980  
          * match.
 981  
          */
 982  
         private Class defaultClass;
 983  
 
 984  
         /** Stores the file extension to be checked against. */
 985  
         private String fileExtension;
 986  
 
 987  
         /**
 988  
          * Creates a new instance of
 989  
          * <code>FileExtensionConfigurationProvider</code> and initializes it.
 990  
          *
 991  
          * @param matchingClass the class to be created when the file extension
 992  
          * matches
 993  
          * @param defaultClass the class to be created when the file extension
 994  
          * does not match
 995  
          * @param extension the file extension to be checked agains
 996  
          */
 997  
         public FileExtensionConfigurationProvider(Class matchingClass,
 998  
                 Class defaultClass, String extension)
 999  4
         {
 1000  4
             this.matchingClass = matchingClass;
 1001  4
             this.defaultClass = defaultClass;
 1002  4
             fileExtension = extension;
 1003  4
         }
 1004  
 
 1005  
         /**
 1006  
          * Creates the configuration object. The class is determined by the file
 1007  
          * name's extension.
 1008  
          *
 1009  
          * @param beanClass the class
 1010  
          * @param data the bean declaration
 1011  
          * @return the new bean
 1012  
          * @throws Exception if an error occurs
 1013  
          */
 1014  
         protected Object createBeanInstance(Class beanClass,
 1015  
                 BeanDeclaration data) throws Exception
 1016  
         {
 1017  26
             String fileName = ((ConfigurationDeclaration) data)
 1018  
                     .getConfiguration().getString(ATTR_FILENAME);
 1019  26
             if (fileName != null
 1020  
                     && fileName.toLowerCase().trim().endsWith(fileExtension))
 1021  
             {
 1022  6
                 return super.createBeanInstance(matchingClass, data);
 1023  
             }
 1024  
             else
 1025  
             {
 1026  20
                 return super.createBeanInstance(defaultClass, data);
 1027  
             }
 1028  
         }
 1029  
     }
 1030  
 
 1031  
     /**
 1032  
      * A specialized configuration provider class that allows to include other
 1033  
      * configuration definition files.
 1034  
      */
 1035  
     static class ConfigurationBuilderProvider extends ConfigurationProvider
 1036  
     {
 1037  
         /**
 1038  
          * Creates a new instance of <code>ConfigurationBuilderProvider</code>.
 1039  
          */
 1040  
         public ConfigurationBuilderProvider()
 1041  
         {
 1042  2
             super(DefaultConfigurationBuilder.class);
 1043  2
         }
 1044  
 
 1045  
         /**
 1046  
          * Creates the configuration. First creates a configuration builder
 1047  
          * object. Then returns the configuration created by this builder.
 1048  
          *
 1049  
          * @param decl the configuration declaration
 1050  
          * @return the configuration
 1051  
          * @exception Exception if an error occurs
 1052  
          */
 1053  
         public AbstractConfiguration getConfiguration(
 1054  
                 ConfigurationDeclaration decl) throws Exception
 1055  
         {
 1056  2
             DefaultConfigurationBuilder builder = (DefaultConfigurationBuilder) super
 1057  
                     .getConfiguration(decl);
 1058  2
             return builder.getConfiguration(true);
 1059  
         }
 1060  
     }
 1061  
 
 1062  
     static
 1063  
     {
 1064  
         // register the configuration bean factory
 1065  2
         BeanHelper.registerBeanFactory(CONFIG_BEAN_FACTORY_NAME,
 1066  
                 new ConfigurationBeanFactory());
 1067  
     }
 1068  
 }