Coverage Report - org.apache.commons.configuration.DynamicCombinedConfiguration
 
Classes in this File Line Coverage Branch Coverage Complexity
DynamicCombinedConfiguration
36%
72/195
32%
18/56
1,375
DynamicCombinedConfiguration$1
50%
1/2
N/A
1,375
DynamicCombinedConfiguration$ConfigData
100%
8/8
N/A
1,375
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.apache.commons.configuration;
 18  
 
 19  
 import java.math.BigDecimal;
 20  
 import java.math.BigInteger;
 21  
 import java.util.ArrayList;
 22  
 import java.util.Collection;
 23  
 import java.util.HashMap;
 24  
 import java.util.Iterator;
 25  
 import java.util.List;
 26  
 import java.util.Map;
 27  
 import java.util.Properties;
 28  
 import java.util.Set;
 29  
 import java.util.concurrent.ConcurrentHashMap;
 30  
 import java.util.concurrent.ConcurrentMap;
 31  
 
 32  
 import org.apache.commons.configuration.event.ConfigurationErrorListener;
 33  
 import org.apache.commons.configuration.event.ConfigurationListener;
 34  
 import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
 35  
 import org.apache.commons.configuration.tree.ConfigurationNode;
 36  
 import org.apache.commons.configuration.tree.ExpressionEngine;
 37  
 import org.apache.commons.configuration.tree.NodeCombiner;
 38  
 import org.apache.commons.lang.text.StrSubstitutor;
 39  
 import org.apache.commons.logging.Log;
 40  
 import org.apache.commons.logging.LogFactory;
 41  
 
 42  
 /**
 43  
  * DynamicCombinedConfiguration allows a set of CombinedConfigurations to be used. Each CombinedConfiguration
 44  
  * is referenced by a key that is dynamically constructed from a key pattern on each call. The key pattern
 45  
  * will be resolved using the configured ConfigurationInterpolator.
 46  
  * @since 1.6
 47  
  * @author <a
 48  
  * href="http://commons.apache.org/configuration/team-list.html">Commons
 49  
  * Configuration team</a>
 50  
  * @version $Id: DynamicCombinedConfiguration.java 1534064 2013-10-21 08:44:33Z henning $
 51  
  */
 52  
 public class DynamicCombinedConfiguration extends CombinedConfiguration
 53  
 {
 54  
     /**
 55  
      * Prevent recursion while resolving unprefixed properties.
 56  
      */
 57  1
     private static ThreadLocal<Boolean> recursive = new ThreadLocal<Boolean>()
 58  1
     {
 59  
         @Override
 60  
         protected synchronized Boolean initialValue()
 61  
         {
 62  0
             return Boolean.FALSE;
 63  
         }
 64  
     };
 65  
 
 66  
     /** The CombinedConfigurations */
 67  29
     private final ConcurrentMap<String, CombinedConfiguration> configs =
 68  
             new ConcurrentHashMap<String, CombinedConfiguration>();
 69  
 
 70  
     /** Stores a list with the contained configurations. */
 71  29
     private List<ConfigData> configurations = new ArrayList<ConfigData>();
 72  
 
 73  
     /** Stores a map with the named configurations. */
 74  29
     private Map<String, AbstractConfiguration> namedConfigurations =
 75  
             new HashMap<String, AbstractConfiguration>();
 76  
 
 77  
     /** The key pattern for the CombinedConfiguration map */
 78  
     private String keyPattern;
 79  
 
 80  
     /** Stores the combiner. */
 81  
     private NodeCombiner nodeCombiner;
 82  
 
 83  
     /** The name of the logger to use for each CombinedConfiguration */
 84  29
     private String loggerName = DynamicCombinedConfiguration.class.getName();
 85  
 
 86  
     /** The object for handling variable substitution in key patterns. */
 87  29
     private StrSubstitutor localSubst = new StrSubstitutor(new ConfigurationInterpolator());
 88  
 
 89  
     /**
 90  
      * Creates a new instance of {@code DynamicCombinedConfiguration} and
 91  
      * initializes the combiner to be used.
 92  
      *
 93  
      * @param comb the node combiner (can be <b>null</b>, then a union combiner
 94  
      * is used as default)
 95  
      */
 96  
     public DynamicCombinedConfiguration(NodeCombiner comb)
 97  
     {
 98  0
         super();
 99  0
         setNodeCombiner(comb);
 100  0
         setIgnoreReloadExceptions(false);
 101  0
         setLogger(LogFactory.getLog(DynamicCombinedConfiguration.class));
 102  0
     }
 103  
 
 104  
     /**
 105  
      * Creates a new instance of {@code DynamicCombinedConfiguration} that uses
 106  
      * a union combiner.
 107  
      *
 108  
      * @see org.apache.commons.configuration.tree.UnionCombiner
 109  
      */
 110  
     public DynamicCombinedConfiguration()
 111  
     {
 112  29
         super();
 113  29
         setIgnoreReloadExceptions(false);
 114  29
         setLogger(LogFactory.getLog(DynamicCombinedConfiguration.class));
 115  29
     }
 116  
 
 117  
     public void setKeyPattern(String pattern)
 118  
     {
 119  29
         this.keyPattern = pattern;
 120  29
     }
 121  
 
 122  
     public String getKeyPattern()
 123  
     {
 124  0
         return this.keyPattern;
 125  
     }
 126  
 
 127  
     /**
 128  
      * Set the name of the Logger to use on each CombinedConfiguration.
 129  
      * @param name The Logger name.
 130  
      */
 131  
     public void setLoggerName(String name)
 132  
     {
 133  24
         this.loggerName = name;
 134  24
     }
 135  
 
 136  
     /**
 137  
      * Returns the node combiner that is used for creating the combined node
 138  
      * structure.
 139  
      *
 140  
      * @return the node combiner
 141  
      */
 142  
     @Override
 143  
     public NodeCombiner getNodeCombiner()
 144  
     {
 145  63
         return nodeCombiner;
 146  
     }
 147  
 
 148  
     /**
 149  
      * Sets the node combiner. This object will be used when the combined node
 150  
      * structure is to be constructed. It must not be <b>null</b>, otherwise an
 151  
      * {@code IllegalArgumentException} exception is thrown. Changing the
 152  
      * node combiner causes an invalidation of this combined configuration, so
 153  
      * that the new combiner immediately takes effect.
 154  
      *
 155  
      * @param nodeCombiner the node combiner
 156  
      */
 157  
     @Override
 158  
     public void setNodeCombiner(NodeCombiner nodeCombiner)
 159  
     {
 160  57
         if (nodeCombiner == null)
 161  
         {
 162  0
             throw new IllegalArgumentException(
 163  
                     "Node combiner must not be null!");
 164  
         }
 165  57
         this.nodeCombiner = nodeCombiner;
 166  57
         invalidateAll();
 167  57
     }
 168  
     /**
 169  
      * Adds a new configuration to this combined configuration. It is possible
 170  
      * (but not mandatory) to give the new configuration a name. This name must
 171  
      * be unique, otherwise a {@code ConfigurationRuntimeException} will
 172  
      * be thrown. With the optional {@code at} argument you can specify
 173  
      * where in the resulting node structure the content of the added
 174  
      * configuration should appear. This is a string that uses dots as property
 175  
      * delimiters (independent on the current expression engine). For instance
 176  
      * if you pass in the string {@code "database.tables"},
 177  
      * all properties of the added configuration will occur in this branch.
 178  
      *
 179  
      * @param config the configuration to add (must not be <b>null</b>)
 180  
      * @param name the name of this configuration (can be <b>null</b>)
 181  
      * @param at the position of this configuration in the combined tree (can be
 182  
      * <b>null</b>)
 183  
      */
 184  
     @Override
 185  
     public void addConfiguration(AbstractConfiguration config, String name,
 186  
             String at)
 187  
     {
 188  60
         ConfigData cd = new ConfigData(config, name, at);
 189  60
         configurations.add(cd);
 190  60
         if (name != null)
 191  
         {
 192  59
             namedConfigurations.put(name, config);
 193  
         }
 194  60
     }
 195  
        /**
 196  
      * Returns the number of configurations that are contained in this combined
 197  
      * configuration.
 198  
      *
 199  
      * @return the number of contained configurations
 200  
      */
 201  
     @Override
 202  
     public int getNumberOfConfigurations()
 203  
     {
 204  0
         return configurations.size();
 205  
     }
 206  
 
 207  
     /**
 208  
      * Returns the configuration at the specified index. The contained
 209  
      * configurations are numbered in the order they were added to this combined
 210  
      * configuration. The index of the first configuration is 0.
 211  
      *
 212  
      * @param index the index
 213  
      * @return the configuration at this index
 214  
      */
 215  
     @Override
 216  
     public Configuration getConfiguration(int index)
 217  
     {
 218  0
         ConfigData cd = configurations.get(index);
 219  0
         return cd.getConfiguration();
 220  
     }
 221  
 
 222  
     /**
 223  
      * Returns the configuration with the given name. This can be <b>null</b>
 224  
      * if no such configuration exists.
 225  
      *
 226  
      * @param name the name of the configuration
 227  
      * @return the configuration with this name
 228  
      */
 229  
     @Override
 230  
     public Configuration getConfiguration(String name)
 231  
     {
 232  0
         return namedConfigurations.get(name);
 233  
     }
 234  
 
 235  
     /**
 236  
      * Returns a set with the names of all configurations contained in this
 237  
      * combined configuration. Of course here are only these configurations
 238  
      * listed, for which a name was specified when they were added.
 239  
      *
 240  
      * @return a set with the names of the contained configurations (never
 241  
      * <b>null</b>)
 242  
      */
 243  
     @Override
 244  
     public Set<String> getConfigurationNames()
 245  
     {
 246  0
         return namedConfigurations.keySet();
 247  
     }
 248  
 
 249  
     /**
 250  
      * Removes the configuration with the specified name.
 251  
      *
 252  
      * @param name the name of the configuration to be removed
 253  
      * @return the removed configuration (<b>null</b> if this configuration
 254  
      * was not found)
 255  
      */
 256  
     @Override
 257  
     public Configuration removeConfiguration(String name)
 258  
     {
 259  0
         Configuration conf = getConfiguration(name);
 260  0
         if (conf != null)
 261  
         {
 262  0
             removeConfiguration(conf);
 263  
         }
 264  0
         return conf;
 265  
     }
 266  
 
 267  
     /**
 268  
      * Removes the specified configuration from this combined configuration.
 269  
      *
 270  
      * @param config the configuration to be removed
 271  
      * @return a flag whether this configuration was found and could be removed
 272  
      */
 273  
     @Override
 274  
     public boolean removeConfiguration(Configuration config)
 275  
     {
 276  0
         for (int index = 0; index < getNumberOfConfigurations(); index++)
 277  
         {
 278  0
             if (configurations.get(index).getConfiguration() == config)
 279  
             {
 280  0
                 removeConfigurationAt(index);
 281  
 
 282  
             }
 283  
         }
 284  
 
 285  0
         return super.removeConfiguration(config);
 286  
     }
 287  
 
 288  
     /**
 289  
      * Removes the configuration at the specified index.
 290  
      *
 291  
      * @param index the index
 292  
      * @return the removed configuration
 293  
      */
 294  
     @Override
 295  
     public Configuration removeConfigurationAt(int index)
 296  
     {
 297  0
         ConfigData cd = configurations.remove(index);
 298  0
         if (cd.getName() != null)
 299  
         {
 300  0
             namedConfigurations.remove(cd.getName());
 301  
         }
 302  0
         return super.removeConfigurationAt(index);
 303  
     }
 304  
     /**
 305  
      * Returns the configuration root node of this combined configuration. This
 306  
      * method will construct a combined node structure using the current node
 307  
      * combiner if necessary.
 308  
      *
 309  
      * @return the combined root node
 310  
      */
 311  
     @Override
 312  
     public ConfigurationNode getRootNode()
 313  
     {
 314  0
         return getCurrentConfig().getRootNode();
 315  
     }
 316  
 
 317  
     @Override
 318  
     public void setRootNode(ConfigurationNode rootNode)
 319  
     {
 320  29
         if (configs != null)
 321  
         {
 322  0
             this.getCurrentConfig().setRootNode(rootNode);
 323  
         }
 324  
         else
 325  
         {
 326  29
             super.setRootNode(rootNode);
 327  
         }
 328  29
     }
 329  
 
 330  
     @Override
 331  
     public void addProperty(String key, Object value)
 332  
     {
 333  2
         this.getCurrentConfig().addProperty(key, value);
 334  2
     }
 335  
 
 336  
     @Override
 337  
     public void clear()
 338  
     {
 339  29
         if (configs != null)
 340  
         {
 341  0
             this.getCurrentConfig().clear();
 342  
         }
 343  29
     }
 344  
 
 345  
     @Override
 346  
     public void clearProperty(String key)
 347  
     {
 348  0
         this.getCurrentConfig().clearProperty(key);
 349  0
     }
 350  
 
 351  
     @Override
 352  
     public boolean containsKey(String key)
 353  
     {
 354  0
         return this.getCurrentConfig().containsKey(key);
 355  
     }
 356  
 
 357  
     @Override
 358  
     public BigDecimal getBigDecimal(String key, BigDecimal defaultValue)
 359  
     {
 360  0
         return this.getCurrentConfig().getBigDecimal(key, defaultValue);
 361  
     }
 362  
 
 363  
     @Override
 364  
     public BigDecimal getBigDecimal(String key)
 365  
     {
 366  0
         return this.getCurrentConfig().getBigDecimal(key);
 367  
     }
 368  
 
 369  
     @Override
 370  
     public BigInteger getBigInteger(String key, BigInteger defaultValue)
 371  
     {
 372  0
         return this.getCurrentConfig().getBigInteger(key, defaultValue);
 373  
     }
 374  
 
 375  
     @Override
 376  
     public BigInteger getBigInteger(String key)
 377  
     {
 378  0
         return this.getCurrentConfig().getBigInteger(key);
 379  
     }
 380  
 
 381  
     @Override
 382  
     public boolean getBoolean(String key, boolean defaultValue)
 383  
     {
 384  0
         return this.getCurrentConfig().getBoolean(key, defaultValue);
 385  
     }
 386  
 
 387  
     @Override
 388  
     public Boolean getBoolean(String key, Boolean defaultValue)
 389  
     {
 390  0
         return this.getCurrentConfig().getBoolean(key, defaultValue);
 391  
     }
 392  
 
 393  
     @Override
 394  
     public boolean getBoolean(String key)
 395  
     {
 396  0
         return this.getCurrentConfig().getBoolean(key);
 397  
     }
 398  
 
 399  
     @Override
 400  
     public byte getByte(String key, byte defaultValue)
 401  
     {
 402  0
         return this.getCurrentConfig().getByte(key, defaultValue);
 403  
     }
 404  
 
 405  
     @Override
 406  
     public Byte getByte(String key, Byte defaultValue)
 407  
     {
 408  0
         return this.getCurrentConfig().getByte(key, defaultValue);
 409  
     }
 410  
 
 411  
     @Override
 412  
     public byte getByte(String key)
 413  
     {
 414  0
         return this.getCurrentConfig().getByte(key);
 415  
     }
 416  
 
 417  
     @Override
 418  
     public double getDouble(String key, double defaultValue)
 419  
     {
 420  0
         return this.getCurrentConfig().getDouble(key, defaultValue);
 421  
     }
 422  
 
 423  
     @Override
 424  
     public Double getDouble(String key, Double defaultValue)
 425  
     {
 426  0
         return this.getCurrentConfig().getDouble(key, defaultValue);
 427  
     }
 428  
 
 429  
     @Override
 430  
     public double getDouble(String key)
 431  
     {
 432  0
         return this.getCurrentConfig().getDouble(key);
 433  
     }
 434  
 
 435  
     @Override
 436  
     public float getFloat(String key, float defaultValue)
 437  
     {
 438  0
         return this.getCurrentConfig().getFloat(key, defaultValue);
 439  
     }
 440  
 
 441  
     @Override
 442  
     public Float getFloat(String key, Float defaultValue)
 443  
     {
 444  0
         return this.getCurrentConfig().getFloat(key, defaultValue);
 445  
     }
 446  
 
 447  
     @Override
 448  
     public float getFloat(String key)
 449  
     {
 450  0
         return this.getCurrentConfig().getFloat(key);
 451  
     }
 452  
 
 453  
     @Override
 454  
     public int getInt(String key, int defaultValue)
 455  
     {
 456  0
         return this.getCurrentConfig().getInt(key, defaultValue);
 457  
     }
 458  
 
 459  
     @Override
 460  
     public int getInt(String key)
 461  
     {
 462  65
         return this.getCurrentConfig().getInt(key);
 463  
     }
 464  
 
 465  
     @Override
 466  
     public Integer getInteger(String key, Integer defaultValue)
 467  
     {
 468  0
         return this.getCurrentConfig().getInteger(key, defaultValue);
 469  
     }
 470  
 
 471  
     @Override
 472  
     public Iterator<String> getKeys()
 473  
     {
 474  0
         return this.getCurrentConfig().getKeys();
 475  
     }
 476  
 
 477  
     @Override
 478  
     public Iterator<String> getKeys(String prefix)
 479  
     {
 480  0
         return this.getCurrentConfig().getKeys(prefix);
 481  
     }
 482  
 
 483  
     @Override
 484  
     public List<Object> getList(String key, List<?> defaultValue)
 485  
     {
 486  0
         return this.getCurrentConfig().getList(key, defaultValue);
 487  
     }
 488  
 
 489  
     @Override
 490  
     public List<Object> getList(String key)
 491  
     {
 492  0
         return this.getCurrentConfig().getList(key);
 493  
     }
 494  
 
 495  
     @Override
 496  
     public long getLong(String key, long defaultValue)
 497  
     {
 498  0
         return this.getCurrentConfig().getLong(key, defaultValue);
 499  
     }
 500  
 
 501  
     @Override
 502  
     public Long getLong(String key, Long defaultValue)
 503  
     {
 504  0
         return this.getCurrentConfig().getLong(key, defaultValue);
 505  
     }
 506  
 
 507  
     @Override
 508  
     public long getLong(String key)
 509  
     {
 510  0
         return this.getCurrentConfig().getLong(key);
 511  
     }
 512  
 
 513  
     @Override
 514  
     public Properties getProperties(String key)
 515  
     {
 516  0
         return this.getCurrentConfig().getProperties(key);
 517  
     }
 518  
 
 519  
     @Override
 520  
     public Object getProperty(String key)
 521  
     {
 522  0
         return this.getCurrentConfig().getProperty(key);
 523  
     }
 524  
 
 525  
     @Override
 526  
     public short getShort(String key, short defaultValue)
 527  
     {
 528  0
         return this.getCurrentConfig().getShort(key, defaultValue);
 529  
     }
 530  
 
 531  
     @Override
 532  
     public Short getShort(String key, Short defaultValue)
 533  
     {
 534  0
         return this.getCurrentConfig().getShort(key, defaultValue);
 535  
     }
 536  
 
 537  
     @Override
 538  
     public short getShort(String key)
 539  
     {
 540  0
         return this.getCurrentConfig().getShort(key);
 541  
     }
 542  
 
 543  
     @Override
 544  
     public String getString(String key, String defaultValue)
 545  
     {
 546  803
         return this.getCurrentConfig().getString(key, defaultValue);
 547  
     }
 548  
 
 549  
     @Override
 550  
     public String getString(String key)
 551  
     {
 552  204796
         return this.getCurrentConfig().getString(key);
 553  
     }
 554  
 
 555  
     @Override
 556  
     public String[] getStringArray(String key)
 557  
     {
 558  0
         return this.getCurrentConfig().getStringArray(key);
 559  
     }
 560  
 
 561  
     @Override
 562  
     public boolean isEmpty()
 563  
     {
 564  0
         return this.getCurrentConfig().isEmpty();
 565  
     }
 566  
 
 567  
     @Override
 568  
     public void setProperty(String key, Object value)
 569  
     {
 570  0
         if (configs != null)
 571  
         {
 572  0
             this.getCurrentConfig().setProperty(key, value);
 573  
         }
 574  0
     }
 575  
 
 576  
     @Override
 577  
     public Configuration subset(String prefix)
 578  
     {
 579  0
         return this.getCurrentConfig().subset(prefix);
 580  
     }
 581  
 
 582  
     @Override
 583  
     public Node getRoot()
 584  
     {
 585  0
         return this.getCurrentConfig().getRoot();
 586  
     }
 587  
 
 588  
     @Override
 589  
     public void setRoot(Node node)
 590  
     {
 591  0
         if (configs != null)
 592  
         {
 593  0
             this.getCurrentConfig().setRoot(node);
 594  
         }
 595  
         else
 596  
         {
 597  0
             super.setRoot(node);
 598  
         }
 599  0
     }
 600  
 
 601  
     @Override
 602  
     public ExpressionEngine getExpressionEngine()
 603  
     {
 604  63
         return super.getExpressionEngine();
 605  
     }
 606  
 
 607  
     @Override
 608  
     public void setExpressionEngine(ExpressionEngine expressionEngine)
 609  
     {
 610  29
         super.setExpressionEngine(expressionEngine);
 611  29
     }
 612  
 
 613  
     @Override
 614  
     public void addNodes(String key, Collection<? extends ConfigurationNode> nodes)
 615  
     {
 616  0
         this.getCurrentConfig().addNodes(key, nodes);
 617  0
     }
 618  
 
 619  
     @Override
 620  
     public SubnodeConfiguration configurationAt(String key, boolean supportUpdates)
 621  
     {
 622  0
         return this.getCurrentConfig().configurationAt(key, supportUpdates);
 623  
     }
 624  
 
 625  
     @Override
 626  
     public SubnodeConfiguration configurationAt(String key)
 627  
     {
 628  2
         return this.getCurrentConfig().configurationAt(key);
 629  
     }
 630  
 
 631  
     @Override
 632  
     public List<HierarchicalConfiguration> configurationsAt(String key)
 633  
     {
 634  1
         return this.getCurrentConfig().configurationsAt(key);
 635  
     }
 636  
 
 637  
     @Override
 638  
     public void clearTree(String key)
 639  
     {
 640  0
         this.getCurrentConfig().clearTree(key);
 641  0
     }
 642  
 
 643  
     @Override
 644  
     public int getMaxIndex(String key)
 645  
     {
 646  4
         return this.getCurrentConfig().getMaxIndex(key);
 647  
     }
 648  
 
 649  
     @Override
 650  
     public Configuration interpolatedConfiguration()
 651  
     {
 652  0
         return this.getCurrentConfig().interpolatedConfiguration();
 653  
     }
 654  
 
 655  
 
 656  
     /**
 657  
      * Returns the configuration source, in which the specified key is defined.
 658  
      * This method will determine the configuration node that is identified by
 659  
      * the given key. The following constellations are possible:
 660  
      * <ul>
 661  
      * <li>If no node object is found for this key, <b>null</b> is returned.</li>
 662  
      * <li>If the key maps to multiple nodes belonging to different
 663  
      * configuration sources, a {@code IllegalArgumentException} is
 664  
      * thrown (in this case no unique source can be determined).</li>
 665  
      * <li>If exactly one node is found for the key, the (child) configuration
 666  
      * object, to which the node belongs is determined and returned.</li>
 667  
      * <li>For keys that have been added directly to this combined
 668  
      * configuration and that do not belong to the namespaces defined by
 669  
      * existing child configurations this configuration will be returned.</li>
 670  
      * </ul>
 671  
      *
 672  
      * @param key the key of a configuration property
 673  
      * @return the configuration, to which this property belongs or <b>null</b>
 674  
      * if the key cannot be resolved
 675  
      * @throws IllegalArgumentException if the key maps to multiple properties
 676  
      * and the source cannot be determined, or if the key is <b>null</b>
 677  
      */
 678  
     @Override
 679  
     public Configuration getSource(String key)
 680  
     {
 681  0
         if (key == null)
 682  
         {
 683  0
             throw new IllegalArgumentException("Key must not be null!");
 684  
         }
 685  0
         return getCurrentConfig().getSource(key);
 686  
     }
 687  
 
 688  
     @Override
 689  
     public void addConfigurationListener(ConfigurationListener l)
 690  
     {
 691  0
         super.addConfigurationListener(l);
 692  
 
 693  0
         for (CombinedConfiguration cc : configs.values())
 694  
         {
 695  0
             cc.addConfigurationListener(l);
 696  0
         }
 697  0
     }
 698  
 
 699  
     @Override
 700  
     public boolean removeConfigurationListener(ConfigurationListener l)
 701  
     {
 702  0
         for (CombinedConfiguration cc : configs.values())
 703  
         {
 704  0
             cc.removeConfigurationListener(l);
 705  0
         }
 706  0
         return super.removeConfigurationListener(l);
 707  
     }
 708  
 
 709  
     @Override
 710  
     public Collection<ConfigurationListener> getConfigurationListeners()
 711  
     {
 712  63
         return super.getConfigurationListeners();
 713  
     }
 714  
 
 715  
     @Override
 716  
     public void clearConfigurationListeners()
 717  
     {
 718  0
         for (CombinedConfiguration cc : configs.values())
 719  
         {
 720  0
             cc.clearConfigurationListeners();
 721  0
         }
 722  0
         super.clearConfigurationListeners();
 723  0
     }
 724  
 
 725  
     @Override
 726  
     public void addErrorListener(ConfigurationErrorListener l)
 727  
     {
 728  0
         for (CombinedConfiguration cc : configs.values())
 729  
         {
 730  0
             cc.addErrorListener(l);
 731  0
         }
 732  0
         super.addErrorListener(l);
 733  0
     }
 734  
 
 735  
     @Override
 736  
     public boolean removeErrorListener(ConfigurationErrorListener l)
 737  
     {
 738  0
         for (CombinedConfiguration cc : configs.values())
 739  
         {
 740  0
             cc.removeErrorListener(l);
 741  0
         }
 742  0
         return super.removeErrorListener(l);
 743  
     }
 744  
 
 745  
     @Override
 746  
     public void clearErrorListeners()
 747  
     {
 748  0
         for (CombinedConfiguration cc : configs.values())
 749  
         {
 750  0
             cc.clearErrorListeners();
 751  0
         }
 752  0
         super.clearErrorListeners();
 753  0
     }
 754  
 
 755  
     @Override
 756  
     public Collection<ConfigurationErrorListener> getErrorListeners()
 757  
     {
 758  63
         return super.getErrorListeners();
 759  
     }
 760  
 
 761  
     /**
 762  
      * Returns a copy of this object. This implementation performs a deep clone,
 763  
      * i.e. all contained configurations will be cloned, too. For this to work,
 764  
      * all contained configurations must be cloneable. Registered event
 765  
      * listeners won't be cloned. The clone will use the same node combiner than
 766  
      * the original.
 767  
      *
 768  
      * @return the copied object
 769  
      */
 770  
     @Override
 771  
     public Object clone()
 772  
     {
 773  0
         return super.clone();
 774  
     }
 775  
 
 776  
     /**
 777  
      * Invalidates the current combined configuration. This means that the next time a
 778  
      * property is accessed the combined node structure must be re-constructed.
 779  
      * Invalidation of a combined configuration also means that an event of type
 780  
      * {@code EVENT_COMBINED_INVALIDATE} is fired. Note that while other
 781  
      * events most times appear twice (once before and once after an update),
 782  
      * this event is only fired once (after update).
 783  
      */
 784  
     @Override
 785  
     public void invalidate()
 786  
     {
 787  0
         getCurrentConfig().invalidate();
 788  0
     }
 789  
 
 790  
     public void invalidateAll()
 791  
     {
 792  57
         if (configs == null)
 793  
         {
 794  29
             return;
 795  
         }
 796  28
         for (CombinedConfiguration cc : configs.values())
 797  
         {
 798  0
             cc.invalidate();
 799  0
         }
 800  28
     }
 801  
 
 802  
     /*
 803  
      * Don't allow resolveContainerStore to be called recursively.
 804  
      * @param key The key to resolve.
 805  
      * @return The value of the key.
 806  
      */
 807  
     @Override
 808  
     protected Object resolveContainerStore(String key)
 809  
     {
 810  0
         if (recursive.get().booleanValue())
 811  
         {
 812  0
             return null;
 813  
         }
 814  0
         recursive.set(Boolean.TRUE);
 815  
         try
 816  
         {
 817  0
             return super.resolveContainerStore(key);
 818  
         }
 819  
         finally
 820  
         {
 821  0
             recursive.set(Boolean.FALSE);
 822  
         }
 823  
     }
 824  
 
 825  
     private CombinedConfiguration getCurrentConfig()
 826  
     {
 827  205693
         String key = localSubst.replace(keyPattern);
 828  205754
         CombinedConfiguration config = configs.get(key);
 829  
         // The double-checked works here due to the Thread guarantees of ConcurrentMap.
 830  205720
         if (config == null)
 831  
         {
 832  63
             synchronized (configs)
 833  
             {
 834  63
                 config = configs.get(key);
 835  63
                 if (config == null)
 836  
                 {
 837  63
                     config = new CombinedConfiguration(getNodeCombiner());
 838  63
                     if (loggerName != null)
 839  
                     {
 840  63
                         Log log = LogFactory.getLog(loggerName);
 841  63
                         if (log != null)
 842  
                         {
 843  63
                             config.setLogger(log);
 844  
                         }
 845  
                     }
 846  63
                     config.setIgnoreReloadExceptions(isIgnoreReloadExceptions());
 847  63
                     config.setExpressionEngine(this.getExpressionEngine());
 848  63
                     config.setDelimiterParsingDisabled(isDelimiterParsingDisabled());
 849  63
                     config.setConversionExpressionEngine(getConversionExpressionEngine());
 850  63
                     config.setListDelimiter(getListDelimiter());
 851  63
                     for (ConfigurationErrorListener listener : getErrorListeners())
 852  
                     {
 853  0
                         config.addErrorListener(listener);
 854  0
                     }
 855  63
                     for (ConfigurationListener listener : getConfigurationListeners())
 856  
                     {
 857  0
                         config.addConfigurationListener(listener);
 858  0
                     }
 859  63
                     config.setForceReloadCheck(isForceReloadCheck());
 860  63
                     for (ConfigData data : configurations)
 861  
                     {
 862  130
                         config.addConfiguration(data.getConfiguration(), data.getName(), data.getAt());
 863  130
                     }
 864  63
                     configs.put(key, config);
 865  
                 }
 866  63
             }
 867  
         }
 868  205690
         if (getLogger().isDebugEnabled())
 869  
         {
 870  0
             getLogger().debug("Returning config for " + key + ": " + config);
 871  
         }
 872  205676
         return config;
 873  
     }
 874  
 
 875  
     /**
 876  
      * Internal class that identifies each Configuration.
 877  
      */
 878  
     static class ConfigData
 879  
     {
 880  
         /** Stores a reference to the configuration. */
 881  
         private AbstractConfiguration configuration;
 882  
 
 883  
         /** Stores the name under which the configuration is stored. */
 884  
         private String name;
 885  
 
 886  
         /** Stores the at string.*/
 887  
         private String at;
 888  
 
 889  
         /**
 890  
          * Creates a new instance of {@code ConfigData} and initializes
 891  
          * it.
 892  
          *
 893  
          * @param config the configuration
 894  
          * @param n the name
 895  
          * @param at the at position
 896  
          */
 897  
         public ConfigData(AbstractConfiguration config, String n, String at)
 898  60
         {
 899  60
             configuration = config;
 900  60
             name = n;
 901  60
             this.at = at;
 902  60
         }
 903  
 
 904  
         /**
 905  
          * Returns the stored configuration.
 906  
          *
 907  
          * @return the configuration
 908  
          */
 909  
         public AbstractConfiguration getConfiguration()
 910  
         {
 911  130
             return configuration;
 912  
         }
 913  
 
 914  
         /**
 915  
          * Returns the configuration's name.
 916  
          *
 917  
          * @return the name
 918  
          */
 919  
         public String getName()
 920  
         {
 921  130
             return name;
 922  
         }
 923  
 
 924  
         /**
 925  
          * Returns the at position of this configuration.
 926  
          *
 927  
          * @return the at position
 928  
          */
 929  
         public String getAt()
 930  
         {
 931  130
             return at;
 932  
         }
 933  
 
 934  
     }
 935  
 }