You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by oh...@apache.org on 2008/03/28 20:06:07 UTC

svn commit: r642337 - in /commons/proper/configuration/branches/configuration2_experimental/src: main/java/org/apache/commons/configuration2/combined/ test/java/org/apache/commons/configuration2/combined/

Author: oheger
Date: Fri Mar 28 12:06:04 2008
New Revision: 642337

URL: http://svn.apache.org/viewvc?rev=642337&view=rev
Log:
New implementation of CombinedConfiguration based on node handlers

Added:
    commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/combined/CombinedConfiguration.java
      - copied, changed from r640051, commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/CombinedConfiguration.java
    commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/combined/TestCombinedConfiguration.java
      - copied, changed from r636104, commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestCombinedConfiguration.java

Copied: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/combined/CombinedConfiguration.java (from r640051, commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/CombinedConfiguration.java)
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/combined/CombinedConfiguration.java?p2=commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/combined/CombinedConfiguration.java&p1=commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/CombinedConfiguration.java&r1=640051&r2=642337&rev=642337&view=diff
==============================================================================
--- commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/CombinedConfiguration.java (original)
+++ commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/combined/CombinedConfiguration.java Fri Mar 28 12:06:04 2008
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.configuration2;
+package org.apache.commons.configuration2.combined;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -24,15 +24,16 @@
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.commons.configuration2.AbstractHierarchicalConfiguration;
+import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.configuration2.ConfigurationRuntimeException;
+import org.apache.commons.configuration2.ConfigurationUtils;
 import org.apache.commons.configuration2.event.ConfigurationEvent;
 import org.apache.commons.configuration2.event.ConfigurationListener;
-import org.apache.commons.configuration2.tree.ConfigurationNode;
-import org.apache.commons.configuration2.tree.DefaultConfigurationKey;
-import org.apache.commons.configuration2.tree.DefaultConfigurationNode;
-import org.apache.commons.configuration2.tree.DefaultExpressionEngine;
-import org.apache.commons.configuration2.tree.NodeCombiner;
-import org.apache.commons.configuration2.tree.UnionCombiner;
-import org.apache.commons.configuration2.tree.ViewNode;
+import org.apache.commons.configuration2.expr.NodeHandler;
+import org.apache.commons.configuration2.expr.NodeList;
+import org.apache.commons.configuration2.expr.def.DefaultConfigurationKey;
+import org.apache.commons.configuration2.expr.def.DefaultExpressionEngine;
 
 /**
  * <p>
@@ -40,11 +41,11 @@
  * </p>
  * <p>
  * This class maintains a list of configuration objects, which can be added
- * using the divers <code>addConfiguration()</code> methods. After that the
+ * using the various <code>addConfiguration()</code> methods. After that the
  * configurations can be accessed either by name (if one was provided when the
  * configuration was added) or by index. For the whole set of managed
  * configurations a logical node structure is constructed. For this purpose a
- * <code>{@link org.apache.commons.configuration2.tree.NodeCombiner NodeCombiner}</code>
+ * <code>{@link NodeCombiner}</code>
  * object can be set. This makes it possible to specify different algorithms for
  * the combination process.
  * </p>
@@ -100,8 +101,7 @@
  *
  * </dd>
  * </dl>
- * As a <code>NodeCombiner</code> a
- * <code>{@link org.apache.commons.configuration2.tree.OverrideCombiner OverrideCombiner}</code>
+ * As a <code>NodeCombiner</code> an <code>{@link OverrideCombiner}</code>
  * is used. This combiner will ensure that defined user settings take precedence
  * over the default values. If the resulting <code>CombinedConfiguration</code>
  * is queried for the background color, <code>blue</code> will be returned
@@ -123,7 +123,7 @@
  * it finds one in the second, and adds it to the resulting combined
  * configuration. So the property is still present (with a different value now).</li>
  * <li><code>addProperty()</code> can also be problematic: Most node
- * combiners use special view nodes for linking parts of the original
+ * combiners use special combined nodes for linking parts of the original
  * configurations' data together. If new properties are added to such a special
  * node, they do not belong to any of the managed configurations and thus hang
  * in the air. Using the same configurations as in the last example, the
@@ -162,10 +162,11 @@
  * @author <a
  * href="http://commons.apache.org/configuration/team-list.html">Commons
  * Configuration team</a>
- * @since 1.3
+ * @since 2.0
  * @version $Id$
  */
-public class CombinedConfiguration extends HierarchicalConfiguration implements
+public class CombinedConfiguration extends
+        AbstractHierarchicalConfiguration<Object> implements
         ConfigurationListener, Cloneable
 {
     /**
@@ -174,33 +175,31 @@
      */
     public static final int EVENT_COMBINED_INVALIDATE = 40;
 
-    /**
-     * The serial version ID.
-     */
-    private static final long serialVersionUID = 8338574525528692307L;
-
     /** Constant for the expression engine for parsing the at path. */
     private static final DefaultExpressionEngine AT_ENGINE = new DefaultExpressionEngine();
 
+    /** Constant for the node handler for combined nodes. */
+    private static final CombinedNodeHandler COMBINED_NODE_HANDLER = new CombinedNodeHandler();
+
     /** Constant for the default node combiner. */
     private static final NodeCombiner DEFAULT_COMBINER = new UnionCombiner();
 
-    /** Constant for the name of the property used for the reload check.*/
+    /** Constant for the name of the property used for the reload check. */
     private static final String PROP_RELOAD_CHECK = "CombinedConfigurationReloadCheck";
 
     /** Stores the combiner. */
     private NodeCombiner nodeCombiner;
 
     /** Stores the combined root node. */
-    private ConfigurationNode combinedRoot;
+    private CombinedNode combinedRoot;
 
     /** Stores a list with the contained configurations. */
-    private List<ConfigData> configurations;
+    private List<ConfigData<?>> configurations;
 
     /** Stores a map with the named configurations. */
     private Map<String, Configuration> namedConfigurations;
 
-    /** A flag whether an enhanced reload check is to be performed.*/
+    /** A flag whether an enhanced reload check is to be performed. */
     private boolean forceReloadCheck;
 
     /**
@@ -208,10 +207,11 @@
      * initializes the combiner to be used.
      *
      * @param comb the node combiner (can be <b>null</b>, then a union combiner
-     * is used as default)
+     *        is used as default)
      */
     public CombinedConfiguration(NodeCombiner comb)
     {
+        super(new CombinedConfigurationNodeHandler());
         setNodeCombiner((comb != null) ? comb : DEFAULT_COMBINER);
         clear();
     }
@@ -275,8 +275,8 @@
      * configurations. This is a workaround for a problem with some reload
      * implementations that only check if a reload is required when they are
      * triggered. Per default this mode is disabled. If the force reload check
-     * flag is set to <b>true</b>, accessing properties will be less
-     * performant, but reloads on contained configurations will be detected.
+     * flag is set to <b>true</b>, accessing properties will be less efficient,
+     * but reloads on contained configurations will be detected.
      *
      * @param forceReloadCheck the value of the flag
      * @since 1.4
@@ -300,10 +300,14 @@
      * @param config the configuration to add (must not be <b>null</b>)
      * @param name the name of this configuration (can be <b>null</b>)
      * @param at the position of this configuration in the combined tree (can be
-     * <b>null</b>)
-     */
-    public void addConfiguration(AbstractConfiguration config, String name,
-            String at)
+     *        <b>null</b>)
+     * @throws IllegalArgumentException if the configuration is <b>null</b>
+     * @throws ConfigurationRuntimeException if there is already a configuration
+     *         with the given name
+     */
+    @SuppressWarnings("unchecked")
+    public void addConfiguration(AbstractHierarchicalConfiguration<?> config,
+            String name, String at)
     {
         if (config == null)
         {
@@ -325,6 +329,7 @@
             namedConfigurations.put(name, config);
         }
 
+        config.getNodeHandler().initNodeHandlerRegistry(getCombinedNodeHandler());
         config.addConfigurationListener(this);
         invalidate();
     }
@@ -337,7 +342,8 @@
      * @param config the configuration to add (must not be <b>null</b>)
      * @param name the name of this configuration (can be <b>null</b>)
      */
-    public void addConfiguration(AbstractConfiguration config, String name)
+    public void addConfiguration(AbstractHierarchicalConfiguration<?> config,
+            String name)
     {
         addConfiguration(config, name, null);
     }
@@ -349,7 +355,7 @@
      *
      * @param config the configuration to add (must not be <b>null</b>)
      */
-    public void addConfiguration(AbstractConfiguration config)
+    public void addConfiguration(AbstractHierarchicalConfiguration<?> config)
     {
         addConfiguration(config, null, null);
     }
@@ -375,7 +381,7 @@
      */
     public Configuration getConfiguration(int index)
     {
-        ConfigData cd = (ConfigData) configurations.get(index);
+        ConfigData<?> cd = (ConfigData<?>) configurations.get(index);
         return cd.getConfiguration();
     }
 
@@ -401,7 +407,7 @@
     {
         for (int index = 0; index < getNumberOfConfigurations(); index++)
         {
-            if (((ConfigData) configurations.get(index)).getConfiguration() == config)
+            if (((ConfigData<?>) configurations.get(index)).getConfiguration() == config)
             {
                 removeConfigurationAt(index);
                 return true;
@@ -419,7 +425,7 @@
      */
     public Configuration removeConfigurationAt(int index)
     {
-        ConfigData cd = (ConfigData) configurations.remove(index);
+        ConfigData<?> cd = (ConfigData<?>) configurations.remove(index);
         if (cd.getName() != null)
         {
             namedConfigurations.remove(cd.getName());
@@ -434,7 +440,7 @@
      *
      * @param name the name of the configuration to be removed
      * @return the removed configuration (<b>null</b> if this configuration
-     * was not found)
+     *         was not found)
      */
     public Configuration removeConfiguration(String name)
     {
@@ -452,7 +458,7 @@
      * listed, for which a name was specified when they were added.
      *
      * @return a set with the names of the contained configurations (never
-     * <b>null</b>)
+     *         <b>null</b>)
      */
     public Set<String> getConfigurationNames()
     {
@@ -498,12 +504,14 @@
      *
      * @return the combined root node
      */
-    public ConfigurationNode getRootNode()
+    @Override
+    public Object getRootNode()
     {
         synchronized (getNodeCombiner())
         {
             if (combinedRoot == null)
             {
+                getCombinedNodeHandler().setHandlers(createSubHandlers());
                 combinedRoot = constructCombinedNode();
             }
             return combinedRoot;
@@ -516,7 +524,7 @@
     public void clear()
     {
         fireEvent(EVENT_CLEAR, null, null, true);
-        configurations = new ArrayList<ConfigData>();
+        configurations = new ArrayList<ConfigData<?>>();
         namedConfigurations = new HashMap<String, Configuration>();
         fireEvent(EVENT_CLEAR, null, null, false);
         invalidate();
@@ -533,17 +541,27 @@
      */
     public Object clone()
     {
-        CombinedConfiguration copy = (CombinedConfiguration) super.clone();
-        copy.clear();
-        for (ConfigData cd : configurations)
+        try
         {
-            copy.addConfiguration((AbstractConfiguration) ConfigurationUtils
-                    .cloneConfiguration(cd.getConfiguration()), cd.getName(),
-                    cd.getAt());
-        }
+            CombinedConfiguration copy = (CombinedConfiguration) super.clone();
+            copy.clear();
+            for (ConfigData<?> cd : configurations)
+            {
+                copy
+                        .addConfiguration(
+                                (AbstractHierarchicalConfiguration<?>) ConfigurationUtils
+                                        .cloneConfiguration(cd
+                                                .getConfiguration()), cd
+                                        .getName(), cd.getAt());
+            }
 
-        copy.setRootNode(new DefaultConfigurationNode());
-        return copy;
+            return copy;
+        }
+        catch (CloneNotSupportedException cnsex)
+        {
+            // cannot happen
+            throw new ConfigurationRuntimeException(cnsex);
+        }
     }
 
     /**
@@ -560,7 +578,7 @@
     {
         if (isForceReloadCheck())
         {
-            for (ConfigData cd : configurations)
+            for (ConfigData<?> cd : configurations)
             {
                 try
                 {
@@ -597,9 +615,9 @@
      *
      * @param key the key of a configuration property
      * @return the configuration, to which this property belongs or <b>null</b>
-     * if the key cannot be resolved
+     *         if the key cannot be resolved
      * @throws IllegalArgumentException if the key maps to multiple properties
-     * and the source cannot be determined, or if the key is <b>null</b>
+     *         and the source cannot be determined, or if the key is <b>null</b>
      * @since 1.5
      */
     public Configuration getSource(String key)
@@ -609,20 +627,20 @@
             throw new IllegalArgumentException("Key must not be null!");
         }
 
-        List<ConfigurationNode> nodes = fetchNodeList(key);
-        if (nodes.isEmpty())
+        NodeList<Object> nodes = fetchNodeList(key);
+        if (nodes.size() == 0)
         {
             return null;
         }
 
-        Iterator<ConfigurationNode> it = nodes.iterator();
-        Configuration source = findSourceConfiguration(it.next());
-        while (it.hasNext())
+        Configuration source = findSourceConfiguration(nodes, 0);
+        for (int idx = 1; idx < nodes.size(); idx++)
         {
-            Configuration src = findSourceConfiguration(it.next());
+            Configuration src = findSourceConfiguration(nodes, idx);
             if (src != source)
             {
-                throw new IllegalArgumentException("The key " + key + " is defined by multiple sources!");
+                throw new IllegalArgumentException("The key " + key
+                        + " is defined by multiple sources!");
             }
         }
 
@@ -630,50 +648,88 @@
     }
 
     /**
+     * Creates a map with an association between the existing node classes and
+     * their <code>NodeHandler</code>s. This method is called whenever new
+     * child configurations (with potentially new node types) have been added.
+     *
+     * @return a map with information about the existing node types and their
+     *         handlers
+     */
+    protected Map<Class<?>, NodeHandler<?>> createSubHandlers()
+    {
+        Map<Class<?>, NodeHandler<?>> result = new HashMap<Class<?>, NodeHandler<?>>();
+
+        for (ConfigData<?> cd : configurations)
+        {
+            result.put(cd.getConfiguration().getRootNode().getClass(), cd
+                    .getConfiguration().getNodeHandler());
+        }
+
+        return result;
+    }
+
+    /**
      * Creates the root node of this combined configuration.
      *
      * @return the combined root node
      */
-    private ConfigurationNode constructCombinedNode()
+    private CombinedNode constructCombinedNode()
     {
+        initSubHandlers();
+
         if (getNumberOfConfigurations() < 1)
         {
-            return new ViewNode();
+            return new CombinedNode();
         }
 
         else
         {
-            Iterator<ConfigData> it = configurations.iterator();
-            ConfigurationNode node = it.next().getTransformedRoot();
+            Iterator<ConfigData<?>> it = configurations.iterator();
+            CombinedNode node = it.next().getTransformedRoot();
             while (it.hasNext())
             {
-                node = getNodeCombiner().combine(node,
-                        ((ConfigData) it.next()).getTransformedRoot());
+                node = getNodeCombiner().combine(node, getNodeHandler(),
+                        it.next().getTransformedRoot(), getNodeHandler());
             }
             return node;
         }
     }
 
     /**
+     * Initializes the mapping of the node handlers for the sub configurations.
+     * This method calls <code>createSubHeandlers()</code> to construct the
+     * handler mapping. Then a handler for the <code>CombinedNode</code> type
+     * is added explicitly. Finally the mapping is passed to this
+     * configuration's node handler.
+     */
+    private void initSubHandlers()
+    {
+        Map<Class<?>, NodeHandler<?>> handlers = new HashMap<Class<?>, NodeHandler<?>>(
+                createSubHandlers());
+        handlers.put(CombinedNode.class, COMBINED_NODE_HANDLER);
+        getCombinedNodeHandler().setHandlers(handlers);
+    }
+
+    /**
      * Determines the configuration that owns the specified node.
      *
      * @param node the node
      * @return the owning configuration
      */
-    private Configuration findSourceConfiguration(ConfigurationNode node)
+    private Configuration findSourceConfiguration(Object node)
     {
-        ConfigurationNode root = null;
-        ConfigurationNode current = node;
+        Object root = null;
+        Object current = node;
 
         // find the root node in this hierarchy
         while (current != null)
         {
             root = current;
-            current = current.getParentNode();
+            current = getNodeHandler().getParent(current);
         }
 
         // Check with the root nodes of the child configurations
-        for (ConfigData cd : configurations)
+        for (ConfigData<?> cd : configurations)
         {
             if (root == cd.getRootNode())
             {
@@ -685,13 +741,39 @@
     }
 
     /**
+     * Determines the configuration that owns the specified node in the given
+     * node list.
+     *
+     * @param nl the node list
+     * @param idx the index of the node in question
+     * @return the owning configuration
+     */
+    private Configuration findSourceConfiguration(NodeList<Object> nl, int idx)
+    {
+        return nl.isNode(idx) ? findSourceConfiguration(nl.getNode(idx))
+                : findSourceConfiguration(nl.getAttributeParent(idx));
+    }
+
+    /**
+     * Returns the node handler for dealing with the combined node structure.
+     *
+     * @return the combined node handler
+     */
+    private CombinedConfigurationNodeHandler getCombinedNodeHandler()
+    {
+        return (CombinedConfigurationNodeHandler) getNodeHandler();
+    }
+
+    /**
      * An internal helper class for storing information about contained
      * configurations.
+     *
+     * @param <T> the type of the nodes used by the represented configuration
      */
-    static class ConfigData
+    static class ConfigData<T>
     {
         /** Stores a reference to the configuration. */
-        private AbstractConfiguration configuration;
+        private AbstractHierarchicalConfiguration<T> configuration;
 
         /** Stores the name under which the configuration is stored. */
         private String name;
@@ -699,11 +781,11 @@
         /** Stores the at information as path of nodes. */
         private Collection<String> atPath;
 
-        /** Stores the at string.*/
+        /** Stores the at string. */
         private String at;
 
-        /** Stores the root node for this child configuration.*/
-        private ConfigurationNode rootNode;
+        /** Stores the root node for this child configuration. */
+        private T rootNode;
 
         /**
          * Creates a new instance of <code>ConfigData</code> and initializes
@@ -713,7 +795,8 @@
          * @param n the name
          * @param at the at position
          */
-        public ConfigData(AbstractConfiguration config, String n, String at)
+        public ConfigData(AbstractHierarchicalConfiguration<T> config,
+                String n, String at)
         {
             configuration = config;
             name = n;
@@ -726,7 +809,7 @@
          *
          * @return the configuration
          */
-        public AbstractConfiguration getConfiguration()
+        public AbstractHierarchicalConfiguration<T> getConfiguration()
         {
             return configuration;
         }
@@ -757,7 +840,7 @@
          * @return the root node of this child configuration
          * @since 1.5
          */
-        public ConfigurationNode getRootNode()
+        public T getRootNode()
         {
             return rootNode;
         }
@@ -769,42 +852,29 @@
          *
          * @return the transformed root node
          */
-        public ConfigurationNode getTransformedRoot()
+        public CombinedNode getTransformedRoot()
         {
-            ViewNode result = new ViewNode();
-            ViewNode atParent = result;
+            CombinedNode result = new CombinedNode();
+            CombinedNode atParent = result;
 
             if (atPath != null)
             {
                 // Build the complete path
                 for (String name : atPath)
                 {
-                    ViewNode node = new ViewNode();
+                    CombinedNode node = new CombinedNode();
                     node.setName(name);
-                    atParent.addChild(node);
+                    atParent.addChild(name, node);
                     atParent = node;
                 }
             }
 
-            // Determine root node
-            // TODO This is a workaround as long as there are hierarchical
-            // configurations not derived from AbstractHierarchicalConfiguration.
-            // Remove if refactoring is complete!
-            ConfigurationNode root;
-            if (getConfiguration() instanceof InMemoryConfiguration)
-            {
-                root = ((InMemoryConfiguration) getConfiguration()).getRootNode();
-            }
-            else
-            {
-                HierarchicalConfiguration hc = ConfigurationUtils
-                .convertToHierarchical(getConfiguration());
-                root = hc.getRootNode();
-            }
-            
+            T root = getConfiguration().getRootNode();
+
             // Copy data of the root node to the new path
-            atParent.appendChildren(root);
-            atParent.appendAttributes(root);
+            atParent.appendChildren(root, getConfiguration().getNodeHandler());
+            atParent
+                    .appendAttributes(root, getConfiguration().getNodeHandler());
             rootNode = root;
 
             return result;
@@ -824,7 +894,8 @@
             }
 
             Collection<String> result = new ArrayList<String>();
-            DefaultConfigurationKey.KeyIterator it = new DefaultConfigurationKey(AT_ENGINE, at).iterator();
+            DefaultConfigurationKey.KeyIterator it = new DefaultConfigurationKey(
+                    AT_ENGINE, at).iterator();
             while (it.hasNext())
             {
                 result.add(it.nextKey());

Copied: commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/combined/TestCombinedConfiguration.java (from r636104, commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestCombinedConfiguration.java)
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/combined/TestCombinedConfiguration.java?p2=commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/combined/TestCombinedConfiguration.java&p1=commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestCombinedConfiguration.java&r1=636104&r2=642337&rev=642337&view=diff
==============================================================================
--- commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestCombinedConfiguration.java (original)
+++ commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/combined/TestCombinedConfiguration.java Fri Mar 28 12:06:04 2008
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.configuration2;
+package org.apache.commons.configuration2.combined;
 
 import java.io.File;
 import java.io.FileWriter;
@@ -23,18 +23,16 @@
 import java.util.Collection;
 import java.util.Set;
 
-import org.apache.commons.configuration2.AbstractConfiguration;
-import org.apache.commons.configuration2.CombinedConfiguration;
+import org.apache.commons.configuration2.AbstractHierarchicalConfiguration;
+import org.apache.commons.configuration2.ConfigurationAssert;
+import org.apache.commons.configuration2.ConfigurationException;
 import org.apache.commons.configuration2.ConfigurationRuntimeException;
-import org.apache.commons.configuration2.HierarchicalConfiguration;
-import org.apache.commons.configuration2.PropertiesConfiguration;
+import org.apache.commons.configuration2.InMemoryConfiguration;
 import org.apache.commons.configuration2.StrictConfigurationComparator;
 import org.apache.commons.configuration2.XMLConfiguration;
 import org.apache.commons.configuration2.event.ConfigurationEvent;
 import org.apache.commons.configuration2.event.ConfigurationListener;
 import org.apache.commons.configuration2.reloading.FileAlwaysReloadingStrategy;
-import org.apache.commons.configuration2.tree.NodeCombiner;
-import org.apache.commons.configuration2.tree.UnionCombiner;
 
 import junit.framework.Assert;
 import junit.framework.TestCase;
@@ -47,23 +45,24 @@
 public class TestCombinedConfiguration extends TestCase
 {
     /** Constant for the name of a sub configuration. */
-    static final String TEST_NAME = "SUBCONFIG";
+    private static final String TEST_NAME = "SUBCONFIG";
 
     /** Constant for a test key. */
-    static final String TEST_KEY = "test.value";
+    private static final String TEST_KEY = "test.value";
 
     /** Constant for the name of the first child configuration.*/
-    static final String CHILD1 = TEST_NAME + "1";
+    private static final String CHILD1 = TEST_NAME + "1";
 
     /** Constant for the name of the second child configuration.*/
-    static final String CHILD2 = TEST_NAME + "2";
+    private static final String CHILD2 = TEST_NAME + "2";
 
     /** The configuration to be tested. */
-    CombinedConfiguration config;
+    private CombinedConfiguration config;
 
     /** The test event listener. */
-    CombinedListener listener;
+    private CombinedListener listener;
 
+    @Override
     protected void setUp() throws Exception
     {
         super.setUp();
@@ -92,7 +91,7 @@
      */
     public void testAddConfiguration()
     {
-        AbstractConfiguration c = setUpTestConfiguration();
+        AbstractHierarchicalConfiguration<?> c = setUpTestConfiguration();
         config.addConfiguration(c);
         checkAddConfig(c);
         assertEquals("Wrong number of configs", 1, config
@@ -109,7 +108,7 @@
      */
     public void testAddConfigurationWithName()
     {
-        AbstractConfiguration c = setUpTestConfiguration();
+        AbstractHierarchicalConfiguration<?> c = setUpTestConfiguration();
         config.addConfiguration(c, TEST_NAME);
         checkAddConfig(c);
         assertEquals("Wrong number of configs", 1, config
@@ -148,7 +147,7 @@
      */
     public void testAddConfigurationAt()
     {
-        AbstractConfiguration c = setUpTestConfiguration();
+        AbstractHierarchicalConfiguration<?> c = setUpTestConfiguration();
         config.addConfiguration(c, null, "my");
         checkAddConfig(c);
         assertTrue("Wrong property value", config.getBoolean("my." + TEST_KEY));
@@ -160,7 +159,7 @@
      */
     public void testAddConfigurationComplexAt()
     {
-        AbstractConfiguration c = setUpTestConfiguration();
+        AbstractHierarchicalConfiguration<?> c = setUpTestConfiguration();
         config.addConfiguration(c, null, "This..is.a.complex");
         checkAddConfig(c);
         assertTrue("Wrong property value", config
@@ -172,7 +171,7 @@
      *
      * @param c the config to check
      */
-    private void checkAddConfig(AbstractConfiguration c)
+    private void checkAddConfig(AbstractHierarchicalConfiguration<?> c)
     {
         Collection<?> listeners = c.getConfigurationListeners();
         assertEquals("Wrong number of configuration listeners", 1, listeners
@@ -227,7 +226,7 @@
      */
     public void testRemoveConfiguration()
     {
-        AbstractConfiguration c = setUpTestConfiguration();
+        AbstractHierarchicalConfiguration<?> c = setUpTestConfiguration();
         config.addConfiguration(c);
         checkAddConfig(c);
         assertTrue("Config could not be removed", config.removeConfiguration(c));
@@ -239,7 +238,7 @@
      */
     public void testRemoveConfigurationAt()
     {
-        AbstractConfiguration c = setUpTestConfiguration();
+        AbstractHierarchicalConfiguration<?> c = setUpTestConfiguration();
         config.addConfiguration(c);
         assertSame("Wrong config removed", c, config.removeConfigurationAt(0));
         checkRemoveConfig(c);
@@ -250,7 +249,7 @@
      */
     public void testRemoveConfigurationByName()
     {
-        AbstractConfiguration c = setUpTestConfiguration();
+        AbstractHierarchicalConfiguration<?> c = setUpTestConfiguration();
         config.addConfiguration(c, TEST_NAME);
         assertSame("Wrong config removed", c, config
                 .removeConfiguration(TEST_NAME));
@@ -262,7 +261,7 @@
      */
     public void testRemoveNamedConfiguration()
     {
-        AbstractConfiguration c = setUpTestConfiguration();
+        AbstractHierarchicalConfiguration<?> c = setUpTestConfiguration();
         config.addConfiguration(c, TEST_NAME);
         config.removeConfiguration(c);
         checkRemoveConfig(c);
@@ -273,7 +272,7 @@
      */
     public void testRemoveNamedConfigurationAt()
     {
-        AbstractConfiguration c = setUpTestConfiguration();
+        AbstractHierarchicalConfiguration<?> c = setUpTestConfiguration();
         config.addConfiguration(c, TEST_NAME);
         assertSame("Wrong config removed", c, config.removeConfigurationAt(0));
         checkRemoveConfig(c);
@@ -304,7 +303,7 @@
      *
      * @param c the removed configuration
      */
-    private void checkRemoveConfig(AbstractConfiguration c)
+    private void checkRemoveConfig(AbstractHierarchicalConfiguration<?> c)
     {
         assertTrue("Listener was not removed", c.getConfigurationListeners()
                 .isEmpty());
@@ -321,7 +320,7 @@
      */
     public void testUpdateContainedConfiguration()
     {
-        AbstractConfiguration c = setUpTestConfiguration();
+        AbstractHierarchicalConfiguration<?> c = setUpTestConfiguration();
         config.addConfiguration(c);
         c.addProperty("test.otherTest", "yes");
         assertEquals("New property not found", "yes", config
@@ -364,7 +363,6 @@
     {
         config.addConfiguration(setUpTestConfiguration());
         config.addConfiguration(setUpTestConfiguration(), TEST_NAME, "conf2");
-        config.addConfiguration(new PropertiesConfiguration(), "props");
 
         CombinedConfiguration cc2 = (CombinedConfiguration) config.clone();
         assertEquals("Wrong number of contained configurations", config
@@ -431,29 +429,28 @@
         config.setForceReloadCheck(true);
         File testDir = new File("target");
         File testXmlFile = new File(testDir, "reload.xml");
-        File testPropsFile = new File(testDir, "reload.properties");
+        File testXmlFile2 = new File(testDir, "reload2.xml");
         writeFile(testXmlFile, "<xml><xmlReload>0</xmlReload></xml>");
-        writeFile(testPropsFile, "propsReload = 0");
+        writeFile(testXmlFile2, "<xml><xmlReload2>0</xmlReload2></xml>");
         XMLConfiguration c1 = new XMLConfiguration(testXmlFile);
         c1.setReloadingStrategy(new FileAlwaysReloadingStrategy());
-        PropertiesConfiguration c2 = new PropertiesConfiguration(testPropsFile);
+        XMLConfiguration c2 = new XMLConfiguration(testXmlFile2);
         c2.setThrowExceptionOnMissing(true);
         c2.setReloadingStrategy(new FileAlwaysReloadingStrategy());
         config.addConfiguration(c1);
         config.addConfiguration(c2);
-        assertEquals("Wrong xml reload value", 0, config.getInt("xmlReload"));
-        assertEquals("Wrong props reload value", 0, config
-                .getInt("propsReload"));
+        assertEquals("Wrong xml 1 reload value", 0, config.getInt("xmlReload"));
+        assertEquals("Wrong xml 2 reload value", 0, config.getInt("xmlReload2"));
 
         writeFile(testXmlFile, "<xml><xmlReload>1</xmlReload></xml>");
-        assertEquals("XML reload not detected", 1, config.getInt("xmlReload"));
+        assertEquals("XML reload 1 not detected", 1, config.getInt("xmlReload"));
         config.setForceReloadCheck(false);
-        writeFile(testPropsFile, "propsReload = 1");
-        assertEquals("Props reload detected though check flag is false", 0, config
-                .getInt("propsReload"));
+        writeFile(testXmlFile2, "<xml><xmlReload2>1</xmlReload2></xml>");
+        assertEquals("XML 2 reload detected though check flag is false", 0, config
+                .getInt("xmlReload2"));
 
         assertTrue("XML file cannot be removed", testXmlFile.delete());
-        assertTrue("Props file cannot be removed", testPropsFile.delete());
+        assertTrue("Props file cannot be removed", testXmlFile2.delete());
     }
 
     /**
@@ -461,8 +458,8 @@
      */
     private void setUpSourceTest()
     {
-        HierarchicalConfiguration c1 = new HierarchicalConfiguration();
-        PropertiesConfiguration c2 = new PropertiesConfiguration();
+        InMemoryConfiguration c1 = new InMemoryConfiguration();
+        InMemoryConfiguration c2 = new InMemoryConfiguration();
         c1.addProperty(TEST_KEY, TEST_NAME);
         c2.addProperty("another.key", "test");
         config.addConfiguration(c1, CHILD1);
@@ -470,23 +467,13 @@
     }
 
     /**
-     * Tests the gestSource() method when the source property is defined in a
-     * hierarchical configuration.
+     * Tests the gestSource() method when the source property is defined.
      */
-    public void testGetSourceHierarchical()
+    public void testGetSourceContained()
     {
         setUpSourceTest();
-        assertEquals("Wrong source configuration", config
+        assertEquals("Wrong source configuration 1", config
                 .getConfiguration(CHILD1), config.getSource(TEST_KEY));
-    }
-
-    /**
-     * Tests whether the source configuration can be detected for non
-     * hierarchical configurations.
-     */
-    public void testGetSourceNonHierarchical()
-    {
-        setUpSourceTest();
         assertEquals("Wrong source configuration", config
                 .getConfiguration(CHILD2), config.getSource("another.key"));
     }
@@ -570,7 +557,7 @@
      */
     public void testEscapeListDelimiters()
     {
-        PropertiesConfiguration sub = new PropertiesConfiguration();
+        InMemoryConfiguration sub = new InMemoryConfiguration();
         sub.addProperty("test.pi", "3\\,1415");
         config.addConfiguration(sub);
         assertEquals("Wrong value", "3,1415", config.getString("test.pi"));
@@ -592,6 +579,23 @@
     }
 
     /**
+     * Tests a combined configuration that is contained in another combined
+     * configuration.
+     */
+    public void testNestedCombinedConfiguration() throws ConfigurationException
+    {
+        CombinedConfiguration cc = new CombinedConfiguration();
+        XMLConfiguration xmlConf = new XMLConfiguration(ConfigurationAssert
+                .getTestFile("test.xml"));
+        cc.addConfiguration(xmlConf);
+        config.addConfiguration(cc);
+        config.addConfiguration(setUpTestConfiguration());
+        assertTrue("Test key not found", config.getBoolean(TEST_KEY));
+        assertEquals("Key in cc not found", "foo", config
+                .getString("element3[@name]"));
+    }
+
+    /**
      * Helper method for writing a file.
      *
      * @param file the file to be written
@@ -621,9 +625,9 @@
      *
      * @return the test configuration
      */
-    private AbstractConfiguration setUpTestConfiguration()
+    private AbstractHierarchicalConfiguration<?> setUpTestConfiguration()
     {
-        HierarchicalConfiguration config = new HierarchicalConfiguration();
+        InMemoryConfiguration config = new InMemoryConfiguration();
         config.addProperty(TEST_KEY, Boolean.TRUE);
         config.addProperty("test.comment", "This is a test");
         return config;