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" ))