Coverage Report - org.apache.commons.configuration.CompositeConfiguration
 
Classes in this File Line Coverage Branch Coverage Complexity
CompositeConfiguration
97%
137/141
94%
70/74
2,769
 
 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.LinkedHashSet;
 24  
 import java.util.LinkedList;
 25  
 import java.util.List;
 26  
 import java.util.ListIterator;
 27  
 import java.util.Set;
 28  
 
 29  
 /**
 30  
  * <p>{@code CompositeConfiguration} allows you to add multiple {@code Configuration}
 31  
  * objects to an aggregated configuration. If you add Configuration1, and then Configuration2,
 32  
  * any properties shared will mean that the value defined by Configuration1
 33  
  * will be returned. If Configuration1 doesn't have the property, then
 34  
  * Configuration2 will be checked. You can add multiple different types or the
 35  
  * same type of properties file.</p>
 36  
  * <p>When querying properties the order in which child configurations have been
 37  
  * added is relevant. To deal with property updates, a so-called <em>in-memory
 38  
  * configuration</em> is used. Per default, such a configuration is created
 39  
  * automatically. All property writes target this special configuration. There
 40  
  * are constructors which allow you to provide a specific in-memory configuration.
 41  
  * If used that way, the in-memory configuration is always the last one in the
 42  
  * list of child configurations. This means that for query operations all other
 43  
  * configurations take precedence.</p>
 44  
  * <p>Alternatively it is possible to mark a child configuration as in-memory
 45  
  * configuration when it is added. In this case the treatment of the in-memory
 46  
  * configuration is slightly different: it remains in the list of child
 47  
  * configurations at the position it was added, i.e. its priority for property
 48  
  * queries can be defined by adding the child configurations in the correct
 49  
  * order.</p>
 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  
  * @version $Id: CompositeConfiguration.java 1534064 2013-10-21 08:44:33Z henning $
 54  
  */
 55  
 public class CompositeConfiguration extends AbstractConfiguration
 56  
 implements Cloneable
 57  
 {
 58  
     /** List holding all the configuration */
 59  134
     private List<Configuration> configList = new LinkedList<Configuration>();
 60  
 
 61  
     /**
 62  
      * Configuration that holds in memory stuff.  Inserted as first so any
 63  
      * setProperty() override anything else added.
 64  
      */
 65  
     private Configuration inMemoryConfiguration;
 66  
 
 67  
     /**
 68  
      * Stores a flag whether the current in-memory configuration is also a
 69  
      * child configuration.
 70  
      */
 71  
     private boolean inMemoryConfigIsChild;
 72  
 
 73  
     /**
 74  
      * Creates an empty CompositeConfiguration object which can then
 75  
      * be added some other Configuration files
 76  
      */
 77  
     public CompositeConfiguration()
 78  127
     {
 79  127
         clear();
 80  127
     }
 81  
 
 82  
     /**
 83  
      * Creates a CompositeConfiguration object with a specified <em>in-memory
 84  
      * configuration</em>. This configuration will store any changes made to the
 85  
      * {@code CompositeConfiguration}. Note: Use this constructor if you want to
 86  
      * set a special type of in-memory configuration. If you have a
 87  
      * configuration which should act as both a child configuration and as
 88  
      * in-memory configuration, use
 89  
      * {@link #addConfiguration(Configuration, boolean)} with a value of
 90  
      * <b>true</b> instead.
 91  
      *
 92  
      * @param inMemoryConfiguration the in memory configuration to use
 93  
      */
 94  
     public CompositeConfiguration(Configuration inMemoryConfiguration)
 95  7
     {
 96  7
         configList.clear();
 97  7
         this.inMemoryConfiguration = inMemoryConfiguration;
 98  7
         configList.add(inMemoryConfiguration);
 99  7
     }
 100  
 
 101  
     /**
 102  
      * Create a CompositeConfiguration with an empty in memory configuration
 103  
      * and adds the collection of configurations specified.
 104  
      *
 105  
      * @param configurations the collection of configurations to add
 106  
      */
 107  
     public CompositeConfiguration(Collection<? extends Configuration> configurations)
 108  
     {
 109  1
         this(new BaseConfiguration(), configurations);
 110  1
     }
 111  
 
 112  
     /**
 113  
      * Creates a CompositeConfiguration with a specified <em>in-memory
 114  
      * configuration</em>, and then adds the given collection of configurations.
 115  
      *
 116  
      * @param inMemoryConfiguration the in memory configuration to use
 117  
      * @param configurations        the collection of configurations to add
 118  
      * @see #CompositeConfiguration(Configuration)
 119  
      */
 120  
     public CompositeConfiguration(Configuration inMemoryConfiguration,
 121  
             Collection<? extends Configuration> configurations)
 122  
     {
 123  1
         this(inMemoryConfiguration);
 124  
 
 125  1
         if (configurations != null)
 126  
         {
 127  1
             for (Configuration c : configurations)
 128  
             {
 129  3
                 addConfiguration(c);
 130  3
             }
 131  
         }
 132  1
     }
 133  
 
 134  
     /**
 135  
      * Add a configuration.
 136  
      *
 137  
      * @param config the configuration to add
 138  
      */
 139  
     public void addConfiguration(Configuration config)
 140  
     {
 141  171
         addConfiguration(config, false);
 142  171
     }
 143  
 
 144  
     /**
 145  
      * Adds a child configuration and optionally makes it the <em>in-memory
 146  
      * configuration</em>. This means that all future property write operations
 147  
      * are executed on this configuration. Note that the current in-memory
 148  
      * configuration is replaced by the new one. If it was created automatically
 149  
      * or passed to the constructor, it is removed from the list of child
 150  
      * configurations! Otherwise, it stays in the list of child configurations
 151  
      * at its current position, but it passes its role as in-memory
 152  
      * configuration to the new one.
 153  
      *
 154  
      * @param config the configuration to be added
 155  
      * @param asInMemory <b>true</b> if this configuration becomes the new
 156  
      *        <em>in-memory</em> configuration, <b>false</b> otherwise
 157  
      * @since 1.8
 158  
      */
 159  
     public void addConfiguration(Configuration config, boolean asInMemory)
 160  
     {
 161  174
         if (!configList.contains(config))
 162  
         {
 163  172
             if (asInMemory)
 164  
             {
 165  3
                 replaceInMemoryConfiguration(config);
 166  3
                 inMemoryConfigIsChild = true;
 167  
             }
 168  
 
 169  172
             if (!inMemoryConfigIsChild)
 170  
             {
 171  
                 // As the inMemoryConfiguration contains all manually added
 172  
                 // keys, we must make sure that it is always last. "Normal", non
 173  
                 // composed configurations add their keys at the end of the
 174  
                 // configuration and we want to mimic this behavior.
 175  168
                 configList.add(configList.indexOf(inMemoryConfiguration),
 176  
                         config);
 177  
             }
 178  
             else
 179  
             {
 180  
                 // However, if the in-memory configuration is a regular child,
 181  
                 // only the order in which child configurations are added is
 182  
                 // relevant
 183  4
                 configList.add(config);
 184  
             }
 185  
 
 186  172
             if (config instanceof AbstractConfiguration)
 187  
             {
 188  172
                 ((AbstractConfiguration) config)
 189  
                         .setThrowExceptionOnMissing(isThrowExceptionOnMissing());
 190  
             }
 191  
         }
 192  174
     }
 193  
 
 194  
     /**
 195  
      * Remove a configuration. The in memory configuration cannot be removed.
 196  
      *
 197  
      * @param config The configuration to remove
 198  
      */
 199  
     public void removeConfiguration(Configuration config)
 200  
     {
 201  
         // Make sure that you can't remove the inMemoryConfiguration from
 202  
         // the CompositeConfiguration object
 203  4
         if (!config.equals(inMemoryConfiguration))
 204  
         {
 205  2
             configList.remove(config);
 206  
         }
 207  4
     }
 208  
 
 209  
     /**
 210  
      * Return the number of configurations.
 211  
      *
 212  
      * @return the number of configuration
 213  
      */
 214  
     public int getNumberOfConfigurations()
 215  
     {
 216  27
         return configList.size();
 217  
     }
 218  
 
 219  
     /**
 220  
      * Removes all child configurations and reinitializes the <em>in-memory
 221  
      * configuration</em>. <strong>Attention:</strong> A new in-memory
 222  
      * configuration is created; the old one is lost.
 223  
      */
 224  
     @Override
 225  
     public void clear()
 226  
     {
 227  137
         configList.clear();
 228  
         // recreate the in memory configuration
 229  137
         inMemoryConfiguration = new BaseConfiguration();
 230  137
         ((BaseConfiguration) inMemoryConfiguration).setThrowExceptionOnMissing(isThrowExceptionOnMissing());
 231  137
         ((BaseConfiguration) inMemoryConfiguration).setListDelimiter(getListDelimiter());
 232  137
         ((BaseConfiguration) inMemoryConfiguration).setDelimiterParsingDisabled(isDelimiterParsingDisabled());
 233  137
         configList.add(inMemoryConfiguration);
 234  137
         inMemoryConfigIsChild = false;
 235  137
     }
 236  
 
 237  
     /**
 238  
      * Add this property to the inmemory Configuration.
 239  
      *
 240  
      * @param key The Key to add the property to.
 241  
      * @param token The Value to add.
 242  
      */
 243  
     @Override
 244  
     protected void addPropertyDirect(String key, Object token)
 245  
     {
 246  38
         inMemoryConfiguration.addProperty(key, token);
 247  38
     }
 248  
 
 249  
     /**
 250  
      * Read property from underlying composite
 251  
      *
 252  
      * @param key key to use for mapping
 253  
      *
 254  
      * @return object associated with the given configuration key.
 255  
      */
 256  
     public Object getProperty(String key)
 257  
     {
 258  146
         Configuration firstMatchingConfiguration = null;
 259  146
         for (Configuration config : configList)
 260  
         {
 261  217
             if (config.containsKey(key))
 262  
             {
 263  121
                 firstMatchingConfiguration = config;
 264  121
                 break;
 265  
             }
 266  96
         }
 267  
 
 268  146
         if (firstMatchingConfiguration != null)
 269  
         {
 270  121
             return firstMatchingConfiguration.getProperty(key);
 271  
         }
 272  
         else
 273  
         {
 274  25
             return null;
 275  
         }
 276  
     }
 277  
 
 278  
     public Iterator<String> getKeys()
 279  
     {
 280  14
         Set<String> keys = new LinkedHashSet<String>();
 281  14
         for (Configuration config : configList)
 282  
         {
 283  24
             for (Iterator<String> it = config.getKeys(); it.hasNext();)
 284  
             {
 285  138
                 keys.add(it.next());
 286  
             }
 287  24
         }
 288  
 
 289  14
         return keys.iterator();
 290  
     }
 291  
 
 292  
     @Override
 293  
     public Iterator<String> getKeys(String key)
 294  
     {
 295  9
         Set<String> keys = new LinkedHashSet<String>();
 296  9
         for (Configuration config : configList)
 297  
         {
 298  22
             for (Iterator<String> it = config.getKeys(key); it.hasNext();)
 299  
             {
 300  216
                 keys.add(it.next());
 301  
             }
 302  22
         }
 303  
 
 304  9
         return keys.iterator();
 305  
     }
 306  
 
 307  
     public boolean isEmpty()
 308  
     {
 309  4
         for (Configuration config : configList)
 310  
         {
 311  4
             if (!config.isEmpty())
 312  
             {
 313  4
                 return false;
 314  
             }
 315  0
         }
 316  
 
 317  0
         return true;
 318  
     }
 319  
 
 320  
     @Override
 321  
     protected void clearPropertyDirect(String key)
 322  
     {
 323  16
         for (Configuration config : configList)
 324  
         {
 325  35
             config.clearProperty(key);
 326  35
         }
 327  16
     }
 328  
 
 329  
     public boolean containsKey(String key)
 330  
     {
 331  55
         for (Configuration config : configList)
 332  
         {
 333  72
             if (config.containsKey(key))
 334  
             {
 335  44
                 return true;
 336  
             }
 337  28
         }
 338  11
         return false;
 339  
     }
 340  
 
 341  
     @Override
 342  
     public List<Object> getList(String key, List<?> defaultValue)
 343  
     {
 344  48
         List<Object> list = new ArrayList<Object>();
 345  
 
 346  
         // add all elements from the first configuration containing the requested key
 347  48
         Iterator<Configuration> it = configList.iterator();
 348  113
         while (it.hasNext() && list.isEmpty())
 349  
         {
 350  65
             Configuration config = it.next();
 351  65
             if (config != inMemoryConfiguration && config.containsKey(key))
 352  
             {
 353  34
                 appendListProperty(list, config, key);
 354  
             }
 355  65
         }
 356  
 
 357  
         // add all elements from the in memory configuration
 358  48
         appendListProperty(list, inMemoryConfiguration, key);
 359  
 
 360  48
         if (list.isEmpty())
 361  
         {
 362  5
             return (List<Object>) defaultValue;
 363  
         }
 364  
 
 365  43
         ListIterator<Object> lit = list.listIterator();
 366  124
         while (lit.hasNext())
 367  
         {
 368  81
             lit.set(interpolate(lit.next()));
 369  
         }
 370  
 
 371  43
         return list;
 372  
     }
 373  
 
 374  
     @Override
 375  
     public String[] getStringArray(String key)
 376  
     {
 377  14
         List<Object> list = getList(key);
 378  
 
 379  
         // transform property values into strings
 380  14
         String[] tokens = new String[list.size()];
 381  
 
 382  35
         for (int i = 0; i < tokens.length; i++)
 383  
         {
 384  21
             tokens[i] = String.valueOf(list.get(i));
 385  
         }
 386  
 
 387  14
         return tokens;
 388  
     }
 389  
 
 390  
     /**
 391  
      * Return the configuration at the specified index.
 392  
      *
 393  
      * @param index The index of the configuration to retrieve
 394  
      * @return the configuration at this index
 395  
      */
 396  
     public Configuration getConfiguration(int index)
 397  
     {
 398  26
         return configList.get(index);
 399  
     }
 400  
 
 401  
     /**
 402  
      * Returns the &quot;in memory configuration&quot;. In this configuration
 403  
      * changes are stored.
 404  
      *
 405  
      * @return the in memory configuration
 406  
      */
 407  
     public Configuration getInMemoryConfiguration()
 408  
     {
 409  8
         return inMemoryConfiguration;
 410  
     }
 411  
 
 412  
     /**
 413  
      * Returns a copy of this object. This implementation will create a deep
 414  
      * clone, i.e. all configurations contained in this composite will also be
 415  
      * cloned. This only works if all contained configurations support cloning;
 416  
      * otherwise a runtime exception will be thrown. Registered event handlers
 417  
      * won't get cloned.
 418  
      *
 419  
      * @return the copy
 420  
      * @since 1.3
 421  
      */
 422  
     @Override
 423  
     public Object clone()
 424  
     {
 425  
         try
 426  
         {
 427  3
             CompositeConfiguration copy = (CompositeConfiguration) super
 428  
                     .clone();
 429  3
             copy.clearConfigurationListeners();
 430  3
             copy.configList = new LinkedList<Configuration>();
 431  3
             copy.inMemoryConfiguration = ConfigurationUtils
 432  
                     .cloneConfiguration(getInMemoryConfiguration());
 433  3
             copy.configList.add(copy.inMemoryConfiguration);
 434  
 
 435  3
             for (Configuration config : configList)
 436  
             {
 437  3
                 if (config != getInMemoryConfiguration())
 438  
                 {
 439  1
                     copy.addConfiguration(ConfigurationUtils
 440  
                             .cloneConfiguration(config));
 441  
                 }
 442  2
             }
 443  
 
 444  2
             return copy;
 445  
         }
 446  0
         catch (CloneNotSupportedException cnex)
 447  
         {
 448  
             // cannot happen
 449  0
             throw new ConfigurationRuntimeException(cnex);
 450  
         }
 451  
     }
 452  
 
 453  
     /**
 454  
      * Sets a flag whether added values for string properties should be checked
 455  
      * for the list delimiter. This implementation ensures that the in memory
 456  
      * configuration is correctly initialized.
 457  
      *
 458  
      * @param delimiterParsingDisabled the new value of the flag
 459  
      * @since 1.4
 460  
      */
 461  
     @Override
 462  
     public void setDelimiterParsingDisabled(boolean delimiterParsingDisabled)
 463  
     {
 464  3
         if (inMemoryConfiguration instanceof AbstractConfiguration)
 465  
         {
 466  2
             ((AbstractConfiguration) inMemoryConfiguration)
 467  
                     .setDelimiterParsingDisabled(delimiterParsingDisabled);
 468  
         }
 469  3
         super.setDelimiterParsingDisabled(delimiterParsingDisabled);
 470  3
     }
 471  
 
 472  
     /**
 473  
      * Sets the character that is used as list delimiter. This implementation
 474  
      * ensures that the in memory configuration is correctly initialized.
 475  
      *
 476  
      * @param listDelimiter the new list delimiter character
 477  
      * @since 1.4
 478  
      */
 479  
     @Override
 480  
     public void setListDelimiter(char listDelimiter)
 481  
     {
 482  3
         if (inMemoryConfiguration instanceof AbstractConfiguration)
 483  
         {
 484  2
             ((AbstractConfiguration) inMemoryConfiguration)
 485  
                     .setListDelimiter(listDelimiter);
 486  
         }
 487  3
         super.setListDelimiter(listDelimiter);
 488  3
     }
 489  
 
 490  
     /**
 491  
      * Returns the configuration source, in which the specified key is defined.
 492  
      * This method will iterate over all existing child configurations and check
 493  
      * whether they contain the specified key. The following constellations are
 494  
      * possible:
 495  
      * <ul>
 496  
      * <li>If exactly one child configuration contains the key, this
 497  
      * configuration is returned as the source configuration. This may be the
 498  
      * <em>in memory configuration</em> (this has to be explicitly checked by
 499  
      * the calling application).</li>
 500  
      * <li>If none of the child configurations contain the key, <b>null</b> is
 501  
      * returned.</li>
 502  
      * <li>If the key is contained in multiple child configurations or if the
 503  
      * key is <b>null</b>, a {@code IllegalArgumentException} is thrown.
 504  
      * In this case the source configuration cannot be determined.</li>
 505  
      * </ul>
 506  
      *
 507  
      * @param key the key to be checked
 508  
      * @return the source configuration of this key
 509  
      * @throws IllegalArgumentException if the source configuration cannot be
 510  
      * determined
 511  
      * @since 1.5
 512  
      */
 513  
     public Configuration getSource(String key)
 514  
     {
 515  5
         if (key == null)
 516  
         {
 517  1
             throw new IllegalArgumentException("Key must not be null!");
 518  
         }
 519  
 
 520  4
         Configuration source = null;
 521  4
         for (Configuration conf : configList)
 522  
         {
 523  12
             if (conf.containsKey(key))
 524  
             {
 525  4
                 if (source != null)
 526  
                 {
 527  1
                     throw new IllegalArgumentException("The key " + key
 528  
                             + " is defined by multiple sources!");
 529  
                 }
 530  3
                 source = conf;
 531  
             }
 532  11
         }
 533  
 
 534  3
         return source;
 535  
     }
 536  
 
 537  
     /**
 538  
      * Replaces the current in-memory configuration by the given one.
 539  
      *
 540  
      * @param config the new in-memory configuration
 541  
      */
 542  
     private void replaceInMemoryConfiguration(Configuration config)
 543  
     {
 544  3
         if (!inMemoryConfigIsChild)
 545  
         {
 546  
             // remove current in-memory configuration
 547  2
             configList.remove(inMemoryConfiguration);
 548  
         }
 549  3
         inMemoryConfiguration = config;
 550  3
     }
 551  
 
 552  
     /**
 553  
      * Adds the value of a property to the given list. This method is used by
 554  
      * {@code getList()} for gathering property values from the child
 555  
      * configurations.
 556  
      *
 557  
      * @param dest the list for collecting the data
 558  
      * @param config the configuration to query
 559  
      * @param key the key of the property
 560  
      */
 561  
     private static void appendListProperty(List<Object> dest, Configuration config,
 562  
             String key)
 563  
     {
 564  82
         Object value = config.getProperty(key);
 565  82
         if (value != null)
 566  
         {
 567  50
             if (value instanceof Collection)
 568  
             {
 569  22
                 Collection<?> col = (Collection<?>) value;
 570  22
                 dest.addAll(col);
 571  22
             }
 572  
             else
 573  
             {
 574  28
                 dest.add(value);
 575  
             }
 576  
         }
 577  82
     }
 578  
 }