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/23 21:31:35 UTC

svn commit: r1580594 - in /commons/proper/configuration/branches/immutableNodes/src: main/java/org/apache/commons/configuration/tree/InMemoryNodeModel.java test/java/org/apache/commons/configuration/tree/TestInMemoryNodeModelTrackedNodes.java

Author: oheger
Date: Sun Mar 23 20:31:34 2014
New Revision: 1580594

URL: http://svn.apache.org/r1580594
Log:
Added trackChildNodeWithCreation() method to InMemoryNodeModel.

This method allows tracking a child node of a selected node. If the child does
not exist, it is created dynamically.

Modified:
    commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/InMemoryNodeModel.java
    commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestInMemoryNodeModelTrackedNodes.java

Modified: commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/InMemoryNodeModel.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/InMemoryNodeModel.java?rev=1580594&r1=1580593&r2=1580594&view=diff
==============================================================================
--- commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/InMemoryNodeModel.java (original)
+++ commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/InMemoryNodeModel.java Sun Mar 23 20:31:34 2014
@@ -489,6 +489,48 @@ public class InMemoryNodeModel implement
     }
 
     /**
+     * Tracks a node which is a child of another node selected by the passed in
+     * key. If the selected node has a child node with this name, it is tracked
+     * and its selector is returned. Otherwise, a new child node with this name
+     * is created first.
+     *
+     * @param key the key for selecting the parent node
+     * @param childName the name of the child node
+     * @param resolver the {@code NodeKeyResolver}
+     * @return the {@code NodeSelector} for the tracked child node
+     * @throws ConfigurationRuntimeException if the passed in key does not
+     *         select a single node
+     */
+    public NodeSelector trackChildNodeWithCreation(String key,
+            String childName, NodeKeyResolver<ImmutableNode> resolver)
+    {
+        MutableObject<NodeSelector> refSelector =
+                new MutableObject<NodeSelector>();
+        boolean done;
+
+        do
+        {
+            TreeData current = structure.get();
+            List<ImmutableNode> nodes =
+                    resolver.resolveNodeKey(current.getRootNode(), key, current);
+            if (nodes.size() != 1)
+            {
+                throw new ConfigurationRuntimeException(
+                        "Key does not select a single node: " + key);
+            }
+
+            ImmutableNode parent = nodes.get(0);
+            TreeData newData =
+                    createDataWithTrackedChildNode(current, parent, childName,
+                            resolver, refSelector);
+
+            done = structure.compareAndSet(current, newData);
+        } while (!done);
+
+        return refSelector.getValue();
+    }
+
+    /**
      * Returns the current {@code ImmutableNode} instance associated with the
      * given {@code NodeSelector}. The node must be a tracked node, i.e.
      * {@link #trackNode(NodeSelector, NodeKeyResolver)} must have been called
@@ -1079,6 +1121,72 @@ public class InMemoryNodeModel implement
     }
 
     /**
+     * Adds a tracked node that has already been resolved to the specified data
+     * object.
+     *
+     * @param current the current {@code TreeData} object
+     * @param node the node in question
+     * @param resolver the {@code NodeKeyResolver}
+     * @param refSelector here the newly created {@code NodeSelector} is
+     *        returned
+     * @return the new {@code TreeData} instance
+     */
+    private static TreeData updateDataWithNewTrackedNode(TreeData current,
+            ImmutableNode node, NodeKeyResolver<ImmutableNode> resolver,
+            MutableObject<NodeSelector> refSelector)
+    {
+        NodeSelector selector =
+                new NodeSelector(resolver.nodeKey(node,
+                        new HashMap<ImmutableNode, String>(), current));
+        refSelector.setValue(selector);
+        NodeTracker newTracker =
+                current.getNodeTracker().trackNodes(
+                        Collections.singleton(selector),
+                        Collections.singleton(node));
+        return current.updateNodeTracker(newTracker);
+    }
+
+    /**
+     * Creates a new data object with a tracked child node of the given parent
+     * node. If such a child node already exists, it is used. Otherwise, a new
+     * one is created.
+     *
+     * @param current the current {@code TreeData} object
+     * @param parent the parent node
+     * @param childName the name of the child node
+     * @param resolver the {@code NodeKeyResolver}
+     * @param refSelector here the newly created {@code NodeSelector} is
+     *        returned
+     * @return the new {@code TreeData} instance
+     */
+    private static TreeData createDataWithTrackedChildNode(TreeData current,
+            ImmutableNode parent, String childName,
+            NodeKeyResolver<ImmutableNode> resolver,
+            MutableObject<NodeSelector> refSelector)
+    {
+        TreeData newData;
+        List<ImmutableNode> namedChildren =
+                current.getChildren(parent, childName);
+        if (!namedChildren.isEmpty())
+        {
+            newData =
+                    updateDataWithNewTrackedNode(current, namedChildren.get(0),
+                            resolver, refSelector);
+        }
+        else
+        {
+            ImmutableNode child =
+                    new ImmutableNode.Builder().name(childName).create();
+            ModelTransaction tx = new ModelTransaction(current, null, resolver);
+            tx.addAddNodeOperation(parent, child);
+            newData =
+                    updateDataWithNewTrackedNode(tx.execute(), child, resolver,
+                            refSelector);
+        }
+        return newData;
+    }
+
+    /**
      * Checks whether the specified collection with values is not empty.
      *
      * @param values the collection with node values

Modified: commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestInMemoryNodeModelTrackedNodes.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestInMemoryNodeModelTrackedNodes.java?rev=1580594&r1=1580593&r2=1580594&view=diff
==============================================================================
--- commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestInMemoryNodeModelTrackedNodes.java (original)
+++ commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestInMemoryNodeModelTrackedNodes.java Sun Mar 23 20:31:34 2014
@@ -20,10 +20,12 @@ import static org.junit.Assert.assertEqu
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -922,4 +924,105 @@ public class TestInMemoryNodeModelTracke
                 .singletonList(NodeStructureHelper.nodeForKey(root,
                         "tables/table(0)/name")));
     }
+
+    /**
+     * Tests whether an existing child of a selected node can be tracked.
+     */
+    @Test
+    public void testTrackChildNodeWithCreationExisting()
+    {
+        NodeKeyResolver<ImmutableNode> resolver = createResolver(false);
+        String childName = "name";
+        String parentKey = "tables/table(0)";
+        String childKey = parentKey + "/" + childName;
+        ImmutableNode node = NodeStructureHelper.nodeForKey(model, parentKey);
+        ImmutableNode child = NodeStructureHelper.nodeForKey(node, childName);
+        EasyMock.expect(
+                resolver.resolveNodeKey(root, TEST_KEY, model.getNodeHandler()))
+                .andReturn(Collections.singletonList(node));
+        expectNodeKey(resolver, child, childKey);
+        EasyMock.replay(resolver);
+
+        NodeSelector childSelector =
+                model.trackChildNodeWithCreation(TEST_KEY, childName, resolver);
+        assertEquals("Wrong selector", new NodeSelector(childKey),
+                childSelector);
+        assertSame("Wrong tracked node", child,
+                model.getTrackedNode(childSelector));
+    }
+
+    /**
+     * Tests whether a child node to be tracked is created if necessary.
+     */
+    @Test
+    public void testTrackChildNodeWithCreationNonExisting()
+    {
+        NodeKeyResolver<ImmutableNode> resolver = createResolver(false);
+        String childName = "space";
+        String parentKey = "tables/table(0)";
+        String childKey = parentKey + "/" + childName;
+        ImmutableNode node = NodeStructureHelper.nodeForKey(model, parentKey);
+        EasyMock.expect(
+                resolver.resolveNodeKey(root, TEST_KEY, model.getNodeHandler()))
+                .andReturn(Collections.singletonList(node));
+        EasyMock.expect(
+                resolver.nodeKey(EasyMock.anyObject(ImmutableNode.class),
+                        EasyMock.eq(new HashMap<ImmutableNode, String>()),
+                        EasyMock.anyObject(TreeData.class)))
+                .andReturn(childKey);
+        EasyMock.replay(resolver);
+
+        NodeSelector childSelector =
+                model.trackChildNodeWithCreation(TEST_KEY, childName, resolver);
+        assertEquals("Wrong selector", new NodeSelector(childKey),
+                childSelector);
+        ImmutableNode child = model.getTrackedNode(childSelector);
+        assertEquals("Wrong child name", childName, child.getNodeName());
+        assertNull("Got a value", child.getValue());
+        ImmutableNode parent = model.getNodeHandler().getParent(child);
+        assertEquals("Wrong parent node", "table", parent.getNodeName());
+        assertEquals("Wrong node path", child,
+                NodeStructureHelper.nodeForKey(model, childKey));
+    }
+
+    /**
+     * Helper method for testing trackChildNodeWithCreation() if invalid query
+     * results are generated.
+     *
+     * @param queryResult the result set of the key
+     */
+    private void checkTrackChildNodeWithCreationInvalidKey(
+            List<ImmutableNode> queryResult)
+    {
+        NodeKeyResolver<ImmutableNode> resolver = createResolver(false);
+        EasyMock.expect(
+                resolver.resolveNodeKey(model.getRootNode(), TEST_KEY,
+                        model.getNodeHandler())).andReturn(queryResult);
+        EasyMock.replay(resolver);
+        model.trackChildNodeWithCreation(TEST_KEY, "someChild", resolver);
+    }
+
+    /**
+     * Tests trackChildNodeWithCreation() if the passed in key does not select a
+     * node.
+     */
+    @Test(expected = ConfigurationRuntimeException.class)
+    public void testTrackChildNodeWithCreationNoResults()
+    {
+        checkTrackChildNodeWithCreationInvalidKey(new ArrayList<ImmutableNode>());
+    }
+
+    /**
+     * Tests trackChildNodeWithCreation() if the passed in key selects multiple
+     * nodes.
+     */
+    @Test(expected = ConfigurationRuntimeException.class)
+    public void testTrackChildNodeWithCreationMultipleResults()
+    {
+        List<ImmutableNode> nodes =
+                Arrays.asList(
+                        NodeStructureHelper.nodeForKey(root, "tables/table(0)"),
+                        NodeStructureHelper.nodeForKey(root, "tables/table(1)"));
+        checkTrackChildNodeWithCreationInvalidKey(nodes);
+    }
 }