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/09 22:04:20 UTC

svn commit: r1575767 - 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  9 21:04:20 2014
New Revision: 1575767

URL: http://svn.apache.org/r1575767
Log:
InMemoryNodeModel now supports addProperty() on a tracked node.

The implementation is analogous to other operations on tracked nodes.

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=1575767&r1=1575766&r2=1575767&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  9 21:04:20 2014
@@ -104,17 +104,39 @@ public class InMemoryNodeModel implement
         return getTreeData();
     }
 
-    public void addProperty(final String key, final Iterable<?> values,
+    public void addProperty(String key, Iterable<?> values,
+            NodeKeyResolver<ImmutableNode> resolver)
+    {
+        addProperty(key, null, values, resolver);
+    }
+
+    /**
+     * Adds new property values using a tracked node as root node. This method
+     * works like the normal {@code addProperty()} method, but the origin of the
+     * operation (also for the interpretation of the passed in key) is a tracked
+     * node identified by the passed in {@code NodeSelector}. The selector can
+     * be <b>null</b>, then the root node is assumed.
+     *
+     * @param key the key
+     * @param selector the {@code NodeSelector} defining the root node (or
+     *        <b>null</b>)
+     * @param resolver the {@code NodeKeyResolver}
+     * @throws ConfigurationRuntimeException if the selector cannot be resolved
+     */
+    public void addProperty(final String key, NodeSelector selector,
+            final Iterable<?> values,
             final NodeKeyResolver<ImmutableNode> resolver)
     {
         if (valuesNotEmpty(values))
         {
-            updateModel(new TransactionInitializer() {
-                public boolean initTransaction(ModelTransaction tx) {
+            updateModel(new TransactionInitializer()
+            {
+                public boolean initTransaction(ModelTransaction tx)
+                {
                     initializeAddTransaction(tx, key, values, resolver);
                     return true;
                 }
-            }, null, resolver);
+            }, selector, resolver);
         }
     }
 
@@ -164,17 +186,14 @@ public class InMemoryNodeModel implement
     public void setProperty(final String key, final Object value,
             final NodeKeyResolver<ImmutableNode> resolver)
     {
-        updateModel(new TransactionInitializer()
-        {
-            public boolean initTransaction(ModelTransaction tx)
-            {
+        updateModel(new TransactionInitializer() {
+            public boolean initTransaction(ModelTransaction tx) {
                 boolean added = false;
                 NodeUpdateData<ImmutableNode> updateData =
                         resolver.resolveUpdateKey(
                                 tx.getCurrentData().getRootNode(), key, value,
                                 tx.getCurrentData());
-                if (!updateData.getNewValues().isEmpty())
-                {
+                if (!updateData.getNewValues().isEmpty()) {
                     initializeAddTransaction(tx, key,
                             updateData.getNewValues(), resolver);
                     added = true;
@@ -211,27 +230,21 @@ public class InMemoryNodeModel implement
      * @param selector the {@code NodeSelector} defining the root node (or
      *        <b>null</b>)
      * @param resolver the {@code NodeKeyResolver}
+     * @throws ConfigurationRuntimeException if the selector cannot be resolved
      */
     public void clearTree(final String key, NodeSelector selector,
             final NodeKeyResolver<ImmutableNode> resolver)
     {
-        updateModel(new TransactionInitializer()
-        {
-            public boolean initTransaction(ModelTransaction tx)
-            {
+        updateModel(new TransactionInitializer() {
+            public boolean initTransaction(ModelTransaction tx) {
                 TreeData currentStructure = tx.getCurrentData();
                 for (QueryResult<ImmutableNode> result : resolver.resolveKey(
-                        tx.getQueryRoot(), key, currentStructure))
-                {
-                    if (result.isAttributeResult())
-                    {
+                        tx.getQueryRoot(), key, currentStructure)) {
+                    if (result.isAttributeResult()) {
                         tx.addRemoveAttributeOperation(result.getNode(),
                                 result.getAttributeName());
-                    }
-                    else
-                    {
-                        if (result.getNode() == currentStructure.getRootNode())
-                        {
+                    } else {
+                        if (result.getNode() == currentStructure.getRootNode()) {
                             // the whole model is to be cleared
                             clear();
                             return false;
@@ -464,7 +477,7 @@ public class InMemoryNodeModel implement
             Iterable<?> values, NodeKeyResolver<ImmutableNode> resolver)
     {
         NodeAddData<ImmutableNode> addData =
-                resolver.resolveAddKey(tx.getCurrentData().getRootNode(), key,
+                resolver.resolveAddKey(tx.getQueryRoot(), key,
                         tx.getCurrentData());
         if (addData.isAttribute())
         {

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=1575767&r1=1575766&r2=1575767&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  9 21:04:20 2014
@@ -23,11 +23,13 @@ import static org.junit.Assert.assertSam
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
 import org.apache.commons.configuration.ex.ConfigurationRuntimeException;
 import org.easymock.EasyMock;
+import org.easymock.IAnswer;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -40,6 +42,9 @@ import org.junit.Test;
  */
 public class TestInMemoryNodeModelTrackedNodes
 {
+    /** Constant for the name of a new table field. */
+    private static final String NEW_FIELD = "newTableField";
+
     /** The root node for the test hierarchy. */
     private static ImmutableNode root;
 
@@ -72,10 +77,26 @@ public class TestInMemoryNodeModelTracke
      */
     private static NodeKeyResolver<ImmutableNode> createResolver()
     {
+        return createResolver(true);
+    }
+
+    /**
+     * Creates a default resolver which supports arbitrary queries on a target
+     * node and allows specifying the replay flag. If the boolean parameter is
+     * false, the mock is not replayed; so additional behaviors can be defined.
+     *
+     * @param replay the replay flag
+     * @return the resolver mock
+     */
+    private static NodeKeyResolver<ImmutableNode> createResolver(boolean replay)
+    {
         NodeKeyResolver<ImmutableNode> resolver =
                 NodeStructureHelper.createResolverMock();
         NodeStructureHelper.expectResolveKeyForQueries(resolver);
-        EasyMock.replay(resolver);
+        if (replay)
+        {
+            EasyMock.replay(resolver);
+        }
         return resolver;
     }
 
@@ -293,6 +314,27 @@ public class TestInMemoryNodeModelTracke
     }
 
     /**
+     * Returns the fields node from the model.
+     *
+     * @return the fields node
+     */
+    private ImmutableNode fieldsNodeFromModel()
+    {
+        return NodeStructureHelper.nodeForKey(model, "tables/table(1)/fields");
+    }
+
+    /**
+     * Returns the fields node from a tracked node.
+     *
+     * @return the fields node
+     */
+    private ImmutableNode fieldsNodeFromTrackedNode()
+    {
+        return NodeStructureHelper.nodeForKey(model.getTrackedNode(selector),
+                "fields");
+    }
+
+    /**
      * Helper method for checking whether the expected field node was removed.
      *
      * @param nodeFields the fields node
@@ -329,8 +371,7 @@ public class TestInMemoryNodeModelTracke
         NodeKeyResolver<ImmutableNode> resolver = createResolver();
         model.trackNode(selector, resolver);
         model.clearProperty("fields.field(0).name", selector, resolver);
-        ImmutableNode nodeFields =
-                NodeStructureHelper.nodeForKey(model, "tables/table(1)/fields");
+        ImmutableNode nodeFields = fieldsNodeFromModel();
         checkForRemovedField(nodeFields, 0);
     }
 
@@ -345,9 +386,7 @@ public class TestInMemoryNodeModelTracke
         ImmutableNode rootNode = model.getRootNode();
         model.clearProperty("fields.field(0).name", selector, resolver);
         assertSame("Model root was changed", rootNode, model.getRootNode());
-        ImmutableNode nodeFields =
-                NodeStructureHelper.nodeForKey(model.getTrackedNode(selector),
-                        "fields");
+        ImmutableNode nodeFields = fieldsNodeFromTrackedNode();
         checkForRemovedField(nodeFields, 0);
     }
 
@@ -360,8 +399,7 @@ public class TestInMemoryNodeModelTracke
         NodeKeyResolver<ImmutableNode> resolver = createResolver();
         model.trackNode(selector, resolver);
         model.clearTree("fields.field(1)", selector, resolver);
-        ImmutableNode nodeFields =
-                NodeStructureHelper.nodeForKey(model, "tables/table(1)/fields");
+        ImmutableNode nodeFields = fieldsNodeFromModel();
         checkForRemovedField(nodeFields, 1);
     }
 
@@ -376,9 +414,88 @@ public class TestInMemoryNodeModelTracke
         ImmutableNode rootNode = model.getRootNode();
         model.clearTree("fields.field(1)", selector, resolver);
         assertSame("Model root was changed", rootNode, model.getRootNode());
-        ImmutableNode nodeFields =
-                NodeStructureHelper.nodeForKey(model.getTrackedNode(selector),
-                        "fields");
+        ImmutableNode nodeFields = fieldsNodeFromTrackedNode();
         checkForRemovedField(nodeFields, 1);
     }
+
+    /**
+     * Prepares the passed in resolver mock to resolve add keys. They are
+     * interpreted on a default expression engine.
+     *
+     * @param resolver the {@code NodeKeyResolver} mock
+     */
+    private static void prepareResolverForAddKeys(
+            NodeKeyResolver<ImmutableNode> resolver)
+    {
+        EasyMock.expect(
+                resolver.resolveAddKey(EasyMock.anyObject(ImmutableNode.class),
+                        EasyMock.anyString(),
+                        EasyMock.anyObject(TreeData.class)))
+                .andAnswer(new IAnswer<NodeAddData<ImmutableNode>>() {
+                    public NodeAddData<ImmutableNode> answer() throws Throwable {
+                        ImmutableNode root =
+                                (ImmutableNode) EasyMock.getCurrentArguments()[0];
+                        String key = (String) EasyMock.getCurrentArguments()[1];
+                        TreeData handler =
+                                (TreeData) EasyMock.getCurrentArguments()[2];
+                        return DefaultExpressionEngine.INSTANCE.prepareAdd(
+                                root, key, handler);
+                    }
+                }).anyTimes();
+    }
+
+    /**
+     * Tests whether a field node was added.
+     *
+     * @param nodeFields the fields node
+     */
+    private static void checkForAddedField(ImmutableNode nodeFields)
+    {
+        assertEquals("Wrong number of children",
+                NodeStructureHelper.fieldsLength(1) + 1, nodeFields
+                        .getChildren().size());
+        ImmutableNode nodeField =
+                nodeFields.getChildren().get(
+                        NodeStructureHelper.fieldsLength(1));
+        assertEquals("Wrong node name", "field", nodeField.getNodeName());
+        assertEquals("Wrong number of children of field node", 1, nodeField
+                .getChildren().size());
+        ImmutableNode nodeName = nodeField.getChildren().get(0);
+        assertEquals("Wrong name of name node", "name", nodeName.getNodeName());
+        assertEquals("Wrong node value", NEW_FIELD, nodeName.getValue());
+    }
+
+    /**
+     * Tests whether an addProperty() operation works on a tracked node.
+     */
+    @Test
+    public void testAddPropertyOnTrackedNode()
+    {
+        NodeKeyResolver<ImmutableNode> resolver = createResolver(false);
+        prepareResolverForAddKeys(resolver);
+        EasyMock.replay(resolver);
+        model.trackNode(selector, resolver);
+        model.addProperty("fields.field(-1).name", selector,
+                Collections.singleton(NEW_FIELD), resolver);
+        checkForAddedField(fieldsNodeFromModel());
+        checkForAddedField(fieldsNodeFromTrackedNode());
+    }
+
+    /**
+     * Tests an addProperty() operation on a tracked node that is detached.
+     */
+    @Test
+    public void testAddPropertyOnDetachedNode()
+    {
+        NodeKeyResolver<ImmutableNode> resolver = createResolver(false);
+        prepareResolverForAddKeys(resolver);
+        EasyMock.replay(resolver);
+        model.trackNode(selector, resolver);
+        initDetachedNode(resolver);
+        ImmutableNode rootNode = model.getRootNode();
+        model.addProperty("fields.field(-1).name", selector,
+                Collections.singleton(NEW_FIELD), resolver);
+        assertSame("Root node was changed", rootNode, model.getRootNode());
+        checkForAddedField(fieldsNodeFromTrackedNode());
+    }
 }