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.util.ArrayList;
    21  import java.util.Collection;
    22  import java.util.Iterator;
    23  import java.util.LinkedList;
    24  import java.util.List;
    25  import java.util.ListIterator;
    26
    27  /***
    28   * This Configuration class allows you to add multiple different types of Configuration
    29   * to this CompositeConfiguration.  If you add Configuration1, and then Configuration2,
    30   * any properties shared will mean that Configuration1 will be returned.
    31   * You can add multiple different types or the same type of properties file.
    32   * If Configuration1 doesn't have the property, then Configuration2 will be checked.
    33   *
    34   * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
    35   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
    36   * @version $Id: CompositeConfiguration.java 705028 2008-10-15 20:33:35Z oheger $
    37   */
    38  public class CompositeConfiguration extends AbstractConfiguration
    39  implements Cloneable
    40  {
    41      /*** List holding all the configuration */
    42      private List configList = new LinkedList();
    43
    44      /***
    45       * Configuration that holds in memory stuff.  Inserted as first so any
    46       * setProperty() override anything else added.
    47       */
    48      private Configuration inMemoryConfiguration;
    49
    50      /***
    51       * Creates an empty CompositeConfiguration object which can then
    52       * be added some other Configuration files
    53       */
    54      public CompositeConfiguration()
    55      {
    56          clear();
    57      }
    58
    59      /***
    60       * Creates a CompositeConfiguration object with a specified in memory
    61       * configuration. This configuration will store any changes made to
    62       * the CompositeConfiguration.
    63       *
    64       * @param inMemoryConfiguration the in memory configuration to use
    65       */
    66      public CompositeConfiguration(Configuration inMemoryConfiguration)
    67      {
    68          configList.clear();
    69          this.inMemoryConfiguration = inMemoryConfiguration;
    70          configList.add(inMemoryConfiguration);
    71      }
    72
    73      /***
    74       * Create a CompositeConfiguration with an empty in memory configuration
    75       * and adds the collection of configurations specified.
    76       *
    77       * @param configurations the collection of configurations to add
    78       */
    79      public CompositeConfiguration(Collection configurations)
    80      {
    81          this(new BaseConfiguration(), configurations);
    82      }
    83
    84      /***
    85       * Creates a CompositeConfiguration with a specified in memory
    86       * configuration, and then adds the given collection of configurations.
    87       *
    88       * @param inMemoryConfiguration the in memory configuration to use
    89       * @param configurations        the collection of configurations to add
    90       */
    91      public CompositeConfiguration(Configuration inMemoryConfiguration, Collection configurations)
    92      {
    93          this(inMemoryConfiguration);
    94
    95          if (configurations != null)
    96          {
    97              Iterator it = configurations.iterator();
    98              while (it.hasNext())
    99              {
    100                 addConfiguration((Configuration) it.next());
    101             }
    102         }
    103     }
    104
    105     /***
    106      * Add a configuration.
    107      *
    108      * @param config the configuration to add
    109      */
    110     public void addConfiguration(Configuration config)
    111     {
    112         if (!configList.contains(config))
    113         {
    114             // As the inMemoryConfiguration contains all manually added keys,
    115             // we must make sure that it is always last. "Normal", non composed
    116             // configuration add their keys at the end of the configuration and
    117             // we want to mimic this behaviour.
    118             configList.add(configList.indexOf(inMemoryConfiguration), config);
    119
    120             if (config instanceof AbstractConfiguration)
    121             {
    122                 ((AbstractConfiguration) config).setThrowExceptionOnMissing(isThrowExceptionOnMissing());
    123             }
    124         }
    125     }
    126
    127     /***
    128      * Remove a configuration. The in memory configuration cannot be removed.
    129      *
    130      * @param config The configuration to remove
    131      */
    132     public void removeConfiguration(Configuration config)
    133     {
    134         // Make sure that you can't remove the inMemoryConfiguration from
    135         // the CompositeConfiguration object
    136         if (!config.equals(inMemoryConfiguration))
    137         {
    138             configList.remove(config);
    139         }
    140     }
    141
    142     /***
    143      * Return the number of configurations.
    144      *
    145      * @return the number of configuration
    146      */
    147     public int getNumberOfConfigurations()
    148     {
    149         return configList.size();
    150     }
    151
    152     /***
    153      * Remove all configuration reinitialize the in memory configuration.
    154      */
    155     public void clear()
    156     {
    157         configList.clear();
    158         // recreate the in memory configuration
    159         inMemoryConfiguration = new BaseConfiguration();
    160         ((BaseConfiguration) inMemoryConfiguration).setThrowExceptionOnMissing(isThrowExceptionOnMissing());
    161         ((BaseConfiguration) inMemoryConfiguration).setListDelimiter(getListDelimiter());
    162         ((BaseConfiguration) inMemoryConfiguration).setDelimiterParsingDisabled(isDelimiterParsingDisabled());
    163         configList.add(inMemoryConfiguration);
    164     }
    165
    166     /***
    167      * Add this property to the inmemory Configuration.
    168      *
    169      * @param key The Key to add the property to.
    170      * @param token The Value to add.
    171      */
    172     protected void addPropertyDirect(String key, Object token)
    173     {
    174         inMemoryConfiguration.addProperty(key, token);
    175     }
    176
    177     /***
    178      * Read property from underlying composite
    179      *
    180      * @param key key to use for mapping
    181      *
    182      * @return object associated with the given configuration key.
    183      */
    184     public Object getProperty(String key)
    185     {
    186         Configuration firstMatchingConfiguration = null;
    187         for (Iterator i = configList.iterator(); i.hasNext();)
    188         {
    189             Configuration config = (Configuration) i.next();
    190             if (config.containsKey(key))
    191             {
    192                 firstMatchingConfiguration = config;
    193                 break;
    194             }
    195         }
    196
    197         if (firstMatchingConfiguration != null)
    198         {
    199             return firstMatchingConfiguration.getProperty(key);
    200         }
    201         else
    202         {
    203             return null;
    204         }
    205     }
    206
    207     public Iterator getKeys()
    208     {
    209         List keys = new ArrayList();
    210         for (Iterator i = configList.iterator(); i.hasNext();)
    211         {
    212             Configuration config = (Configuration) i.next();
    213
    214             Iterator j = config.getKeys();
    215             while (j.hasNext())
    216             {
    217                 String key = (String) j.next();
    218                 if (!keys.contains(key))
    219                 {
    220                     keys.add(key);
    221                 }
    222             }
    223         }
    224
    225         return keys.iterator();
    226     }
    227
    228     public Iterator getKeys(String key)
    229     {
    230         List keys = new ArrayList();
    231         for (Iterator i = configList.iterator(); i.hasNext();)
    232         {
    233             Configuration config = (Configuration) i.next();
    234
    235             Iterator j = config.getKeys(key);
    236             while (j.hasNext())
    237             {
    238                 String newKey = (String) j.next();
    239                 if (!keys.contains(newKey))
    240                 {
    241                     keys.add(newKey);
    242                 }
    243             }
    244         }
    245
    246         return keys.iterator();
    247     }
    248
    249     public boolean isEmpty()
    250     {
    251         boolean isEmpty = true;
    252         for (Iterator i = configList.iterator(); i.hasNext();)
    253         {
    254             Configuration config = (Configuration) i.next();
    255             if (!config.isEmpty())
    256             {
    257                 return false;
    258             }
    259         }
    260
    261         return isEmpty;
    262     }
    263
    264     protected void clearPropertyDirect(String key)
    265     {
    266         for (Iterator i = configList.iterator(); i.hasNext();)
    267         {
    268             Configuration config = (Configuration) i.next();
    269             config.clearProperty(key);
    270         }
    271     }
    272
    273     public boolean containsKey(String key)
    274     {
    275         for (Iterator i = configList.iterator(); i.hasNext();)
    276         {
    277             Configuration config = (Configuration) i.next();
    278             if (config.containsKey(key))
    279             {
    280                 return true;
    281             }
    282         }
    283         return false;
    284     }
    285
    286     public List getList(String key, List defaultValue)
    287     {
    288         List list = new ArrayList();
    289
    290         // add all elements from the first configuration containing the requested key
    291         Iterator it = configList.iterator();
    292         while (it.hasNext() && list.isEmpty())
    293         {
    294             Configuration config = (Configuration) it.next();
    295             if (config != inMemoryConfiguration && config.containsKey(key))
    296             {
    297                 appendListProperty(list, config, key);
    298             }
    299         }
    300
    301         // add all elements from the in memory configuration
    302         appendListProperty(list, inMemoryConfiguration, key);
    303
    304         if (list.isEmpty())
    305         {
    306             return defaultValue;
    307         }
    308
    309         ListIterator lit = list.listIterator();
    310         while (lit.hasNext())
    311         {
    312             lit.set(interpolate(lit.next()));
    313         }
    314
    315         return list;
    316     }
    317
    318     public String[] getStringArray(String key)
    319     {
    320         List list = getList(key);
    321
    322         // transform property values into strings
    323         String[] tokens = new String[list.size()];
    324
    325         for (int i = 0; i < tokens.length; i++)
    326         {
    327             tokens[i] = String.valueOf(list.get(i));
    328         }
    329
    330         return tokens;
    331     }
    332
    333     /***
    334      * Return the configuration at the specified index.
    335      *
    336      * @param index The index of the configuration to retrieve
    337      * @return the configuration at this index
    338      */
    339     public Configuration getConfiguration(int index)
    340     {
    341         return (Configuration) configList.get(index);
    342     }
    343
    344     /***
    345      * Returns the &quot;in memory configuration&quot;. In this configuration
    346      * changes are stored.
    347      *
    348      * @return the in memory configuration
    349      */
    350     public Configuration getInMemoryConfiguration()
    351     {
    352         return inMemoryConfiguration;
    353     }
    354
    355     /***
    356      * Returns a copy of this object. This implementation will create a deep
    357      * clone, i.e. all configurations contained in this composite will also be
    358      * cloned. This only works if all contained configurations support cloning;
    359      * otherwise a runtime exception will be thrown. Registered event handlers
    360      * won't get cloned.
    361      *
    362      * @return the copy
    363      * @since 1.3
    364      */
    365     public Object clone()
    366     {
    367         try
    368         {
    369             CompositeConfiguration copy = (CompositeConfiguration) super
    370                     .clone();
    371             copy.clearConfigurationListeners();
    372             copy.configList = new LinkedList();
    373             copy.inMemoryConfiguration = ConfigurationUtils
    374                     .cloneConfiguration(getInMemoryConfiguration());
    375             copy.configList.add(copy.inMemoryConfiguration);
    376
    377             for (int i = 0; i < getNumberOfConfigurations(); i++)
    378             {
    379                 Configuration config = getConfiguration(i);
    380                 if (config != getInMemoryConfiguration())
    381                 {
    382                     copy.addConfiguration(ConfigurationUtils
    383                             .cloneConfiguration(config));
    384                 }
    385             }
    386
    387             return copy;
    388         }
    389         catch (CloneNotSupportedException cnex)
    390         {
    391             // cannot happen
    392             throw new ConfigurationRuntimeException(cnex);
    393         }
    394     }
    395
    396     /***
    397      * Sets a flag whether added values for string properties should be checked
    398      * for the list delimiter. This implementation ensures that the in memory
    399      * configuration is correctly initialized.
    400      *
    401      * @param delimiterParsingDisabled the new value of the flag
    402      * @since 1.4
    403      */
    404     public void setDelimiterParsingDisabled(boolean delimiterParsingDisabled)
    405     {
    406         ((BaseConfiguration) getInMemoryConfiguration())
    407                 .setDelimiterParsingDisabled(delimiterParsingDisabled);
    408         super.setDelimiterParsingDisabled(delimiterParsingDisabled);
    409     }
    410
    411     /***
    412      * Sets the character that is used as list delimiter. This implementation
    413      * ensures that the in memory configuration is correctly initialized.
    414      *
    415      * @param listDelimiter the new list delimiter character
    416      * @since 1.4
    417      */
    418     public void setListDelimiter(char listDelimiter)
    419     {
    420         ((BaseConfiguration) getInMemoryConfiguration())
    421                 .setListDelimiter(listDelimiter);
    422         super.setListDelimiter(listDelimiter);
    423     }
    424
    425     /***
    426      * Returns the configuration source, in which the specified key is defined.
    427      * This method will iterate over all existing child configurations and check
    428      * whether they contain the specified key. The following constellations are
    429      * possible:
    430      * <ul>
    431      * <li>If exactly one child configuration contains the key, this
    432      * configuration is returned as the source configuration. This may be the
    433      * <em>in memory configuration</em> (this has to be explicitly checked by
    434      * the calling application).</li>
    435      * <li>If none of the child configurations contain the key, <b>null</b> is
    436      * returned.</li>
    437      * <li>If the key is contained in multiple child configurations or if the
    438      * key is <b>null</b>, a <code>IllegalArgumentException</code> is thrown.
    439      * In this case the source configuration cannot be determined.</li>
    440      * </ul>
    441      *
    442      * @param key the key to be checked
    443      * @return the source configuration of this key
    444      * @throws IllegalArgumentException if the source configuration cannot be
    445      * determined
    446      * @since 1.5
    447      */
    448     public Configuration getSource(String key)
    449     {
    450         if (key == null)
    451         {
    452             throw new IllegalArgumentException("Key must not be null!");
    453         }
    454
    455         Configuration source = null;
    456         for (Iterator it = configList.iterator(); it.hasNext();)
    457         {
    458             Configuration conf = (Configuration) it.next();
    459             if (conf.containsKey(key))
    460             {
    461                 if (source != null)
    462                 {
    463                     throw new IllegalArgumentException("The key " + key
    464                             + " is defined by multiple sources!");
    465                 }
    466                 source = conf;
    467             }
    468         }
    469
    470         return source;
    471     }
    472
    473     /***
    474      * Adds the value of a property to the given list. This method is used by
    475      * <code>getList()</code> for gathering property values from the child
    476      * configurations.
    477      *
    478      * @param dest the list for collecting the data
    479      * @param config the configuration to query
    480      * @param key the key of the property
    481      */
    482     private static void appendListProperty(List dest, Configuration config,
    483             String key)
    484     {
    485         Object value = config.getProperty(key);
    486         if (value != null)
    487         {
    488             if (value instanceof Collection)
    489             {
    490                 dest.addAll((Collection) value);
    491             }
    492             else
    493             {
    494                 dest.add(value);
    495             }
    496         }
    497     }
    498 }