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