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