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/04/05 21:50:07 UTC

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

Author: oheger
Date: Sat Apr  5 19:50:06 2014
New Revision: 1585193

URL: http://svn.apache.org/r1585193
Log:
Added a new mergeRoot() method to InMemoryNodeModel.

Such a merge operation is required by concrete configuration implementations
when a new configuration source has been loaded whose data now has to be
added to the already existing content.

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/ModelTransaction.java
    commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestInMemoryNodeModelReferences.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=1585193&r1=1585192&r2=1585193&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 Sat Apr  5 19:50:06 2014
@@ -378,6 +378,59 @@ public class InMemoryNodeModel implement
     }
 
     /**
+     * Merges the root node of this model with the specified node. This method
+     * is typically caused by configuration implementations when a configuration
+     * source is loaded, and its data has to be added to the model. It is
+     * possible to define the new name of the root node and to pass in a map
+     * with reference objects.
+     *
+     * @param node the node to be merged with the root node
+     * @param rootName the new name of the root node; can be <b>null</b>, then
+     *        the name of the root node is not changed unless it is <b>null</b>
+     * @param references an optional map with reference objects
+     * @param rootRef an optional reference object for the new root node
+     * @param resolver the {@code NodeKeyResolver}
+     */
+    public void mergeRoot(final ImmutableNode node, final String rootName,
+            final Map<ImmutableNode, ?> references, final Object rootRef,
+            NodeKeyResolver<ImmutableNode> resolver)
+    {
+        updateModel(new TransactionInitializer()
+        {
+            @Override
+            public boolean initTransaction(ModelTransaction tx)
+            {
+                TreeData current = tx.getCurrentData();
+                String newRootName =
+                        determineRootName(current.getRootNode(), node, rootName);
+                if (newRootName != null)
+                {
+                    tx.addChangeNodeNameOperation(current.getRootNode(),
+                            newRootName);
+                }
+                tx.addAddNodesOperation(current.getRootNode(),
+                        node.getChildren());
+                tx.addAttributesOperation(current.getRootNode(),
+                        node.getAttributes());
+                if (node.getValue() != null)
+                {
+                    tx.addChangeNodeValueOperation(current.getRootNode(),
+                            node.getValue());
+                }
+                if (references != null)
+                {
+                    tx.addNewReferences(references);
+                }
+                if (rootRef != null)
+                {
+                    tx.addNewReference(current.getRootNode(), rootRef);
+                }
+                return true;
+            }
+        }, null, resolver);
+    }
+
+    /**
      * Adds a node to be tracked. After this method has been called with a
      * specific {@code NodeSelector}, the node associated with this key can be
      * always obtained using {@link #getTrackedNode(NodeSelector)} with the same
@@ -984,6 +1037,31 @@ public class InMemoryNodeModel implement
     }
 
     /**
+     * Determines the name of the root node for a merge operation. If a root
+     * name is provided, it is used. Otherwise, if the current root node has no
+     * name, the name of the node to be merged is used. A result of <b>null</b>
+     * means that no node name has to be set.
+     *
+     * @param rootNode the current root node
+     * @param node the node to be merged with the root node
+     * @param rootName the name of the resulting node
+     * @return the new name of the root node
+     */
+    private static String determineRootName(ImmutableNode rootNode,
+            ImmutableNode node, String rootName)
+    {
+        if (rootName != null)
+        {
+            return rootName;
+        }
+        if (rootNode.getNodeName() == null)
+        {
+            return node.getNodeName();
+        }
+        return null;
+    }
+
+    /**
      * Creates the mapping to parent nodes for the nodes structured represented
      * by the passed in root node. Each node is assigned its parent node. Here
      * an iterative algorithm is used rather than a recursive one to avoid stack

Modified: commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/ModelTransaction.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/ModelTransaction.java?rev=1585193&r1=1585192&r2=1585193&view=diff
==============================================================================
--- commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/ModelTransaction.java (original)
+++ commons/proper/configuration/branches/immutableNodes/src/main/java/org/apache/commons/configuration/tree/ModelTransaction.java Sat Apr  5 19:50:06 2014
@@ -118,6 +118,9 @@ class ModelTransaction
      */
     private final SortedMap<Integer, Map<ImmutableNode, Operations>> operations;
 
+    /** A map with reference objects to be added during this transaction. */
+    private Map<ImmutableNode, Object> newReferences;
+
     /** The new root node. */
     private ImmutableNode newRoot;
 
@@ -213,6 +216,19 @@ class ModelTransaction
     }
 
     /**
+     * Adds an operation for adding multiple attributes to a target node.
+     *
+     * @param target the target node
+     * @param attributes the map with attributes to be set
+     */
+    public void addAttributesOperation(ImmutableNode target,
+            Map<String, Object> attributes)
+    {
+        fetchOperations(target, LEVEL_UNKNOWN).addOperation(
+                new AddAttributesOperation(attributes));
+    }
+
+    /**
      * Adds an operation for removing a child node of a given node.
      *
      * @param parent the parent node
@@ -261,6 +277,40 @@ class ModelTransaction
     }
 
     /**
+     * Adds an operation for changing the name of a target node.
+     *
+     * @param target the target node
+     * @param newName the new name for this node
+     */
+    public void addChangeNodeNameOperation(ImmutableNode target, String newName)
+    {
+        fetchOperations(target, LEVEL_UNKNOWN).addOperation(
+                new ChangeNodeNameOperation(newName));
+    }
+
+    /**
+     * Adds a map with new reference objects. The entries in this map are passed
+     * to the {@code ReferenceTracker} during execution of this transaction.
+     *
+     * @param refs the map with new reference objects
+     */
+    public void addNewReferences(Map<ImmutableNode, ?> refs)
+    {
+        fetchReferenceMap().putAll(refs);
+    }
+
+    /**
+     * Adds a new reference object for the given node.
+     *
+     * @param node the affected node
+     * @param ref the reference object for this node
+     */
+    public void addNewReference(ImmutableNode node, Object ref)
+    {
+        fetchReferenceMap().put(node, ref);
+    }
+
+    /**
      * Executes this transaction resulting in a new {@code TreeData} object. The
      * object returned by this method serves as the definition of a new node
      * structure for the calling model.
@@ -273,9 +323,8 @@ class ModelTransaction
         updateParentMapping();
         return new TreeData(newRoot, parentMapping, replacementMapping,
                 currentData.getNodeTracker().update(newRoot, rootNodeSelector,
-                        getResolver(), getCurrentData()), currentData
-                        .getReferenceTracker().updateReferences(replacedNodes,
-                                allRemovedNodes));
+                        getResolver(), getCurrentData()), updateReferenceTracker()
+        );
     }
 
     /**
@@ -471,6 +520,36 @@ class ModelTransaction
     }
 
     /**
+     * Returns an updated {@code ReferenceTracker} instance. The changes
+     * performed during this transaction are applied to the tracker.
+     *
+     * @return the updated tracker instance
+     */
+    private ReferenceTracker updateReferenceTracker()
+    {
+        ReferenceTracker tracker = currentData.getReferenceTracker();
+        if (newReferences != null)
+        {
+            tracker = tracker.addReferences(newReferences);
+        }
+        return tracker.updateReferences(replacedNodes, allRemovedNodes);
+    }
+
+    /**
+     * Returns the map with new reference objects. It is created if necessary.
+     *
+     * @return the map with reference objects
+     */
+    private Map<ImmutableNode, Object> fetchReferenceMap()
+    {
+        if (newReferences == null)
+        {
+            newReferences = new HashMap<ImmutableNode, Object>();
+        }
+        return newReferences;
+    }
+
+    /**
      * Constructs the concatenation of two collections. Both can be null.
      *
      * @param col1 the first collection
@@ -780,6 +859,33 @@ class ModelTransaction
     }
 
     /**
+     * A specialized operation class for adding multiple attributes to a target
+     * node.
+     */
+    private class AddAttributesOperation extends Operation
+    {
+        /** The map with attributes. */
+        private final Map<String, Object> attributes;
+
+        /**
+         * Creates a new instance of {@code AddAttributesOperation}.
+         *
+         * @param attrs the map with attributes
+         */
+        public AddAttributesOperation(Map<String, Object> attrs)
+        {
+            attributes = attrs;
+        }
+
+        @Override
+        protected ImmutableNode apply(ImmutableNode target,
+                Operations operations)
+        {
+            return target.setAttributes(attributes);
+        }
+    }
+
+    /**
      * A specialized operation class for removing an attribute from a target
      * node.
      */
@@ -834,6 +940,33 @@ class ModelTransaction
     }
 
     /**
+     * A specialized operation class which changes the name of a node.
+     */
+    private class ChangeNodeNameOperation extends Operation
+    {
+        /** The new node name. */
+        private final String newName;
+
+        /**
+         * Creates a new instance of {@code ChangeNodeNameOperation} and sets
+         * the new node name.
+         *
+         * @param name the new node name
+         */
+        public ChangeNodeNameOperation(String name)
+        {
+            newName = name;
+        }
+
+        @Override
+        protected ImmutableNode apply(ImmutableNode target,
+                Operations operations)
+        {
+            return target.setName(newName);
+        }
+    }
+
+    /**
      * A helper class which collects multiple update operations to be executed
      * on a single node.
      */

Modified: commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestInMemoryNodeModelReferences.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestInMemoryNodeModelReferences.java?rev=1585193&r1=1585192&r2=1585193&view=diff
==============================================================================
--- commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestInMemoryNodeModelReferences.java (original)
+++ commons/proper/configuration/branches/immutableNodes/src/test/java/org/apache/commons/configuration/tree/TestInMemoryNodeModelReferences.java Sat Apr  5 19:50:06 2014
@@ -57,9 +57,10 @@ public class TestInMemoryNodeModelRefere
         NodeStructureHelper.expectResolveKeyForQueries(resolver);
         NodeStructureHelper.expectResolveAddKeys(resolver);
         EasyMock.replay(resolver);
-        model = new InMemoryNodeModel(NodeStructureHelper.ROOT_AUTHORS_TREE);
+        model = new InMemoryNodeModel();
         Map<ImmutableNode, String> references = createReferences();
-        model.addReferences(references);
+        model.mergeRoot(NodeStructureHelper.ROOT_AUTHORS_TREE, null,
+                references, NodeStructureHelper.ROOT_AUTHORS_TREE.getNodeName(), resolver);
     }
 
     /**
@@ -71,6 +72,7 @@ public class TestInMemoryNodeModelRefere
     {
         Collection<ImmutableNode> nodes =
                 collectNodes(NodeStructureHelper.ROOT_AUTHORS_TREE);
+        nodes.remove(NodeStructureHelper.ROOT_AUTHORS_TREE);
         Map<ImmutableNode, String> refs = new HashMap<ImmutableNode, String>();
         for (ImmutableNode node : nodes)
         {
@@ -224,4 +226,71 @@ public class TestInMemoryNodeModelRefere
         List<Object> removedRefs = handler.removedReferences();
         removedRefs.add("another one");
     }
+
+    /**
+     * Tests whether a value is taken into account when the root node is merged.
+     */
+    @Test
+    public void testMergeRootWithValue()
+    {
+        ImmutableNode node = NodeStructureHelper.createNode("newNode", "test");
+        model.mergeRoot(node, null, null, null, resolver);
+        ImmutableNode root = model.getNodeHandler().getRootNode();
+        assertEquals("Wrong node name",
+                NodeStructureHelper.ROOT_AUTHORS_TREE.getNodeName(),
+                root.getNodeName());
+        assertEquals("Wrong node value", "test", root.getValue());
+    }
+
+    /**
+     * Tests whether the name of the root node can be changed during a merge
+     * operation.
+     */
+    @Test
+    public void testMergeRootOverrideName()
+    {
+        ImmutableNode node = NodeStructureHelper.createNode("newNode", null);
+        final String newName = "newRootNode";
+
+        model.mergeRoot(node, newName, null, null, resolver);
+        ImmutableNode root = model.getNodeHandler().getRootNode();
+        assertEquals("Wrong root name", newName, root.getNodeName());
+    }
+
+    /**
+     * Tests whether attributes are taken into account by a merge operation.
+     */
+    @Test
+    public void testMergeRootWithAttributes()
+    {
+        ImmutableNode node =
+                new ImmutableNode.Builder().addAttribute("key", "value")
+                        .create();
+        model.mergeRoot(node, null, null, null, resolver);
+        ImmutableNode root = model.getNodeHandler().getRootNode();
+        assertEquals("Wrong number of attributes", 1, root.getAttributes()
+                .size());
+        assertEquals("Wrong attribute", "value", root.getAttributes()
+                .get("key"));
+    }
+
+    /**
+     * Tests whether mergeRoot() handles an explicit reference object for the
+     * root node correctly.
+     */
+    @Test
+    public void testMergeRootReference()
+    {
+        final Object rootRef = 20140404210508L;
+        ImmutableNode node = NodeStructureHelper.createNode("newNode", null);
+
+        model.mergeRoot(node, null, null, rootRef, resolver);
+        ReferenceNodeHandler refHandler = model.getReferenceNodeHandler();
+        ImmutableNode checkNode =
+                NodeStructureHelper.nodeForKey(model, "Simmons/Ilium");
+        assertEquals("Wrong reference for node", checkNode.getNodeName(),
+                refHandler.getReference(checkNode));
+        assertEquals("Wrong root reference", rootRef,
+                refHandler.getReference(refHandler.getRootNode()));
+    }
 }