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 2014/03/17 22:12:13 UTC

svn commit: r1578582 - in /commons/proper/configuration/branches/immutableNodes/src: main/java/org/apache/commons/configuration/ test/java/org/apache/commons/configuration/

Author: oheger
Date: Mon Mar 17 21:12:13 2014
New Revision: 1578582

URL: http://svn.apache.org/r1578582
Log:
Implemented configurationAt() in BaseHierarchicalConfiguration.

The method now either creates a BaseHierarchicalConfiguration initialized with
the selected root node or a SubnodeConfiguration with a corresponding
NodeSelector. Adapted test classes.

Modified:
    commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/BaseHierarchicalConfiguration.java
    commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/SubnodeConfiguration.java
    commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/TestHierarchicalConfiguration.java
    commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/TestSubnodeConfiguration.java

Modified: commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/BaseHierarchicalConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/BaseHierarchicalConfiguration.java?rev=1578582&r1=1578581&r2=1578582&view=diff
==============================================================================
--- commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/BaseHierarchicalConfiguration.java (original)
+++ commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/BaseHierarchicalConfiguration.java Mon Mar 17 21:12:13 2014
@@ -28,13 +28,16 @@ import java.util.WeakHashMap;
 
 import org.apache.commons.configuration.event.ConfigurationEvent;
 import org.apache.commons.configuration.event.ConfigurationListener;
+import org.apache.commons.configuration.ex.ConfigurationRuntimeException;
 import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
 import org.apache.commons.configuration.tree.ConfigurationNode;
 import org.apache.commons.configuration.tree.ConfigurationNodeVisitorAdapter;
 import org.apache.commons.configuration.tree.ImmutableNode;
 import org.apache.commons.configuration.tree.InMemoryNodeModel;
 import org.apache.commons.configuration.tree.NodeModel;
+import org.apache.commons.configuration.tree.NodeSelector;
 import org.apache.commons.configuration.tree.QueryResult;
+import org.apache.commons.configuration.tree.TrackedNodeModel;
 
 /**
  * <p>
@@ -221,73 +224,90 @@ public class BaseHierarchicalConfigurati
     }
 
     /**
-     * <p>
-     * Returns a hierarchical subnode configuration object that wraps the
-     * configuration node specified by the given key. This method provides an
-     * easy means of accessing sub trees of a hierarchical configuration. In the
-     * returned configuration the sub tree can directly be accessed, it becomes
-     * the root node of this configuration. Because of this the passed in key
-     * must select exactly one configuration node; otherwise an
-     * {@code IllegalArgumentException} will be thrown.
-     * </p>
-     * <p>
-     * The difference between this method and the
-     * {@link #subset(String)} method is that
-     * {@code subset()} supports arbitrary subsets of configuration nodes
-     * while {@code configurationAt()} only returns a single sub tree.
-     * Please refer to the documentation of the
-     * {@code SubnodeConfiguration} class to obtain further information
-     * about subnode configurations and when they should be used.
-     * </p>
-     * <p>
-     * With the {@code supportUpdate} flag the behavior of the returned
-     * {@code SubnodeConfiguration} regarding updates of its parent
-     * configuration can be determined. A subnode configuration operates on the
-     * same nodes as its parent, so changes at one configuration are normally
-     * directly visible for the other configuration. There are however changes
-     * of the parent configuration, which are not recognized by the subnode
-     * configuration per default. An example for this is a reload operation (for
-     * file-based configurations): Here the complete node set of the parent
-     * configuration is replaced, but the subnode configuration still references
-     * the old nodes. If such changes should be detected by the subnode
-     * configuration, the {@code supportUpdates} flag must be set to
-     * <b>true</b>. This causes the subnode configuration to reevaluate the key
-     * used for its creation each time it is accessed. This guarantees that the
-     * subnode configuration always stays in sync with its key, even if the
-     * parent configuration's data significantly changes. If such a change
-     * makes the key invalid - because it now no longer points to exactly one
-     * node -, the subnode configuration is not reconstructed, but keeps its
-     * old data. It is then quasi detached from its parent.
-     * </p>
-     *
-     * @param key the key that selects the sub tree
-     * @param supportUpdates a flag whether the returned subnode configuration
-     * should be able to handle updates of its parent
-     * @return a hierarchical configuration that contains this sub tree
+     * {@inheritDoc} The result of this implementation depends on the
+     * {@code supportUpdates} flag: If it is <b>false</b>, a plain
+     * {@code BaseHierarchicalConfiguration} is returned using the selected node
+     * as root node. This is suitable for read-only access to properties.
+     * Because the configuration returned in this case is not connected to the
+     * parent configuration, updates on properties made by one configuration are
+     * not reflected by the other one. A value of <b>true</b> for this parameter
+     * causes a tracked node to be created, and result is a
+     * {@link SubnodeConfiguration} based on this tracked node. This
+     * configuration is really connected to its parent, so that updated
+     * properties are visible on both.
+     *
      * @see SubnodeConfiguration
-     * @since 1.5
+     * @throws ConfigurationRuntimeException if the key does not select a single
+     *         node
      */
-    public SubnodeConfiguration configurationAt(String key,
+    public HierarchicalConfiguration<ImmutableNode> configurationAt(String key,
             boolean supportUpdates)
     {
-        beginWrite(false);
-        try
+        BaseHierarchicalConfiguration sub =
+                supportUpdates ? createConnectedSubConfiguration(key)
+                        : createIndependentSubConfiguration(key);
+        initSubConfiguration(sub);
+        return sub;
+    }
+
+    /**
+     * Creates a sub configuration from the specified key which is connected to
+     * this configuration. This implementation creates a
+     * {@link SubnodeConfiguration} with a tracked node identified by the passed
+     * in key.
+     *
+     * @param key the key of the sub configuration
+     * @return the new sub configuration
+     */
+    private BaseHierarchicalConfiguration createConnectedSubConfiguration(
+            String key)
+    {
+        InMemoryNodeModel myModel = (InMemoryNodeModel) getModel();
+        NodeSelector selector = new NodeSelector(key);
+        myModel.trackNode(selector, this);
+        return new SubnodeConfiguration(this, new TrackedNodeModel(myModel,
+                selector, true), selector);
+    }
+
+    /**
+     * Creates a sub configuration from the specified key which is independent
+     * on this configuration. This means that the sub configuration operates on
+     * a separate node model (although the nodes are initially shared).
+     *
+     * @param key the key of the sub configuration
+     * @return the new sub configuration
+     */
+    private BaseHierarchicalConfiguration createIndependentSubConfiguration(
+            String key)
+    {
+        List<ImmutableNode> targetNodes = fetchFilteredNodeResults(key);
+        if (targetNodes.size() != 1)
         {
-//            List<ConfigurationNode> nodes = fetchNodeList(key);
-//            if (nodes.size() != 1)
-//            {
-//                throw new IllegalArgumentException(
-//                        "Passed in key must select exactly one node: " + key);
-//            }
-//            return createAndInitializeSubnodeConfiguration(nodes.get(0), key,
-//                    supportUpdates);
-            //TODO implementation
-            return null;
+            throw new ConfigurationRuntimeException(
+                    "Passed in key must select exactly one node: " + key);
         }
-        finally
+        return new BaseHierarchicalConfiguration(new InMemoryNodeModel(
+                targetNodes.get(0)));
+    }
+
+    /**
+     * Executes a query on the specified key and filters it for node results.
+     *
+     * @param key the key
+     * @return the filtered list with result nodes
+     */
+    private List<ImmutableNode> fetchFilteredNodeResults(String key)
+    {
+        List<QueryResult<ImmutableNode>> results = fetchNodeList(key);
+        List<ImmutableNode> targetNodes = new LinkedList<ImmutableNode>();
+        for (QueryResult<ImmutableNode> result : results)
         {
-            endWrite();
+            if (!result.isAttributeResult())
+            {
+                targetNodes.add(result.getNode());
+            }
         }
+        return targetNodes;
     }
 
     /**
@@ -303,16 +323,11 @@ public class BaseHierarchicalConfigurati
     }
 
     /**
-     * Returns a hierarchical subnode configuration for the node specified by
-     * the given key. This is a short form for {@code configurationAt(key,
+     * {@inheritDoc} This is a short form for {@code configurationAt(key,
      * <b>false</b>)}.
-     *
-     * @param key the key that selects the sub tree
-     * @return a hierarchical configuration that contains this sub tree
-     * @see SubnodeConfiguration
-     * @since 1.3
+     * @throws ConfigurationRuntimeException if the key does not select a single node
      */
-    public SubnodeConfiguration configurationAt(String key)
+    public HierarchicalConfiguration<ImmutableNode> configurationAt(String key)
     {
         return configurationAt(key, false);
     }
@@ -321,6 +336,7 @@ public class BaseHierarchicalConfigurati
      * {@inheritDoc} This implementation creates a {@code SubnodeConfiguration}
      * by delegating to {@code configurationAt()}. Then an immutable wrapper
      * is created and returned.
+     * @throws ConfigurationRuntimeException if the key does not select a single node
      */
     public ImmutableHierarchicalConfiguration immutableConfigurationAt(
             String key)
@@ -330,31 +346,8 @@ public class BaseHierarchicalConfigurati
     }
 
     /**
-     * Returns a list of sub configurations for all configuration nodes selected
-     * by the given key. This method will evaluate the passed in key (using the
-     * current {@code ExpressionEngine}) and then create a subnode
-     * configuration for each returned node (like
-     * {@link #configurationAt(String)}}). This is especially
-     * useful when dealing with list-like structures. As an example consider the
-     * configuration that contains data about database tables and their fields.
-     * If you need access to all fields of a certain table, you can simply do
-     *
-     * <pre>
-     * List fields = config.configurationsAt("tables.table(0).fields.field");
-     * for(Iterator it = fields.iterator(); it.hasNext();)
-     * {
-     *     BaseHierarchicalConfiguration sub = (BaseHierarchicalConfiguration) it.next();
-     *     // now the children and attributes of the field node can be
-     *     // directly accessed
-     *     String fieldName = sub.getString("name");
-     *     String fieldType = sub.getString("type");
-     *     ...
-     * </pre>
-     *
-     * @param key the key for selecting the desired nodes
-     * @return a list with hierarchical configuration objects; each
-     * configuration represents one of the nodes selected by the passed in key
-     * @since 1.3
+     * {@inheritDoc} This implementation creates sub configurations in the same way as
+     * described for {@link #configurationAt(String)}.
      */
     public List<HierarchicalConfiguration<ImmutableNode>> configurationsAt(String key)
     {
@@ -505,6 +498,22 @@ public class BaseHierarchicalConfigurati
     }
 
     /**
+     * Initializes properties of a sub configuration. A sub configuration
+     * inherits some settings from its parent, e.g. the expression engine or the
+     * synchronizer. The corresponding values are copied by this method.
+     *
+     * @param sub the sub configuration to be initialized
+     */
+    private void initSubConfiguration(BaseHierarchicalConfiguration sub)
+    {
+        sub.setSynchronizer(getSynchronizer());
+        sub.setExpressionEngine(getExpressionEngine());
+        sub.setListDelimiterHandler(getListDelimiterHandler());
+        sub.setThrowExceptionOnMissing(isThrowExceptionOnMissing());
+        sub.getInterpolator().setParentInterpolator(getInterpolator());
+    }
+
+    /**
      * Initializes the data related to the management of
      * {@code SubnodeConfiguration} instances. This method is called each time a
      * new {@code SubnodeConfiguration} was created. A configuration and its

Modified: commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/SubnodeConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/SubnodeConfiguration.java?rev=1578582&r1=1578581&r2=1578582&view=diff
==============================================================================
--- commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/SubnodeConfiguration.java (original)
+++ commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/SubnodeConfiguration.java Mon Mar 17 21:12:13 2014
@@ -149,8 +149,6 @@ public class SubnodeConfiguration extend
 
         this.parent = parent;
         rootSelector = selector;
-        initFromParent(parent);
-        initInterpolator();
     }
 
     /**
@@ -215,28 +213,4 @@ public class SubnodeConfiguration extend
         //TODO implementation
         throw new UnsupportedOperationException("Not yet implemented!");
     }
-
-    /**
-     * Initializes this subnode configuration from the given parent
-     * configuration. This method is called by the constructor. It will copy
-     * many settings from the parent.
-     *
-     * @param parentConfig the parent configuration
-     */
-    private void initFromParent(BaseHierarchicalConfiguration parentConfig)
-    {
-        setExpressionEngine(parentConfig.getExpressionEngine());
-        setListDelimiterHandler(parentConfig.getListDelimiterHandler());
-        setThrowExceptionOnMissing(parentConfig.isThrowExceptionOnMissing());
-    }
-
-    /**
-     * Initializes the {@code ConfigurationInterpolator} for this sub configuration.
-     * This is a standard {@code ConfigurationInterpolator} which also references
-     * the {@code ConfigurationInterpolator} of the parent configuration.
-     */
-    private void initInterpolator()
-    {
-        getInterpolator().setParentInterpolator(getParent().getInterpolator());
-    }
 }

Modified: commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/TestHierarchicalConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/TestHierarchicalConfiguration.java?rev=1578582&r1=1578581&r2=1578582&view=diff
==============================================================================
--- commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/TestHierarchicalConfiguration.java (original)
+++ commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/TestHierarchicalConfiguration.java Mon Mar 17 21:12:13 2014
@@ -26,6 +26,7 @@ import java.util.List;
 
 import org.apache.commons.configuration.convert.DefaultListDelimiterHandler;
 import org.apache.commons.configuration.event.ConfigurationListener;
+import org.apache.commons.configuration.ex.ConfigurationRuntimeException;
 import org.apache.commons.configuration.tree.DefaultConfigurationKey;
 import org.apache.commons.configuration.tree.DefaultExpressionEngine;
 import org.apache.commons.configuration.tree.ImmutableNode;
@@ -90,7 +91,13 @@ public class TestHierarchicalConfigurati
         subset = config.subset("tables.table.fields.field");
         prop = subset.getProperty("name");
         assertTrue("prop is not a collection", prop instanceof Collection);
-        assertEquals(10, ((Collection<?>) prop).size());
+        int expectedFieldCount = 0;
+        for (int i = 0; i < NodeStructureHelper.tablesLength(); i++)
+        {
+            expectedFieldCount += NodeStructureHelper.fieldsLength(i);
+        }
+        assertEquals("Wrong number of fields", expectedFieldCount,
+                ((Collection<?>) prop).size());
 
         assertEquals(NodeStructureHelper.field(0, 0), subset.getProperty("name(0)"));
 
@@ -147,13 +154,13 @@ public class TestHierarchicalConfigurati
     }
 
     /**
-     * Tests the configurationAt() method to obtain a configuration for a sub
-     * tree.
+     * Tests whether a configuration obtained via configurationAt() contains the
+     * expected properties.
      */
     @Test
-    public void testConfigurationAt()
+    public void testConfigurationAtReadAccess()
     {
-        BaseHierarchicalConfiguration subConfig =
+        HierarchicalConfiguration<ImmutableNode> subConfig =
                 config.configurationAt("tables.table(1)");
         assertEquals("Wrong table name", NodeStructureHelper.table(1),
                 subConfig.getString("name"));
@@ -165,13 +172,65 @@ public class TestHierarchicalConfigurati
             assertEquals("Wrong field at position " + i,
                     NodeStructureHelper.field(1, i), lstFlds.get(i));
         }
+    }
+
+    /**
+     * Tests an update operation on a sub configuration which is independent on
+     * its parent.
+     */
+    @Test
+    public void testConfigurationAtUpdateSubConfigIndependent()
+    {
+        HierarchicalConfiguration<ImmutableNode> subConfig =
+                config.configurationAt("tables.table(1)");
+        subConfig.setProperty("name", "testTable");
+        assertEquals("Value not changed", "testTable",
+                subConfig.getString("name"));
+        assertEquals("Change visible in parent", NodeStructureHelper.table(1),
+                config.getString("tables.table(1).name"));
+    }
+
+    /**
+     * Tests an update operation on a parent configuration if the sub
+     * configuration is independent.
+     */
+    @Test
+    public void testConfigurationAtUpdateParentIndependent()
+    {
+        HierarchicalConfiguration<ImmutableNode> subConfig =
+                config.configurationAt("tables.table(1)");
+        config.setProperty("tables.table(1).fields.field(2).name", "testField");
+        assertEquals("Change visible in sub config",
+                NodeStructureHelper.field(1, 2),
+                subConfig.getString("fields.field(2).name"));
+    }
 
+    /**
+     * Tests an update operation on a sub configuration which is connected to
+     * its parent.
+     */
+    @Test
+    public void testConfigurationAtUpdateSubConfigConnected()
+    {
+        HierarchicalConfiguration<ImmutableNode> subConfig =
+                config.configurationAt("tables.table(1)", true);
         subConfig.setProperty("name", "testTable");
-        assertEquals("Change not visible in parent", "testTable", config
-                .getString("tables.table(1).name"));
+        assertEquals("Change not visible in parent", "testTable",
+                config.getString("tables.table(1).name"));
+    }
+
+    /**
+     * Tests an update operation on a parent configuration if the sub
+     * configuration is connected.
+     */
+    @Test
+    public void testConfigurationAtUpdateParentConnected()
+    {
+        HierarchicalConfiguration<ImmutableNode> subConfig =
+                config.configurationAt("tables.table(1)", true);
         config.setProperty("tables.table(1).fields.field(2).name", "testField");
-        assertEquals("Change not visible in sub config", "testField", subConfig
-                .getString("fields.field(2).name"));
+        assertEquals("Change visible in sub config", "testField",
+                subConfig.getString("fields.field(2).name"));
     }
 
     /**
@@ -211,41 +270,71 @@ public class TestHierarchicalConfigurati
     }
 
     /**
-     * Tests the configurationAt() method when the passed in key does not exist.
+     * Tests the configurationAt() method if the passed in key does not exist.
      */
-    @Test(expected = IllegalArgumentException.class)
+    @Test(expected = ConfigurationRuntimeException.class)
     public void testConfigurationAtUnknownSubTree()
     {
         config.configurationAt("non.existing.key");
     }
 
     /**
-     * Tests the configurationAt() method when the passed in key selects
+     * Tests configurationAt() for a non existing key if the update flag is set.
+     */
+    @Test(expected = ConfigurationRuntimeException.class)
+    public void testConfigurationAtUnknownSubTreeWithUpdates()
+    {
+        config.configurationAt("non.existing.key", true);
+    }
+
+    /**
+     * Tests the configurationAt() method if the passed in key selects
      * multiple nodes. This should cause an exception.
      */
-    @Test(expected = IllegalArgumentException.class)
+    @Test(expected = ConfigurationRuntimeException.class)
     public void testConfigurationAtMultipleNodes()
     {
         config.configurationAt("tables.table.name");
     }
 
     /**
-     * Tests whether a sub configuration obtained by configurationAt() can be
-     * cleared.
+     * Tests configurationAt() if the passed in key selects multiple nodes and the
+     * update flag is set.
      */
-    @Test
-    public void testConfigurationAtClear()
+    @Test(expected = ConfigurationRuntimeException.class)
+    public void testConfigurationAtMultipleNodesWithUpdates()
     {
-        config.addProperty("test.sub.test", "fail");
-        assertEquals("Wrong index (1)", 0, config.getMaxIndex("test"));
-        SubnodeConfiguration sub = config.configurationAt("test.sub");
-        assertEquals("Wrong value", "fail", sub.getString("test"));
-        sub.clear();
-        assertNull("Key still found", config.getString("test.sub.test"));
-        sub.setProperty("test", "success");
-        assertEquals("Property not set", "success",
-                config.getString("test.sub.test"));
-        assertEquals("Wrong index (2)", 0, config.getMaxIndex("test"));
+        config.configurationAt("tables.table.name", true);
+    }
+
+    /**
+     * Checks configurationAt() if the passed in key selects an attribute.
+     * @param withUpdates the updates flag
+     */
+    private void checkConfigurationAtAttributeNode(boolean withUpdates)
+    {
+        final String key = "tables.table(0)[@type]";
+        config.addProperty(key, "system");
+        config.configurationAt(key, withUpdates);
+    }
+
+    /**
+     * Tests configurationAt() if the passed in key selects an attribute result.
+     */
+    @Test(expected = ConfigurationRuntimeException.class)
+    public void testConfigurationAtAttributeNode()
+    {
+        checkConfigurationAtAttributeNode(false);
+    }
+
+    /**
+     * Tests configurationAt() if the passed in key selects an attribute result and the
+     * updates flag is set.
+     */
+    @Test(expected = ConfigurationRuntimeException.class)
+    public void testConfigurationAtAttributeNodeWithUpdates()
+    {
+        checkConfigurationAtAttributeNode(true);
     }
 
     /**
@@ -257,8 +346,9 @@ public class TestHierarchicalConfigurati
     {
         config.addProperty("test.sub.test", "success");
         config.addProperty("test.other", "check");
-        SubnodeConfiguration sub = config.configurationAt("test.sub");
-        sub.clearAndDetachFromParent();
+        HierarchicalConfiguration<ImmutableNode> sub =
+                config.configurationAt("test.sub", true);
+        sub.clear();
         assertTrue("Sub not empty", sub.isEmpty());
         assertNull("Key still found", config.getString("test.sub.test"));
         sub.setProperty("test", "failure!");
@@ -290,7 +380,7 @@ public class TestHierarchicalConfigurati
     @Test
     public void testConfigurationsAt()
     {
-        List<SubnodeConfiguration> lstFlds =
+        List<HierarchicalConfiguration<ImmutableNode>> lstFlds =
                 config.configurationsAt("tables.table(1).fields.field");
         checkSubConfigurations(lstFlds);
     }
@@ -426,10 +516,10 @@ public class TestHierarchicalConfigurati
     @Test
     public void testChildConfigurationsAt()
     {
-        List<SubnodeConfiguration> children =
+        List<HierarchicalConfiguration<ImmutableNode>> children =
                 config.childConfigurationsAt("tables.table(0)");
         assertEquals("Wrong number of elements", 2, children.size());
-        SubnodeConfiguration sub = children.get(0);
+        HierarchicalConfiguration<ImmutableNode> sub = children.get(0);
         String newTabName = "otherTabe";
         sub.setProperty(null, newTabName);
         assertEquals("Table name not changed", newTabName,

Modified: commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/TestSubnodeConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/TestSubnodeConfiguration.java?rev=1578582&r1=1578581&r2=1578582&view=diff
==============================================================================
--- commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/TestSubnodeConfiguration.java (original)
+++ commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/TestSubnodeConfiguration.java Mon Mar 17 21:12:13 2014
@@ -106,18 +106,17 @@ public class TestSubnodeConfiguration
      */
     private void setUpSubnodeConfig()
     {
-        setUpSubnodeConfig(SELECTOR);
+        setUpSubnodeConfig(SUB_KEY);
     }
 
     /**
-     * Initializes the test configuration using the specified selector.
+     * Initializes the test configuration using the specified key.
      *
-     * @param selector the selector
+     * @param key the key
      */
-    private void setUpSubnodeConfig(NodeSelector selector)
+    private void setUpSubnodeConfig(String key)
     {
-        TrackedNodeModel subModel = setUpTrackedModel(selector);
-        config = new SubnodeConfiguration(parent, subModel, selector);
+        config = (SubnodeConfiguration) parent.configurationAt(key, true);
     }
 
     /**
@@ -314,7 +313,7 @@ public class TestSubnodeConfiguration
     public void testSetExpressionEngine()
     {
         parent.setExpressionEngine(new XPathExpressionEngine());
-        setUpSubnodeConfig(new NodeSelector("tables/table[1]"));
+        setUpSubnodeConfig("tables/table[1]");
         assertEquals("Wrong field name", NodeStructureHelper.field(0, 1),
                 config.getString("fields/field[2]/name"));
         Set<String> keys = new HashSet<String>();
@@ -328,13 +327,30 @@ public class TestSubnodeConfiguration
     }
 
     /**
-     * Tests the configurationAt() method.
+     * Tests the configurationAt() method if updates are not supported.
+     */
+    @Test
+    public void testConfiguarationAtNoUpdates()
+    {
+        setUpSubnodeConfig();
+        HierarchicalConfiguration<ImmutableNode> sub2 =
+                config.configurationAt("fields.field(1)");
+        assertEquals("Wrong value of property",
+                NodeStructureHelper.field(0, 1), sub2.getString("name"));
+        parent.setProperty("tables.table(0).fields.field(1).name", "otherName");
+        assertEquals("Change of parent is visible",
+                NodeStructureHelper.field(0, 1), sub2.getString("name"));
+    }
+
+    /**
+     * Tests configurationAt() if updates are supported.
      */
     @Test
-    public void testConfiguarationAt()
+    public void testConfigurationAtWithUpdateSupport()
     {
         setUpSubnodeConfig();
-        SubnodeConfiguration sub2 = config.configurationAt("fields.field(1)");
+        SubnodeConfiguration sub2 =
+                (SubnodeConfiguration) config.configurationAt("fields.field(1)", true);
         assertEquals("Wrong value of property",
                 NodeStructureHelper.field(0, 1), sub2.getString("name"));
         assertEquals("Wrong parent", config.getParent(), sub2.getParent());
@@ -360,28 +376,50 @@ public class TestSubnodeConfiguration
     }
 
     /**
-     * An additional test for interpolation when the configurationAt() method is
-     * involved.
+     * Helper method for testing interpolation facilities between a sub and its
+     * parent configuration.
+     *
+     * @param withUpdates the supports updates flag
      */
-    @Test
-    public void testInterpolationFromConfigurationAt()
+    private void checkInterpolationFromConfigurationAt(boolean withUpdates)
     {
         parent.addProperty("base.dir", "/home/foo");
         parent.addProperty("test.absolute.dir.dir1", "${base.dir}/path1");
         parent.addProperty("test.absolute.dir.dir2", "${base.dir}/path2");
         parent.addProperty("test.absolute.dir.dir3", "${base.dir}/path3");
 
-        Configuration sub = parent.configurationAt("test.absolute.dir");
+        Configuration sub =
+                parent.configurationAt("test.absolute.dir", withUpdates);
         for (int i = 1; i < 4; i++)
         {
             assertEquals("Wrong interpolation in parent", "/home/foo/path" + i,
                     parent.getString("test.absolute.dir.dir" + i));
-            assertEquals("Wrong interpolation in subnode",
-                    "/home/foo/path" + i, sub.getString("dir" + i));
+            assertEquals("Wrong interpolation in sub", "/home/foo/path" + i,
+                    sub.getString("dir" + i));
         }
     }
 
     /**
+     * Tests whether interpolation works for a sub configuration obtained via
+     * configurationAt() if updates are not supported.
+     */
+    @Test
+    public void testInterpolationFromConfigurationAtNoUpdateSupport()
+    {
+        checkInterpolationFromConfigurationAt(false);
+    }
+
+    /**
+     * Tests whether interpolation works for a sub configuration obtained via
+     * configurationAt() if updates are supported.
+     */
+    @Test
+    public void testInterpolationFromConfigurationAtWithUpdateSupport()
+    {
+        checkInterpolationFromConfigurationAt(true);
+    }
+
+    /**
      * An additional test for interpolation when the configurationAt() method is
      * involved for a local interpolation.
      */
@@ -440,7 +478,9 @@ public class TestSubnodeConfiguration
     @Test
     public void testUpdateEvents() throws ConfigurationException
     {
-        config = parent.configurationAt(SUB_KEY, true);
+        BaseHierarchicalConfiguration config =
+                (BaseHierarchicalConfiguration) parent.configurationAt(SUB_KEY,
+                        true);
         ConfigurationListenerTestImpl l = new ConfigurationListenerTestImpl();
         config.addConfigurationListener(l);
         updateParent();