View Javadoc

    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
    18  package org.apache.commons.configuration;
    19
    20  import java.io.File;
    21  import java.io.IOException;
    22  import java.io.InputStream;
    23  import java.net.URL;
    24  import java.util.Collection;
    25  import java.util.Iterator;
    26  import java.util.LinkedList;
    27  import java.util.Map;
    28
    29  import org.apache.commons.configuration.plist.PropertyListConfiguration;
    30  import org.apache.commons.configuration.plist.XMLPropertyListConfiguration;
    31  import org.apache.commons.digester.AbstractObjectCreationFactory;
    32  import org.apache.commons.digester.Digester;
    33  import org.apache.commons.digester.ObjectCreationFactory;
    34  import org.apache.commons.digester.Substitutor;
    35  import org.apache.commons.digester.substitution.MultiVariableExpander;
    36  import org.apache.commons.digester.substitution.VariableSubstitutor;
    37  import org.apache.commons.digester.xmlrules.DigesterLoader;
    38  import org.apache.commons.lang.StringUtils;
    39  import org.apache.commons.logging.Log;
    40  import org.apache.commons.logging.LogFactory;
    41  import org.xml.sax.Attributes;
    42  import org.xml.sax.SAXException;
    43
    44  /***
    45   * Factory class to create a CompositeConfiguration from a .xml file using
    46   * Digester.  By default it can handle the Configurations from commons-
    47   * configuration.  If you need to add your own, then you can pass in your own
    48   * digester rules to use.  It is also namespace aware, by providing a
    49   * digesterRuleNamespaceURI.
    50   *
    51   * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
    52   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
    53   * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a>
    54   * @version $Id: ConfigurationFactory.java 439648 2006-09-02 20:42:10Z oheger $
    55   */
    56  public class ConfigurationFactory
    57  {
    58      /*** Constant for the root element in the info file.*/
    59      private static final String SEC_ROOT = "configuration/";
    60
    61      /*** Constant for the override section.*/
    62      private static final String SEC_OVERRIDE = SEC_ROOT + "override/";
    63
    64      /*** Constant for the additional section.*/
    65      private static final String SEC_ADDITIONAL = SEC_ROOT + "additional/";
    66
    67      /*** Constant for the optional attribute.*/
    68      private static final String ATTR_OPTIONAL = "optional";
    69
    70      /*** Constant for the fileName attribute.*/
    71      private static final String ATTR_FILENAME = "fileName";
    72
    73      /*** Constant for the default base path (points to actual directory).*/
    74      private static final String DEF_BASE_PATH = ".";
    75
    76      /*** static logger */
    77      private static Log log = LogFactory.getLog(ConfigurationFactory.class);
    78
    79      /*** The XML file with the details about the configuration to load */
    80      private String configurationFileName;
    81
    82      /*** The URL to the XML file with the details about the configuration to load. */
    83      private URL configurationURL;
    84
    85      /***
    86       * The implicit base path for included files. This path is determined by
    87       * the configuration to load and used unless no other base path was
    88       * explicitely specified.
    89       */
    90      private String implicitBasePath;
    91
    92      /*** The basePath to prefix file paths for file based property files. */
    93      private String basePath;
    94
    95      /*** URL for xml digester rules file */
    96      private URL digesterRules;
    97
    98      /*** The digester namespace to parse */
    99      private String digesterRuleNamespaceURI;
    100
    101     /***
    102      * Constructor
    103      */
    104     public ConfigurationFactory()
    105     {
    106         setBasePath(DEF_BASE_PATH);
    107     }
    108     /***
    109      * Constructor with ConfigurationFile Name passed
    110      *
    111      * @param configurationFileName The path to the configuration file
    112      */
    113     public ConfigurationFactory(String configurationFileName)
    114     {
    115         setConfigurationFileName(configurationFileName);
    116     }
    117
    118     /***
    119      * Return the configuration provided by this factory. It loads the
    120      * configuration file which is a XML description of the actual
    121      * configurations to load. It can contain various different types of
    122      * configuration, e.g. Properties, XML and JNDI.
    123      *
    124      * @return A Configuration object
    125      * @throws ConfigurationException A generic exception that we had trouble during the
    126      * loading of the configuration data.
    127      */
    128     public Configuration getConfiguration() throws ConfigurationException
    129     {
    130         Digester digester;
    131         InputStream input = null;
    132         ConfigurationBuilder builder = new ConfigurationBuilder();
    133         URL url = getConfigurationURL();
    134         try
    135         {
    136             if (url == null)
    137             {
    138                 url = ConfigurationUtils.locate(implicitBasePath, getConfigurationFileName());
    139             }
    140             input = url.openStream();
    141         }
    142         catch (Exception e)
    143         {
    144             log.error("Exception caught opening stream to URL", e);
    145             throw new ConfigurationException("Exception caught opening stream to URL", e);
    146         }
    147
    148         if (getDigesterRules() == null)
    149         {
    150             digester = new Digester();
    151             configureNamespace(digester);
    152             initDefaultDigesterRules(digester);
    153         }
    154         else
    155         {
    156             digester = DigesterLoader.createDigester(getDigesterRules());
    157             // This might already be too late. As far as I can see, the namespace
    158             // awareness must be configured before the digester rules are loaded.
    159             configureNamespace(digester);
    160         }
    161
    162         // Configure digester to always enable the context class loader
    163         digester.setUseContextClassLoader(true);
    164         // Add a substitutor to resolve system properties
    165         enableDigesterSubstitutor(digester);
    166         // Put the composite builder object below all of the other objects.
    167         digester.push(builder);
    168         // Parse the input stream to configure our mappings
    169         try
    170         {
    171             digester.parse(input);
    172             input.close();
    173         }
    174         catch (SAXException saxe)
    175         {
    176             log.error("SAX Exception caught", saxe);
    177             throw new ConfigurationException("SAX Exception caught", saxe);
    178         }
    179         catch (IOException ioe)
    180         {
    181             log.error("IO Exception caught", ioe);
    182             throw new ConfigurationException("IO Exception caught", ioe);
    183         }
    184         return builder.getConfiguration();
    185     }
    186
    187     /***
    188      * Returns the configurationFile.
    189      *
    190      * @return The name of the configuration file. Can be null.
    191      */
    192     public String getConfigurationFileName()
    193     {
    194         return configurationFileName;
    195     }
    196
    197     /***
    198      * Sets the configurationFile.
    199      *
    200      * @param configurationFileName  The name of the configurationFile to use.
    201      */
    202     public void setConfigurationFileName(String configurationFileName)
    203     {
    204         File file = new File(configurationFileName).getAbsoluteFile();
    205         this.configurationFileName = file.getName();
    206         implicitBasePath = file.getParent();
    207     }
    208
    209     /***
    210      * Returns the URL of the configuration file to be loaded.
    211      *
    212      * @return the URL of the configuration to load
    213      */
    214     public URL getConfigurationURL()
    215     {
    216         return configurationURL;
    217     }
    218
    219     /***
    220      * Sets the URL of the configuration to load. This configuration can be
    221      * either specified by a file name or by a URL.
    222      *
    223      * @param url the URL of the configuration to load
    224      */
    225     public void setConfigurationURL(URL url)
    226     {
    227         configurationURL = url;
    228         implicitBasePath = url.toString();
    229     }
    230
    231     /***
    232      * Returns the digesterRules.
    233      *
    234      * @return URL
    235      */
    236     public URL getDigesterRules()
    237     {
    238         return digesterRules;
    239     }
    240
    241     /***
    242      * Sets the digesterRules.
    243      *
    244      * @param digesterRules The digesterRules to set
    245      */
    246     public void setDigesterRules(URL digesterRules)
    247     {
    248         this.digesterRules = digesterRules;
    249     }
    250
    251     /***
    252      * Adds a substitutor to interpolate system properties
    253      *
    254      * @param digester The digester to which we add the substitutor
    255      */
    256     protected void enableDigesterSubstitutor(Digester digester)
    257     {
    258         Map systemProperties = System.getProperties();
    259         MultiVariableExpander expander = new MultiVariableExpander();
    260         expander.addSource("$", systemProperties);
    261
    262         // allow expansion in both xml attributes and element text
    263         Substitutor substitutor = new VariableSubstitutor(expander);
    264         digester.setSubstitutor(substitutor);
    265     }
    266
    267     /***
    268      * Initializes the parsing rules for the default digester
    269      *
    270      * This allows the Configuration Factory to understand the default types:
    271      * Properties, XML and JNDI. Two special sections are introduced:
    272      * <code>&lt;override&gt;</code> and <code>&lt;additional&gt;</code>.
    273      *
    274      * @param digester The digester to configure
    275      */
    276     protected void initDefaultDigesterRules(Digester digester)
    277     {
    278         initDigesterSectionRules(digester, SEC_ROOT, false);
    279         initDigesterSectionRules(digester, SEC_OVERRIDE, false);
    280         initDigesterSectionRules(digester, SEC_ADDITIONAL, true);
    281     }
    282
    283     /***
    284      * Sets up digester rules for a specified section of the configuration
    285      * info file.
    286      *
    287      * @param digester the current digester instance
    288      * @param matchString specifies the section
    289      * @param additional a flag if rules for the additional section are to be
    290      * added
    291      */
    292     protected void initDigesterSectionRules(Digester digester, String matchString, boolean additional)
    293     {
    294         setupDigesterInstance(
    295             digester,
    296             matchString + "properties",
    297             new PropertiesConfigurationFactory(),
    298             null,
    299             additional);
    300
    301         setupDigesterInstance(
    302             digester,
    303             matchString + "plist",
    304             new PropertyListConfigurationFactory(),
    305             null,
    306             additional);
    307
    308         setupDigesterInstance(
    309             digester,
    310             matchString + "xml",
    311             new FileConfigurationFactory(XMLConfiguration.class),
    312             null,
    313             additional);
    314
    315         setupDigesterInstance(
    316             digester,
    317             matchString + "hierarchicalXml",
    318             new FileConfigurationFactory(XMLConfiguration.class),
    319             null,
    320             additional);
    321
    322         setupDigesterInstance(
    323             digester,
    324             matchString + "jndi",
    325             new JNDIConfigurationFactory(),
    326             null,
    327             additional);
    328
    329         setupDigesterInstance(
    330             digester,
    331             matchString + "system",
    332             new SystemConfigurationFactory(),
    333             null,
    334             additional);
    335     }
    336
    337     /***
    338      * Sets up digester rules for a configuration to be loaded.
    339      *
    340      * @param digester the current digester
    341      * @param matchString the pattern to match with this rule
    342      * @param factory an ObjectCreationFactory instance to use for creating new
    343      * objects
    344      * @param method the name of a method to be called or <b>null</b> for none
    345      * @param additional a flag if rules for the additional section are to be
    346      * added
    347      */
    348     protected void setupDigesterInstance(
    349             Digester digester,
    350             String matchString,
    351             ObjectCreationFactory factory,
    352             String method,
    353             boolean additional)
    354     {
    355         if (additional)
    356         {
    357             setupUnionRules(digester, matchString);
    358         }
    359
    360         digester.addFactoryCreate(matchString, factory);
    361         digester.addSetProperties(matchString);
    362
    363         if (method != null)
    364         {
    365             digester.addCallMethod(matchString, method);
    366         }
    367
    368         digester.addSetNext(matchString, "addConfiguration", Configuration.class.getName());
    369     }
    370
    371     /***
    372      * Sets up rules for configurations in the additional section.
    373      *
    374      * @param digester the current digester
    375      * @param matchString the pattern to match with this rule
    376      */
    377     protected void setupUnionRules(Digester digester, String matchString)
    378     {
    379         digester.addObjectCreate(matchString,
    380         AdditionalConfigurationData.class);
    381         digester.addSetProperties(matchString);
    382         digester.addSetNext(matchString, "addAdditionalConfig",
    383         AdditionalConfigurationData.class.getName());
    384     }
    385
    386     /***
    387      * Returns the digesterRuleNamespaceURI.
    388      *
    389      * @return A String with the digesterRuleNamespaceURI.
    390      */
    391     public String getDigesterRuleNamespaceURI()
    392     {
    393         return digesterRuleNamespaceURI;
    394     }
    395
    396     /***
    397      * Sets the digesterRuleNamespaceURI.
    398      *
    399      * @param digesterRuleNamespaceURI The new digesterRuleNamespaceURI to use
    400      */
    401     public void setDigesterRuleNamespaceURI(String digesterRuleNamespaceURI)
    402     {
    403         this.digesterRuleNamespaceURI = digesterRuleNamespaceURI;
    404     }
    405
    406     /***
    407      * Configure the current digester to be namespace aware and to have
    408      * a Configuration object to which all of the other configurations
    409      * should be added
    410      *
    411      * @param digester The Digester to configure
    412      */
    413     private void configureNamespace(Digester digester)
    414     {
    415         if (getDigesterRuleNamespaceURI() != null)
    416         {
    417             digester.setNamespaceAware(true);
    418             digester.setRuleNamespaceURI(getDigesterRuleNamespaceURI());
    419         }
    420         else
    421         {
    422             digester.setNamespaceAware(false);
    423         }
    424         digester.setValidating(false);
    425     }
    426
    427     /***
    428      * Returns the Base path from which this Configuration Factory operates.
    429      * This is never null. If you set the BasePath to null, then a base path
    430      * according to the configuration to load is returned.
    431      *
    432      * @return The base Path of this configuration factory.
    433      */
    434     public String getBasePath()
    435     {
    436         String path = StringUtils.isEmpty(basePath)
    437                 || DEF_BASE_PATH.equals(basePath) ? implicitBasePath : basePath;
    438         return StringUtils.isEmpty(path) ? DEF_BASE_PATH : path;
    439     }
    440
    441     /***
    442      * Sets the basePath for all file references from this Configuration Factory.
    443      * Normally a base path need not to be set because it is determined by
    444      * the location of the configuration file to load. All relative pathes in
    445      * this file are resolved relative to this file. Setting a base path makes
    446      * sense if such relative pathes should be otherwise resolved, e.g. if
    447      * the configuration file is loaded from the class path and all sub
    448      * configurations it refers to are stored in a special config directory.
    449      *
    450      * @param basePath The new basePath to set.
    451      */
    452     public void setBasePath(String basePath)
    453     {
    454         this.basePath = basePath;
    455     }
    456
    457     /***
    458      * A base class for digester factory classes. This base class maintains
    459      * a default class for the objects to be created.
    460      * There will be sub classes for specific configuration implementations.
    461      */
    462     public class DigesterConfigurationFactory extends AbstractObjectCreationFactory
    463     {
    464         /*** Actual class to use. */
    465         private Class clazz;
    466
    467         /***
    468          * Creates a new instance of <code>DigesterConfigurationFactory</code>.
    469          *
    470          * @param clazz the class which we should instantiate
    471          */
    472         public DigesterConfigurationFactory(Class clazz)
    473         {
    474             this.clazz = clazz;
    475         }
    476
    477         /***
    478          * Creates an instance of the specified class.
    479          *
    480          * @param attribs the attributes (ignored)
    481          * @return the new object
    482          * @throws Exception if object creation fails
    483          */
    484         public Object createObject(Attributes attribs) throws Exception
    485         {
    486             return clazz.newInstance();
    487         }
    488     }
    489
    490     /***
    491      * A tiny inner class that allows the Configuration Factory to
    492      * let the digester construct FileConfiguration objects
    493      * that already have the correct base Path set.
    494      *
    495      */
    496     public class FileConfigurationFactory extends DigesterConfigurationFactory
    497     {
    498         /***
    499          * C'tor
    500          *
    501          * @param clazz The class which we should instantiate.
    502          */
    503         public FileConfigurationFactory(Class clazz)
    504         {
    505             super(clazz);
    506         }
    507
    508         /***
    509          * Gets called by the digester.
    510          *
    511          * @param attributes the actual attributes
    512          * @return the new object
    513          * @throws Exception Couldn't instantiate the requested object.
    514          */
    515         public Object createObject(Attributes attributes) throws Exception
    516         {
    517             FileConfiguration conf = createConfiguration(attributes);
    518             conf.setBasePath(getBasePath());
    519             conf.setFileName(attributes.getValue(ATTR_FILENAME));
    520             try
    521             {
    522                 log.info("Trying to load configuration " + conf.getFileName());
    523                 conf.load();
    524             }
    525             catch (ConfigurationException cex)
    526             {
    527                 if (attributes.getValue(ATTR_OPTIONAL) != null
    528                         && PropertyConverter.toBoolean(attributes.getValue(ATTR_OPTIONAL)).booleanValue())
    529                 {
    530                     log.warn("Could not load optional configuration " + conf.getFileName());
    531                 }
    532                 else
    533                 {
    534                     throw cex;
    535                 }
    536             }
    537             return conf;
    538         }
    539
    540         /***
    541          * Creates the object, a <code>FileConfiguration</code>.
    542          *
    543          * @param attributes the actual attributes
    544          * @return the file configuration
    545          * @throws Exception if the object could not be created
    546          */
    547         protected FileConfiguration createConfiguration(Attributes attributes) throws Exception
    548         {
    549             return (FileConfiguration) super.createObject(attributes);
    550         }
    551     }
    552
    553     /***
    554      * A factory that returns an XMLPropertiesConfiguration for .xml files
    555      * and a PropertiesConfiguration for the others.
    556      *
    557      * @since 1.2
    558      */
    559     public class PropertiesConfigurationFactory extends FileConfigurationFactory
    560     {
    561         /***
    562          * Creates a new instance of <code>PropertiesConfigurationFactory</code>.
    563          */
    564         public PropertiesConfigurationFactory()
    565         {
    566             super(null);
    567         }
    568
    569         /***
    570          * Creates the new configuration object. Based on the file name
    571          * provided in the attributes either a <code>PropertiesConfiguration</code>
    572          * or a <code>XMLPropertiesConfiguration</code> object will be
    573          * returned.
    574          *
    575          * @param attributes the attributes
    576          * @return the new configuration object
    577          * @throws Exception if an error occurs
    578          */
    579         protected FileConfiguration createConfiguration(Attributes attributes) throws Exception
    580         {
    581             String filename = attributes.getValue(ATTR_FILENAME);
    582
    583             if (filename != null && filename.toLowerCase().trim().endsWith(".xml"))
    584             {
    585                 return new XMLPropertiesConfiguration();
    586             }
    587             else
    588             {
    589                 return new PropertiesConfiguration();
    590             }
    591         }
    592     }
    593
    594     /***
    595      * A factory that returns an XMLPropertyListConfiguration for .xml files
    596      * and a PropertyListConfiguration for the others.
    597      *
    598      * @since 1.2
    599      */
    600     public class PropertyListConfigurationFactory extends FileConfigurationFactory
    601     {
    602         /***
    603          * Creates a new instance of <code>PropertyListConfigurationFactory</code>.
    604          */
    605         public PropertyListConfigurationFactory()
    606         {
    607             super(null);
    608         }
    609
    610         /***
    611          * Creates the new configuration object. Based on the file name
    612          * provided in the attributes either a <code>XMLPropertyListConfiguration</code>
    613          * or a <code>PropertyListConfiguration</code> object will be
    614          * returned.
    615          *
    616          * @param attributes the attributes
    617          * @return the new configuration object
    618          * @throws Exception if an error occurs
    619          */
    620         protected FileConfiguration createConfiguration(Attributes attributes) throws Exception
    621         {
    622             String filename = attributes.getValue(ATTR_FILENAME);
    623
    624             if (filename != null && filename.toLowerCase().trim().endsWith(".xml"))
    625             {
    626                 return new XMLPropertyListConfiguration();
    627             }
    628             else
    629             {
    630                 return new PropertyListConfiguration();
    631             }
    632         }
    633     }
    634
    635     /***
    636      * A tiny inner class that allows the Configuration Factory to
    637      * let the digester construct JNDIConfiguration objects.
    638      */
    639     private class JNDIConfigurationFactory extends DigesterConfigurationFactory
    640     {
    641         /***
    642          * Creates a new instance of <code>JNDIConfigurationFactory</code>.
    643          */
    644         public JNDIConfigurationFactory()
    645         {
    646             super(JNDIConfiguration.class);
    647         }
    648     }
    649
    650     /***
    651      * A tiny inner class that allows the Configuration Factory to
    652      * let the digester construct SystemConfiguration objects.
    653      */
    654     private class SystemConfigurationFactory extends DigesterConfigurationFactory
    655     {
    656         /***
    657          * Creates a new instance of <code>SystemConfigurationFactory</code>.
    658          */
    659         public SystemConfigurationFactory()
    660         {
    661             super(SystemConfiguration.class);
    662         }
    663     }
    664
    665     /***
    666      * A simple data class that holds all information about a configuration
    667      * from the <code>&lt;additional&gt;</code> section.
    668      */
    669     public static class AdditionalConfigurationData
    670     {
    671         /*** Stores the configuration object.*/
    672         private Configuration configuration;
    673
    674         /*** Stores the location of this configuration in the global tree.*/
    675         private String at;
    676
    677         /***
    678          * Returns the value of the <code>at</code> attribute.
    679          *
    680          * @return the at attribute
    681          */
    682         public String getAt()
    683         {
    684             return at;
    685         }
    686
    687         /***
    688          * Sets the value of the <code>at</code> attribute.
    689          *
    690          * @param string the attribute value
    691          */
    692         public void setAt(String string)
    693         {
    694             at = string;
    695         }
    696
    697         /***
    698          * Returns the configuration object.
    699          *
    700          * @return the configuration
    701          */
    702         public Configuration getConfiguration()
    703         {
    704             return configuration;
    705         }
    706
    707         /***
    708          * Sets the configuration object. Note: Normally this method should be
    709          * named <code>setConfiguration()</code>, but the name
    710          * <code>addConfiguration()</code> is required by some of the digester
    711          * rules.
    712          *
    713          * @param config the configuration to set
    714          */
    715         public void addConfiguration(Configuration config)
    716         {
    717             configuration = config;
    718         }
    719     }
    720
    721     /***
    722      * An internally used helper class for constructing the composite
    723      * configuration object.
    724      */
    725     public static class ConfigurationBuilder
    726     {
    727         /*** Stores the composite configuration.*/
    728         private CompositeConfiguration config;
    729
    730         /*** Stores a collection with the configs from the additional section.*/
    731         private Collection additionalConfigs;
    732
    733         /***
    734          * Creates a new instance of <code>ConfigurationBuilder</code>.
    735          */
    736         public ConfigurationBuilder()
    737         {
    738             config = new CompositeConfiguration();
    739             additionalConfigs = new LinkedList();
    740         }
    741
    742         /***
    743          * Adds a new configuration to this object. This method is called by
    744          * Digester.
    745          *
    746          * @param conf the configuration to be added
    747          */
    748         public void addConfiguration(Configuration conf)
    749         {
    750             config.addConfiguration(conf);
    751         }
    752
    753         /***
    754          * Adds information about an additional configuration. This method is
    755          * called by Digester.
    756          *
    757          * @param data the data about the additional configuration
    758          */
    759         public void addAdditionalConfig(AdditionalConfigurationData data)
    760         {
    761             additionalConfigs.add(data);
    762         }
    763
    764         /***
    765          * Returns the final composite configuration.
    766          *
    767          * @return the final configuration object
    768          */
    769         public CompositeConfiguration getConfiguration()
    770         {
    771             if (!additionalConfigs.isEmpty())
    772             {
    773                 Configuration unionConfig = createAdditionalConfiguration(additionalConfigs);
    774                 if (unionConfig != null)
    775                 {
    776                     addConfiguration(unionConfig);
    777                 }
    778                 additionalConfigs.clear();
    779             }
    780
    781             return config;
    782         }
    783
    784         /***
    785          * Creates a configuration object with the union of all properties
    786          * defined in the <code>&lt;additional&gt;</code> section. This
    787          * implementation returns a <code>HierarchicalConfiguration</code>
    788          * object.
    789          *
    790          * @param configs a collection with
    791          * <code>AdditionalConfigurationData</code> objects
    792          * @return the union configuration (can be <b>null</b>)
    793          */
    794         protected Configuration createAdditionalConfiguration(Collection configs)
    795         {
    796             HierarchicalConfiguration result = new HierarchicalConfiguration();
    797
    798             for (Iterator it = configs.iterator(); it.hasNext();)
    799             {
    800                 AdditionalConfigurationData cdata =
    801                 (AdditionalConfigurationData) it.next();
    802                 result.addNodes(cdata.getAt(),
    803                 createRootNode(cdata).getChildren());
    804             }
    805
    806             return result.isEmpty() ? null : result;
    807         }
    808
    809         /***
    810          * Creates a configuration root node for the specified configuration.
    811          *
    812          * @param cdata the configuration data object
    813          * @return a root node for this configuration
    814          */
    815         private HierarchicalConfiguration.Node createRootNode(AdditionalConfigurationData cdata)
    816         {
    817             if (cdata.getConfiguration() instanceof HierarchicalConfiguration)
    818             {
    819                 // we can directly use this configuration's root node
    820                 return ((HierarchicalConfiguration) cdata.getConfiguration()).getRoot();
    821             }
    822             else
    823             {
    824                 // transform configuration to a hierarchical root node
    825                 HierarchicalConfiguration hc = new HierarchicalConfiguration();
    826                 ConfigurationUtils.copy(cdata.getConfiguration(), hc);
    827                 return hc.getRoot();
    828             }
    829         }
    830     }
    831 }