Merge pull request #558 from leethomason/clone

Support clone (deep copy) of XMLDocument and XMLNode
diff --git a/tinyxml2.cpp b/tinyxml2.cpp
index 99b5e92..772d2ae 100755
--- a/tinyxml2.cpp
+++ b/tinyxml2.cpp
@@ -772,6 +772,18 @@
     }

 }

 

+XMLNode* XMLNode::DeepClone(XMLDocument* document) const

+{

+	XMLNode* clone = this->ShallowClone(document);

+	if (!clone) return 0;

+

+	for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) {

+		XMLNode* childClone = child->DeepClone(document);

+		TIXMLASSERT(childClone);

+		clone->InsertEndChild(childClone);

+	}

+	return clone;

+}

 

 void XMLNode::DeleteChildren()

 {

@@ -2038,6 +2050,19 @@
 }

 

 

+void XMLDocument::DeepCopy(XMLDocument* target)

+{

+	TIXMLASSERT(target);

+    if (target == this) {

+        return; // technically success - a no-op.

+    }

+

+	target->Clear();

+	for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) {

+		target->InsertEndChild(node->DeepClone(target));

+	}

+}

+

 XMLElement* XMLDocument::NewElement( const char* name )

 {

     XMLElement* ele = CreateUnlinkedNode<XMLElement>( _elementPool );

diff --git a/tinyxml2.h b/tinyxml2.h
index fd7f6b4..3465f2f 100755
--- a/tinyxml2.h
+++ b/tinyxml2.h
@@ -53,7 +53,7 @@
         AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h

 */

 

-#if defined( _DEBUG ) || defined( DEBUG ) || defined (__DEBUG__)

+#if defined( _DEBUG ) || defined (__DEBUG__)

 #   ifndef DEBUG

 #       define DEBUG

 #   endif

@@ -865,6 +865,21 @@
     */

     virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0;

 

+	/**

+		Make a copy of this node and all its children.

+

+		If the 'document' is null, then the nodes will

+		be allocated in the current document. If document 

+        is specified, the memory will be allocated is the 

+        specified XMLDocument.

+

+		NOTE: This is probably not the correct tool to 

+		copy a document, since XMLDocuments can have multiple

+		top level XMLNodes. You probably want to use

+        XMLDocument::DeepCopy()

+	*/

+	XMLNode* DeepClone( XMLDocument* document ) const;

+

     /**

     	Test if 2 nodes are the same, but don't test children.

     	The 2 nodes do not need to be in the same Document.

@@ -1806,7 +1821,16 @@
     /// Clear the document, resetting it to the initial state.

     void Clear();

 

-    // internal

+	/**

+		Copies this document to a target document.

+		The target will be completely cleared before the copy.

+		If you want to copy a sub-tree, see XMLNode::DeepClone().

+

+		NOTE: that the 'target' must be non-null.

+	*/

+	void DeepCopy(XMLDocument* target);

+

+	// internal

     char* Identify( char* p, XMLNode** node );

 

 	// internal

diff --git a/xmltest.cpp b/xmltest.cpp
index d892a9e..550bf75 100644
--- a/xmltest.cpp
+++ b/xmltest.cpp
@@ -1131,6 +1131,86 @@
 	}

 

 	{

+		// Deep Cloning of root element.

+		XMLDocument doc2;

+		XMLPrinter printer1;

+		{

+			// Make sure doc1 is deleted before we test doc2

+			const char* xml =

+				"<root>"

+				"    <child1 foo='bar'/>"

+				"    <!-- comment thing -->"

+				"    <child2 val='1'>Text</child2>"

+				"</root>";

+			XMLDocument doc;

+			doc.Parse(xml);

+

+			doc.Print(&printer1);

+			XMLNode* root = doc.RootElement()->DeepClone(&doc2);

+			doc2.InsertFirstChild(root);

+		}

+		XMLPrinter printer2;

+		doc2.Print(&printer2);

+

+		XMLTest("Deep clone of element.", printer1.CStr(), printer2.CStr(), true);

+	}

+

+	{

+		// Deep Cloning of sub element.

+		XMLDocument doc2;

+		XMLPrinter printer1;

+		{

+			// Make sure doc1 is deleted before we test doc2

+			const char* xml =

+				"<?xml version ='1.0'?>"

+				"<root>"

+				"    <child1 foo='bar'/>"

+				"    <!-- comment thing -->"

+				"    <child2 val='1'>Text</child2>"

+				"</root>";

+			XMLDocument doc;

+			doc.Parse(xml);

+

+			const XMLElement* subElement = doc.FirstChildElement("root")->FirstChildElement("child2");

+			subElement->Accept(&printer1);

+

+			XMLNode* clonedSubElement = subElement->DeepClone(&doc2);

+			doc2.InsertFirstChild(clonedSubElement);

+		}

+		XMLPrinter printer2;

+		doc2.Print(&printer2);

+

+		XMLTest("Deep clone of sub-element.", printer1.CStr(), printer2.CStr(), true);

+	}

+

+	{

+		// Deep cloning of document.

+		XMLDocument doc2;

+		XMLPrinter printer1;

+		{

+			// Make sure doc1 is deleted before we test doc2

+			const char* xml =

+				"<?xml version ='1.0'?>"

+				"<!-- Top level comment. -->"

+				"<root>"

+				"    <child1 foo='bar'/>"

+				"    <!-- comment thing -->"

+				"    <child2 val='1'>Text</child2>"

+				"</root>";

+			XMLDocument doc;

+			doc.Parse(xml);

+			doc.Print(&printer1);

+

+			doc.DeepCopy(&doc2);

+		}

+		XMLPrinter printer2;

+		doc2.Print(&printer2);

+

+		XMLTest("DeepCopy of document.", printer1.CStr(), printer2.CStr(), true);

+	}

+

+

+ 	{

 		// This shouldn't crash.

 		XMLDocument doc;

 		if(XML_SUCCESS != doc.LoadFile( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ))