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()));
+ }
}