Author: oheger
Date: Sat Nov 7 16:10:40 2009
New Revision: 833705
URL: http://svn.apache.org/viewvc?rev=833705&view=rev
Log:
Added a copy constructor to XMLConfigurationSource.
Modified:
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/InMemoryConfigurationSource.java
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/impl/XMLConfigurationSource.java
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/impl/TestXMLConfigurationSource.java
Modified:
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/InMemoryConfigurationSource.java
URL:
http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/InMemoryConfigurationSource.java?rev=833705&r1=833704&r2=833705&view=diff
==============================================================================
---
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/InMemoryConfigurationSource.java
(original)
+++
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/InMemoryConfigurationSource.java
Sat Nov 7 16:10:40 2009
@@ -78,10 +78,7 @@
this();
if (c != null)
{
- CloneVisitor visitor = new CloneVisitor();
- NodeVisitorAdapter
- .visit(visitor, c.getRootNode(), getNodeHandler());
- setRootNode(visitor.getClone());
+ setRootNode(copyNodes(c.getRootNode(), NODE_HANDLER));
}
}
@@ -133,24 +130,71 @@
}
/**
+ * Copies the node structure below the specified root node. This method
+ * traverses the node structure with a specialized visitor that can create
a
+ * deep clone of all nodes.
+ *
+ * @param <N> the type of the nodes
+ * @param node the root node of the hierarchy which is to be copied
+ * @param handler the {...@code NodeHandler} to be used
+ * @return the root node of the copied structure
+ */
+ protected static <N extends ConfigurationNode> N copyNodes(N node,
+ NodeHandler<N> handler)
+ {
+ CloneVisitor<N> visitor = new CloneVisitor<N>();
+ NodeVisitorAdapter.visit(visitor, node, handler);
+ return visitor.getClone();
+ }
+
+ /**
+ * Clears all reference fields in a node structure. A configuration node
can
+ * store a so-called "reference". The meaning of this data is
+ * determined by a concrete sub class. Typically such references are
+ * specific for a configuration instance. If this instance is cloned or
+ * copied, they must be cleared. This can be done using this method.
+ *
+ * @param <N> the type of the nodes
+ * @param node the root node of the node hierarchy, in which the references
+ * are to be cleared
+ * @param handler the {...@code NodeHandler} to be used
+ */
+ protected static <N extends ConfigurationNode> void clearReferences(N node,
+ NodeHandler<N> handler)
+ {
+ NodeVisitorAdapter.visit(new NodeVisitorAdapter<N>()
+ {
+ @Override
+ public void visitBeforeChildren(N node, NodeHandler<N> handler)
+ {
+ node.setReference(null);
+ for (ConfigurationNode attr : node.getAttributes())
+ {
+ attr.setReference(null);
+ }
+ }
+ }, node, handler);
+ }
+
+ /**
* A specialized visitor that is able to create a deep copy of a node
* hierarchy.
*/
- private static class CloneVisitor extends
- NodeVisitorAdapter<ConfigurationNode>
+ private static class CloneVisitor<N extends ConfigurationNode> extends
+ NodeVisitorAdapter<N>
{
/** A stack with the actual object to be copied. */
- private Stack<ConfigurationNode> copyStack;
+ private Stack<N> copyStack;
/** Stores the result of the clone process. */
- private ConfigurationNode result;
+ private N result;
/**
* Creates a new instance of {...@code CloneVisitor}.
*/
public CloneVisitor()
{
- copyStack = new Stack<ConfigurationNode>();
+ copyStack = new Stack<N>();
}
/**
@@ -159,10 +203,10 @@
* @param node the node
*/
@Override
- public void visitAfterChildren(ConfigurationNode node,
- NodeHandler<ConfigurationNode> handler)
+ public void visitAfterChildren(N node,
+ NodeHandler<N> handler)
{
- ConfigurationNode copy = copyStack.pop();
+ N copy = copyStack.pop();
if (copyStack.isEmpty())
{
result = copy;
@@ -175,10 +219,11 @@
* @param node the node
*/
@Override
- public void visitBeforeChildren(ConfigurationNode node,
- NodeHandler<ConfigurationNode> handler)
+ public void visitBeforeChildren(N node,
+ NodeHandler<N> handler)
{
- ConfigurationNode copy = (ConfigurationNode) node.clone();
+ @SuppressWarnings("unchecked")
+ N copy = (N) node.clone();
copy.setParentNode(null);
for (ConfigurationNode attr : node.getAttributes())
@@ -199,7 +244,7 @@
*
* @return the cloned root node
*/
- public ConfigurationNode getClone()
+ public N getClone()
{
return result;
}
Modified:
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/impl/XMLConfigurationSource.java
URL:
http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/impl/XMLConfigurationSource.java?rev=833705&r1=833704&r2=833705&view=diff
==============================================================================
---
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/impl/XMLConfigurationSource.java
(original)
+++
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/base/impl/XMLConfigurationSource.java
Sat Nov 7 16:10:40 2009
@@ -43,6 +43,7 @@
import org.apache.commons.configuration2.ConfigurationException;
import org.apache.commons.configuration2.base.Capability;
import org.apache.commons.configuration2.base.DefaultLocatorSupport;
+import org.apache.commons.configuration2.base.HierarchicalConfigurationSource;
import org.apache.commons.configuration2.base.InMemoryConfigurationSource;
import org.apache.commons.configuration2.base.LocatorSupport;
import org.apache.commons.configuration2.base.StreamBasedSource;
@@ -173,30 +174,28 @@
public XMLConfigurationSource()
{
xmlNodeHandler = new XMLNodeHandler();
- locatorSupport = new DefaultLocatorSupport(this)
+ locatorSupport = initLocatorSupport();
+ }
+
+ /**
+ * Creates a new instance of {...@code XMLConfigurationSource} and
initializes
+ * its data from the content of the specified {...@code
+ * HierarchicalConfigurationSource}. This is a typical copy constructor.
+ *
+ * @param c the {...@code HierarchicalConfigurationSource} to be copied
(can be
+ * <b>null</b>)
+ * @see
InMemoryConfigurationSource#InMemoryConfigurationSource(HierarchicalConfigurationSource)
+ */
+ public XMLConfigurationSource(
+ HierarchicalConfigurationSource<? extends ConfigurationNode> c)
+ {
+ this();
+ if (c != null)
{
- /**
- * Loads data from the specified {...@code InputStream}. This
- * implementation directly delegates to the {...@code
- * load(InputStream)} method of the owning {...@code
- * XMLConfigurationSource}.
- *
- * @param in the {...@code InputStream}
- * @throws ConfigurationException if an error occurs
- */
- @Override
- public void load(InputStream in) throws ConfigurationException
- {
- try
- {
- XMLConfigurationSource.this.load(in);
- }
- catch (IOException ioex)
- {
- throw new ConfigurationException(ioex);
- }
- }
- };
+ setRootNode(copyNodes(c.getRootNode(), xmlNodeHandler));
+ clearReferences(getRootNode(), xmlNodeHandler);
+ }
+ setRootElementName(getRootNode().getName());
}
/**
@@ -868,6 +867,42 @@
}
/**
+ * Initializes the {...@code LocatorSupport} object used by this
configuration
+ * source. This implementation returns a special {...@code LocatorSupport}
+ * implementation whose {...@code load(InputStream)} method delegates to
the
+ * corresponding {...@code load()} method of this instance.
+ *
+ * @return the {...@code LocatorSupport} object
+ */
+ private LocatorSupport initLocatorSupport()
+ {
+ return new DefaultLocatorSupport(this)
+ {
+ /**
+ * Loads data from the specified {...@code InputStream}. This
+ * implementation directly delegates to the {...@code
+ * load(InputStream)} method of the owning {...@code
+ * XMLConfigurationSource}.
+ *
+ * @param in the {...@code InputStream}
+ * @throws ConfigurationException if an error occurs
+ */
+ @Override
+ public void load(InputStream in) throws ConfigurationException
+ {
+ try
+ {
+ XMLConfigurationSource.this.load(in);
+ }
+ catch (IOException ioex)
+ {
+ throw new ConfigurationException(ioex);
+ }
+ }
+ };
+ }
+
+ /**
* Tests whether the specified node has some child elements.
*
* @param node the node to check
Modified:
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/impl/TestXMLConfigurationSource.java
URL:
http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/impl/TestXMLConfigurationSource.java?rev=833705&r1=833704&r2=833705&view=diff
==============================================================================
---
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/impl/TestXMLConfigurationSource.java
(original)
+++
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/base/impl/TestXMLConfigurationSource.java
Sat Nov 7 16:10:40 2009
@@ -996,4 +996,132 @@
assertEquals("Invalid not trimmed", "Some other text", conf
.getString("space.testInvalid"));
}
+
+ /**
+ * Tests the copy constructor.
+ */
+ @Test
+ public void testInitCopy() throws ConfigurationException
+ {
+ XMLConfigurationSource src2 = new XMLConfigurationSource(source);
+ ConfigurationAssert.assertEquals(source, src2);
+ }
+
+ /**
+ * Tests that the XML document is reset by the copy constructor.
+ */
+ @Test
+ public void testInitCopyDocument()
+ {
+ XMLConfigurationSource copy = new XMLConfigurationSource(source);
+ assertNull("Document was copied, too", copy.getDocument());
+ }
+
+ /**
+ * Tests whether element references were cleared by the copy constructor.
+ */
+ @Test
+ public void testInitCopyClearReferences()
+ {
+ XMLConfigurationSource copy = new XMLConfigurationSource(source);
+ ConfigurationNode root = copy.getRootNode();
+ for (ConfigurationNode node : root.getChildren())
+ {
+ assertNull("Reference was not cleared", node.getReference());
+ }
+ }
+
+ /**
+ * Tests whether a configuration source created by the copy constructor can
+ * be correctly saved and reloaded.
+ */
+ @Test
+ public void testInitCopySave() throws ConfigurationException
+ {
+ XMLConfigurationSource copy = new XMLConfigurationSource(source);
+ conf = new ConfigurationImpl<ConfigurationNode>(copy);
+ Configuration<ConfigurationNode> conf2 = reload();
+ // Known issue: For elements with the xml:space="preserve" attribute
+ // that have child elements the number of line feeds is changed.
+ // Therefore the space property has a different value after reloading.
+ conf.clearProperty("space");
+ conf2.clearProperty("space");
+ ConfigurationAssert.assertEquals(conf, conf2);
+ }
+
+ /**
+ * Tests saving a configuration that was created from a hierarchical sub
+ * configuration.
+ */
+ @Test
+ public void testInitCopySaveAfterCreateWithCopyConstructorSub()
+ throws ConfigurationException
+ {
+ Configuration<ConfigurationNode> hc = conf.configurationAt("element2");
+ XMLConfigurationSource copy = new XMLConfigurationSource(hc
+ .getConfigurationSource());
+ conf = new ConfigurationImpl<ConfigurationNode>(copy);
+ Configuration<ConfigurationNode> conf2 = reload();
+ ConfigurationAssert.assertEquals(conf, conf2);
+ XMLConfigurationSource src = (XMLConfigurationSource) conf2
+ .getConfigurationSource();
+ assertEquals("Wrong name of root element", "element2", src
+ .getRootElementName());
+ }
+
+ /**
+ * Tests whether the name of the root element is copied when a
configuration
+ * is created using the copy constructor.
+ */
+ @Test
+ public void testInitCopyRootName() throws ConfigurationException,
+ IOException
+ {
+ final String rootName = "rootElement";
+ final String xml = "<" + rootName + "><test>true</test></" + rootName
+ + ">";
+ source.clear();
+ source.load(new StringReader(xml));
+ XMLConfigurationSource copy = new XMLConfigurationSource(source);
+ assertEquals("Wrong name of root element", rootName, copy
+ .getRootElementName());
+ copy.getCapability(LocatorSupport.class).save(testOutLocator);
+ copy = new XMLConfigurationSource();
+ copy.getCapability(LocatorSupport.class).load(testOutLocator);
+ assertEquals("Wrong name of root element after save", rootName, copy
+ .getRootElementName());
+ }
+
+ /**
+ * Tests whether the name of the root element is copied for a configuration
+ * for which not yet a document exists.
+ */
+ @Test
+ public void testInitCopyRootNameNoDocument() throws ConfigurationException
+ {
+ final String rootName = "rootElement";
+ source = new XMLConfigurationSource();
+ source.setRootElementName(rootName);
+ conf = new ConfigurationImpl<ConfigurationNode>(source);
+ conf.setProperty("test", Boolean.TRUE);
+ XMLConfigurationSource copy = new XMLConfigurationSource(source);
+ assertEquals("Wrong name of root element", rootName, copy
+ .getRootElementName());
+ copy.getCapability(LocatorSupport.class).save(testOutLocator);
+ copy = new XMLConfigurationSource();
+ copy.getCapability(LocatorSupport.class).load(testOutLocator);
+ assertEquals("Wrong name of root element after save", rootName, copy
+ .getRootElementName());
+ }
+
+ /**
+ * Tests the copy constructor if null is passed in.
+ */
+ @Test
+ public void testInitCopyNull()
+ {
+ source = new XMLConfigurationSource(null);
+ conf = new ConfigurationImpl<ConfigurationNode>(source);
+ assertTrue("Not empty", conf.isEmpty());
+ }
}