commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ohe...@apache.org
Subject svn commit: r1575757 - in /commons/proper/configuration/branches/immutableNodes/src: main/java/org/apache/commons/configuration/tree/ test/java/org/apache/commons/configuration/tree/
Date Sun, 09 Mar 2014 20:56:56 GMT
Author: oheger
Date: Sun Mar  9 20:56:55 2014
New Revision: 1575757

URL: http://svn.apache.org/r1575757
Log:
NodeTracker now has the notion of detached nodes.

Tracked nodes can become detached after changes of the underlying nodes
structure if their NodeSelector does no longer select a single node.

Modified:
    commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/InMemoryNodeModel.java
    commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeTracker.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=1575757&r1=1575756&r2=1575757&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 20:56:55 2014
@@ -405,6 +405,27 @@ public class InMemoryNodeModel implement
     }
 
     /**
+     * Returns a flag whether the specified tracked node is detached. As long as
+     * the {@code NodeSelector} associated with that node returns a single
+     * instance, the tracked node is said to be <em>life</em>. If now an update
+     * of the model happens which invalidates the selector (maybe the target
+     * node was removed), the tracked node becomes detached. It is still
+     * possible to query the node; here the latest valid instance is returned.
+     * But further changes on the node model are no longer tracked for this
+     * node. So even if there are further changes which would make the
+     * {@code NodeSelector} valid again, the tracked node stays in detached
+     * state.
+     *
+     * @param selector the {@code NodeSelector} defining the desired node
+     * @return a flag whether this tracked node is in detached state
+     * @throws ConfigurationRuntimeException if the selector is unknown
+     */
+    public boolean isTrackedNodeDetached(NodeSelector selector)
+    {
+        return structure.get().getNodeTracker().isTrackedNodeDetached(selector);
+    }
+
+    /**
      * Removes a tracked node. This method is the opposite of
      * {@code trackNode()}. It has to be called if there is no longer the need
      * to track a specific node. Note that for each call of {@code trackNode()}
@@ -513,7 +534,7 @@ public class InMemoryNodeModel implement
     {
         NodeTracker newTracker =
                 (current != null) ? current.getNodeTracker()
-                        : new NodeTracker();
+                        .detachAllTrackedNodes() : new NodeTracker();
         return new TreeData(root, createParentMapping(root),
                 Collections.<ImmutableNode, ImmutableNode> emptyMap(),
                 newTracker);

Modified: commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeTracker.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeTracker.java?rev=1575757&r1=1575756&r2=1575757&view=diff
==============================================================================
--- commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeTracker.java
(original)
+++ commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/NodeTracker.java
Sun Mar  9 20:56:55 2014
@@ -155,6 +155,19 @@ class NodeTracker
     }
 
     /**
+     * Returns a flag whether the specified tracked node is detached.
+     *
+     * @param selector the {@code NodeSelector}
+     * @return a flag whether this node is detached
+     * @throws ConfigurationRuntimeException if no data for this selector is
+     *         available
+     */
+    public boolean isTrackedNodeDetached(NodeSelector selector)
+    {
+        return getTrackedNodeData(selector).isDetached();
+    }
+
+    /**
      * Updates tracking information after the node structure has been changed.
      * This method iterates over all tracked nodes. The selectors are evaluated
      * again to update the node reference. If this fails for a selector, the
@@ -180,18 +193,67 @@ class NodeTracker
         for (Map.Entry<NodeSelector, TrackedNodeData> e : trackedNodes
                 .entrySet())
         {
-            ImmutableNode newTarget =
-                    e.getKey().select(root, resolver, handler);
-            TrackedNodeData newTrackData =
-                    (newTarget != null) ? e.getValue().updateNode(newTarget)
-                            : e.getValue();
-            newState.put(e.getKey(), newTrackData);
+            newState.put(e.getKey(), determineUpdatedTrackedNodeData(root, resolver, handler,
e));
+        }
+
+        return new NodeTracker(newState);
+    }
+
+    /**
+     * Marks all tracked nodes as detached. This method is called if there are
+     * some drastic changes on the underlying node structure, e.g. if the root
+     * node was replaced.
+     *
+     * @return the updated instance
+     */
+    public NodeTracker detachAllTrackedNodes()
+    {
+        if (trackedNodes.isEmpty())
+        {
+            // there is not state to be updated
+            return this;
+        }
+
+        Map<NodeSelector, TrackedNodeData> newState =
+                new HashMap<NodeSelector, TrackedNodeData>();
+        for (Map.Entry<NodeSelector, TrackedNodeData> e : trackedNodes
+                .entrySet())
+        {
+            TrackedNodeData newData =
+                    e.getValue().isDetached() ? e.getValue() : e.getValue()
+                            .detach();
+            newState.put(e.getKey(), newData);
         }
 
         return new NodeTracker(newState);
     }
 
     /**
+     * Returns a {@code TrackedNodeData} object for an update operation. If the
+     * tracked node is still life, its selector is applied to the current root
+     * node. It may become detached if there is no match.
+     *
+     * @param root the root node
+     * @param resolver the {@code NodeKeyResolver}
+     * @param handler the {@code NodeHandler}
+     * @param e the current selector and {@code TrackedNodeData}
+     * @return the updated {@code TrackedNodeData}
+     */
+    private TrackedNodeData determineUpdatedTrackedNodeData(ImmutableNode root,
+            NodeKeyResolver<ImmutableNode> resolver,
+            NodeHandler<ImmutableNode> handler,
+            Map.Entry<NodeSelector, TrackedNodeData> e)
+    {
+        if (e.getValue().isDetached())
+        {
+            return e.getValue();
+        }
+        ImmutableNode newTarget = e.getKey().select(root, resolver, handler);
+        return (newTarget != null) ? e.getValue().updateNode(newTarget) : e
+                .getValue().detach();
+    }
+
+    /**
      * Obtains the {@code TrackedNodeData} object for the specified selector. If
      * the selector cannot be resolved, an exception is thrown.
      *
@@ -255,6 +317,9 @@ class NodeTracker
         /** The number of observers of this tracked node. */
         private final int observerCount;
 
+        /** A flag whether the node is detached. */
+        private final boolean detached;
+
         /**
          * Creates a new instance of {@code TrackedNodeData} and initializes it
          * with the current reference to the tracked node.
@@ -263,7 +328,7 @@ class NodeTracker
          */
         public TrackedNodeData(ImmutableNode nd)
         {
-            this(nd, 1);
+            this(nd, 1, false);
         }
 
         /**
@@ -272,11 +337,14 @@ class NodeTracker
          *
          * @param nd the tracked node
          * @param obsCount the observer count
+         * @param isDetached a flag whether the node is detached
          */
-        private TrackedNodeData(ImmutableNode nd, int obsCount)
+        private TrackedNodeData(ImmutableNode nd, int obsCount,
+                boolean isDetached)
         {
             node = nd;
             observerCount = obsCount;
+            detached = isDetached;
         }
 
         /**
@@ -290,6 +358,16 @@ class NodeTracker
         }
 
         /**
+         * Returns a flag whether the represented tracked node is detached.
+         *
+         * @return the detached flag
+         */
+        public boolean isDetached()
+        {
+            return detached;
+        }
+
+        /**
          * Another observer was added for this tracked node. This method returns
          * a new instance with an adjusted observer count.
          *
@@ -297,7 +375,7 @@ class NodeTracker
          */
         public TrackedNodeData observerAdded()
         {
-            return new TrackedNodeData(node, observerCount + 1);
+            return new TrackedNodeData(node, observerCount + 1, isDetached());
         }
 
         /**
@@ -311,7 +389,7 @@ class NodeTracker
         public TrackedNodeData observerRemoved()
         {
             return (observerCount <= 1) ? null : new TrackedNodeData(node,
-                    observerCount - 1);
+                    observerCount - 1, isDetached());
         }
 
         /**
@@ -324,7 +402,19 @@ class NodeTracker
          */
         public TrackedNodeData updateNode(ImmutableNode newNode)
         {
-            return new TrackedNodeData(newNode, observerCount);
+            return new TrackedNodeData(newNode, observerCount, isDetached());
+        }
+
+        /**
+         * Returns an instance with the detached flag set to true. This method
+         * is called if the selector of a tracked node does not match a single
+         * node any more.
+         *
+         * @return the updated instance
+         */
+        public TrackedNodeData detach()
+        {
+            return new TrackedNodeData(getNode(), observerCount, true);
         }
     }
 }

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=1575757&r1=1575756&r2=1575757&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 20:56:55 2014
@@ -17,8 +17,10 @@
 package org.apache.commons.configuration.tree;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import org.apache.commons.configuration.ex.ConfigurationRuntimeException;
@@ -213,4 +215,67 @@ public class TestInMemoryNodeModelTracke
         model.untrackNode(selector);
         assertNotNull("No tracked node", model.getTrackedNode(selector));
     }
+
+    /**
+     * Tests isDetached() for a node which has just been tracked.
+     */
+    @Test
+    public void testIsDetachedFalseNoUpdates()
+    {
+        NodeKeyResolver<ImmutableNode> resolver = createResolver();
+        model.trackNode(selector, resolver);
+        assertFalse("Node is detached", model.isTrackedNodeDetached(selector));
+    }
+
+    /**
+     * Tests isDetached() for a life node.
+     */
+    @Test
+    public void testIsDetachedFalseAfterUpdate()
+    {
+        NodeKeyResolver<ImmutableNode> resolver = createResolver();
+        model.trackNode(selector, resolver);
+        model.clearProperty("tables.table(1).fields.field(1).name", resolver);
+        assertFalse("Node is detached", model.isTrackedNodeDetached(selector));
+    }
+
+    /**
+     * Tests isDetached() for an actually detached node.
+     */
+    @Test
+    public void testIsDetachedTrue()
+    {
+        NodeKeyResolver<ImmutableNode> resolver = createResolver();
+        model.trackNode(selector, resolver);
+        model.clearTree("tables.table(0)", resolver);
+        assertTrue("Node is not detached",
+                model.isTrackedNodeDetached(selector));
+    }
+
+    /**
+     * Tests whether a clear() operation causes nodes to be detached.
+     */
+    @Test
+    public void testIsDetachedAfterClear()
+    {
+        NodeKeyResolver<ImmutableNode> resolver = createResolver();
+        model.trackNode(selector, resolver);
+        model.clear();
+        assertTrue("Node is not detached",
+                model.isTrackedNodeDetached(selector));
+    }
+
+    /**
+     * Tests whether tracked nodes become detached when a new root node is set.
+     */
+    @Test
+    public void testIsDetachedAfterSetRoot()
+    {
+        NodeKeyResolver<ImmutableNode> resolver = createResolver();
+        model.trackNode(selector, resolver);
+        model.clearProperty("tables.table(1).fields.field(1).name", resolver);
+        model.setRootNode(root);
+        assertTrue("Node is not detached",
+                model.isTrackedNodeDetached(selector));
+    }
 }



Mime
View raw message