Coverage Report - org.apache.commons.configuration.CompositeConfiguration
 
Classes in this File Line Coverage Branch Coverage Complexity
CompositeConfiguration
98%
128/131
100%
32/32
2,708
 
 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  127
     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  122
     {
 56  122
         clear();
 57  122
     }
 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  5
     {
 68  5
         configList.clear();
 69  5
         this.inMemoryConfiguration = inMemoryConfiguration;
 70  5
         configList.add(inMemoryConfiguration);
 71  5
     }
 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  1
         this(new BaseConfiguration(), configurations);
 82  1
     }
 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  1
         this(inMemoryConfiguration);
 94  
 
 95  1
         if (configurations != null)
 96  
         {
 97  1
             Iterator it = configurations.iterator();
 98  4
             while (it.hasNext())
 99  
             {
 100  3
                 addConfiguration((Configuration) it.next());
 101  
             }
 102  
         }
 103  1
     }
 104  
 
 105  
     /**
 106  
      * Add a configuration.
 107  
      *
 108  
      * @param config the configuration to add
 109  
      */
 110  
     public void addConfiguration(Configuration config)
 111  
     {
 112  168
         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  166
             configList.add(configList.indexOf(inMemoryConfiguration), config);
 119  
 
 120  166
             if (config instanceof AbstractConfiguration)
 121  
             {
 122  166
                 ((AbstractConfiguration) config).setThrowExceptionOnMissing(isThrowExceptionOnMissing());
 123  
             }
 124  
         }
 125  168
     }
 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  4
         if (!config.equals(inMemoryConfiguration))
 137  
         {
 138  2
             configList.remove(config);
 139  
         }
 140  4
     }
 141  
 
 142  
     /**
 143  
      * Return the number of configurations.
 144  
      *
 145  
      * @return the number of configuration
 146  
      */
 147  
     public int getNumberOfConfigurations()
 148  
     {
 149  31
         return configList.size();
 150  
     }
 151  
 
 152  
     /**
 153  
      * Remove all configuration reinitialize the in memory configuration.
 154  
      */
 155  
     public void clear()
 156  
     {
 157  132
         configList.clear();
 158  
         // recreate the in memory configuration
 159  132
         inMemoryConfiguration = new BaseConfiguration();
 160  132
         ((BaseConfiguration) inMemoryConfiguration).setThrowExceptionOnMissing(isThrowExceptionOnMissing());
 161  132
         ((BaseConfiguration) inMemoryConfiguration).setListDelimiter(getListDelimiter());
 162  132
         ((BaseConfiguration) inMemoryConfiguration).setDelimiterParsingDisabled(isDelimiterParsingDisabled());
 163  132
         configList.add(inMemoryConfiguration);
 164  132
     }
 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  35
         inMemoryConfiguration.addProperty(key, token);
 175  35
     }
 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  142
         Configuration firstMatchingConfiguration = null;
 187  142
         for (Iterator i = configList.iterator(); i.hasNext();)
 188  
         {
 189  212
             Configuration config = (Configuration) i.next();
 190  212
             if (config.containsKey(key))
 191  
             {
 192  117
                 firstMatchingConfiguration = config;
 193  117
                 break;
 194  
             }
 195  
         }
 196  
 
 197  142
         if (firstMatchingConfiguration != null)
 198  
         {
 199  117
             return firstMatchingConfiguration.getProperty(key);
 200  
         }
 201  
         else
 202  
         {
 203  25
             return null;
 204  
         }
 205  
     }
 206  
 
 207  
     public Iterator getKeys()
 208  
     {
 209  14
         List keys = new ArrayList();
 210  14
         for (Iterator i = configList.iterator(); i.hasNext();)
 211  
         {
 212  24
             Configuration config = (Configuration) i.next();
 213  
 
 214  24
             Iterator j = config.getKeys();
 215  154
             while (j.hasNext())
 216  
             {
 217  130
                 String key = (String) j.next();
 218  130
                 if (!keys.contains(key))
 219  
                 {
 220  129
                     keys.add(key);
 221  
                 }
 222  
             }
 223  
         }
 224  
 
 225  14
         return keys.iterator();
 226  
     }
 227  
 
 228  
     public Iterator getKeys(String key)
 229  
     {
 230  9
         List keys = new ArrayList();
 231  9
         for (Iterator i = configList.iterator(); i.hasNext();)
 232  
         {
 233  22
             Configuration config = (Configuration) i.next();
 234  
 
 235  22
             Iterator j = config.getKeys(key);
 236  218
             while (j.hasNext())
 237  
             {
 238  196
                 String newKey = (String) j.next();
 239  196
                 if (!keys.contains(newKey))
 240  
                 {
 241  194
                     keys.add(newKey);
 242  
                 }
 243  
             }
 244  
         }
 245  
 
 246  9
         return keys.iterator();
 247  
     }
 248  
 
 249  
     public boolean isEmpty()
 250  
     {
 251  4
         boolean isEmpty = true;
 252  4
         for (Iterator i = configList.iterator(); i.hasNext();)
 253  
         {
 254  4
             Configuration config = (Configuration) i.next();
 255  4
             if (!config.isEmpty())
 256  
             {
 257  4
                 return false;
 258  
             }
 259  
         }
 260  
 
 261  0
         return isEmpty;
 262  
     }
 263  
 
 264  
     protected void clearPropertyDirect(String key)
 265  
     {
 266  16
         for (Iterator i = configList.iterator(); i.hasNext();)
 267  
         {
 268  35
             Configuration config = (Configuration) i.next();
 269  35
             config.clearProperty(key);
 270  
         }
 271  16
     }
 272  
 
 273  
     public boolean containsKey(String key)
 274  
     {
 275  55
         for (Iterator i = configList.iterator(); i.hasNext();)
 276  
         {
 277  72
             Configuration config = (Configuration) i.next();
 278  72
             if (config.containsKey(key))
 279  
             {
 280  44
                 return true;
 281  
             }
 282  
         }
 283  11
         return false;
 284  
     }
 285  
 
 286  
     public List getList(String key, List defaultValue)
 287  
     {
 288  48
         List list = new ArrayList();
 289  
 
 290  
         // add all elements from the first configuration containing the requested key
 291  48
         Iterator it = configList.iterator();
 292  113
         while (it.hasNext() && list.isEmpty())
 293  
         {
 294  65
             Configuration config = (Configuration) it.next();
 295  65
             if (config != inMemoryConfiguration && config.containsKey(key))
 296  
             {
 297  34
                 appendListProperty(list, config, key);
 298  
             }
 299  
         }
 300  
 
 301  
         // add all elements from the in memory configuration
 302  48
         appendListProperty(list, inMemoryConfiguration, key);
 303  
 
 304  48
         if (list.isEmpty())
 305  
         {
 306  5
             return defaultValue;
 307  
         }
 308  
 
 309  43
         ListIterator lit = list.listIterator();
 310  124
         while (lit.hasNext())
 311  
         {
 312  81
             lit.set(interpolate(lit.next()));
 313  
         }
 314  
 
 315  43
         return list;
 316  
     }
 317  
 
 318  
     public String[] getStringArray(String key)
 319  
     {
 320  14
         List list = getList(key);
 321  
 
 322  
         // transform property values into strings
 323  14
         String[] tokens = new String[list.size()];
 324  
 
 325  35
         for (int i = 0; i < tokens.length; i++)
 326  
         {
 327  21
             tokens[i] = String.valueOf(list.get(i));
 328  
         }
 329  
 
 330  14
         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  29
         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  12
         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  3
             CompositeConfiguration copy = (CompositeConfiguration) super
 370  
                     .clone();
 371  3
             copy.clearConfigurationListeners();
 372  3
             copy.configList = new LinkedList();
 373  3
             copy.inMemoryConfiguration = ConfigurationUtils
 374  
                     .cloneConfiguration(getInMemoryConfiguration());
 375  3
             copy.configList.add(copy.inMemoryConfiguration);
 376  
 
 377  5
             for (int i = 0; i < getNumberOfConfigurations(); i++)
 378  
             {
 379  3
                 Configuration config = getConfiguration(i);
 380  3
                 if (config != getInMemoryConfiguration())
 381  
                 {
 382  1
                     copy.addConfiguration(ConfigurationUtils
 383  
                             .cloneConfiguration(config));
 384  
                 }
 385  
             }
 386  
 
 387  2
             return copy;
 388  
         }
 389  0
         catch (CloneNotSupportedException cnex)
 390  
         {
 391  
             // cannot happen
 392  0
             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  2
         ((BaseConfiguration) getInMemoryConfiguration())
 407  
                 .setDelimiterParsingDisabled(delimiterParsingDisabled);
 408  2
         super.setDelimiterParsingDisabled(delimiterParsingDisabled);
 409  2
     }
 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  2
         ((BaseConfiguration) getInMemoryConfiguration())
 421  
                 .setListDelimiter(listDelimiter);
 422  2
         super.setListDelimiter(listDelimiter);
 423  2
     }
 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  5
         if (key == null)
 451  
         {
 452  1
             throw new IllegalArgumentException("Key must not be null!");
 453  
         }
 454  
 
 455  4
         Configuration source = null;
 456  4
         for (Iterator it = configList.iterator(); it.hasNext();)
 457  
         {
 458  12
             Configuration conf = (Configuration) it.next();
 459  12
             if (conf.containsKey(key))
 460  
             {
 461  4
                 if (source != null)
 462  
                 {
 463  1
                     throw new IllegalArgumentException("The key " + key
 464  
                             + " is defined by multiple sources!");
 465  
                 }
 466  3
                 source = conf;
 467  
             }
 468  
         }
 469  
 
 470  3
         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  82
         Object value = config.getProperty(key);
 486  82
         if (value != null)
 487  
         {
 488  50
             if (value instanceof Collection)
 489  
             {
 490  22
                 dest.addAll((Collection) value);
 491  
             }
 492  
             else
 493  
             {
 494  28
                 dest.add(value);
 495  
             }
 496  
         }
 497  82
     }
 498  
 }