summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorarseny.kapoulkine@gmail.com <arseny.kapoulkine@gmail.com@99668b35-9821-0410-8761-19e4c4f06640>2010-10-19 14:29:02 +0000
committerarseny.kapoulkine@gmail.com <arseny.kapoulkine@gmail.com@99668b35-9821-0410-8761-19e4c4f06640>2010-10-19 14:29:02 +0000
commitb979d4c2bd1e8f445befd53ea8357b594f1ca3b2 (patch)
treee4cefc7e257b796d07be70488e80f657d9e0160b
parentd8c19b201f93dc070fb37472f933b53e49b393bc (diff)
Added prepend_attribute, prepend_child and prepend_copy functions
git-svn-id: http://pugixml.googlecode.com/svn/trunk@769 99668b35-9821-0410-8761-19e4c4f06640
-rw-r--r--docs/manual.qbk16
-rw-r--r--src/pugixml.cpp71
-rw-r--r--src/pugixml.hpp4
-rw-r--r--tests/test_dom_modify.cpp105
4 files changed, 192 insertions, 4 deletions
diff --git a/docs/manual.qbk b/docs/manual.qbk
index fe45b30..430ba36 100644
--- a/docs/manual.qbk
+++ b/docs/manual.qbk
@@ -1059,18 +1059,20 @@ This is an example of setting attribute name and value ([@samples/modify_base.cp
[section:add Adding nodes/attributes]
-[#xml_node::append_attribute][#xml_node::insert_attribute_after][#xml_node::insert_attribute_before][#xml_node::append_child][#xml_node::insert_child_after][#xml_node::insert_child_before]
+[#xml_node::prepend_attribute][#xml_node::append_attribute][#xml_node::insert_attribute_after][#xml_node::insert_attribute_before][#xml_node::prepend_child][#xml_node::append_child][#xml_node::insert_child_after][#xml_node::insert_child_before]
Nodes and attributes do not exist outside of document tree, so you can't create them without adding them to some document. A node or attribute can be created at the end of node/attribute list or before\/after some other node:
xml_attribute xml_node::append_attribute(const char_t* name);
+ xml_attribute xml_node::prepend_attribute(const char_t* name);
xml_attribute xml_node::insert_attribute_after(const char_t* name, const xml_attribute& attr);
xml_attribute xml_node::insert_attribute_before(const char_t* name, const xml_attribute& attr);
xml_node xml_node::append_child(xml_node_type type = node_element);
+ xml_node xml_node::prepend_child(xml_node_type type = node_element);
xml_node xml_node::insert_child_after(xml_node_type type, const xml_node& node);
xml_node xml_node::insert_child_before(xml_node_type type, const xml_node& node);
-`append_attribute` and `append_child` create a new node/attribute at the end of the corresponding list of the node the method is called on; `insert_attribute_after`, `insert_attribute_before`, `insert_child_after` and `insert_attribute_before` add the node\/attribute before or after specified node\/attribute.
+`append_attribute` and `append_child` create a new node\/attribute at the end of the corresponding list of the node the method is called on; `prepend_attribute` and `prepend_child` create a new node\/attribute at the beginning of the list; `insert_attribute_after`, `insert_attribute_before`, `insert_child_after` and `insert_attribute_before` add the node\/attribute before or after specified node\/attribute.
Attribute functions create an attribute with the specified name; you can specify the empty name and change the name later if you want to. Node functions create the node with the specified type; since node type can't be changed, you have to know the desired type beforehand. Also note that not all types can be added as children; see below for clarification.
@@ -1127,17 +1129,19 @@ This is an example of removing attributes\/nodes from the document ([@samples/mo
[section:clone Cloning nodes/attributes]
-[#xml_node::append_copy][#xml_node::insert_copy_after][#xml_node::insert_copy_before]
+[#xml_node::prepend_copy][#xml_node::append_copy][#xml_node::insert_copy_after][#xml_node::insert_copy_before]
With the help of previously described functions, it is possible to create trees with any contents and structure, including cloning the existing data. However since this is an often needed operation, pugixml provides built-in node/attribute cloning facilities. Since nodes and attributes do not exist outside of document tree, you can't create a standalone copy - you have to immediately insert it somewhere in the tree. For this, you can use one of the following functions:
xml_attribute xml_node::append_copy(const xml_attribute& proto);
+ xml_attribute xml_node::prepend_copy(const xml_attribute& proto);
xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr);
xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr);
xml_node xml_node::append_copy(const xml_node& proto);
+ xml_node xml_node::prepend_copy(const xml_node& proto);
xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node);
xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node);
-These functions mirror the structure of `append_child`, `insert_child_before` and related functions - they take the handle to the prototype object, which is to be cloned, insert a new attribute\/node at the appropriate place, and then copy the attribute data or the whole node subtree to the new object. The functions return the handle to the resulting duplicate object, or null handle on failure.
+These functions mirror the structure of `append_child`, `prepend_child`, `insert_child_before` and related functions - they take the handle to the prototype object, which is to be cloned, insert a new attribute\/node at the appropriate place, and then copy the attribute data or the whole node subtree to the new object. The functions return the handle to the resulting duplicate object, or null handle on failure.
The attribute is copied along with the name and value; the node is copied along with its type, name and value; additionally attribute list and all children are recursively cloned, resulting in the deep subtree clone. The prototype object can be a part of the same document, or a part of any other document.
@@ -2051,21 +2055,25 @@ Classes:
[lbr]
* `xml_attribute `[link xml_node::append_attribute append_attribute]`(const char_t* name);`
+ * `xml_attribute `[link xml_node::prepend_attribute prepend_attribute]`(const char_t* name);`
* `xml_attribute `[link xml_node::insert_attribute_after insert_attribute_after]`(const char_t* name, const xml_attribute& attr);`
* `xml_attribute `[link xml_node::insert_attribute_before insert_attribute_before]`(const char_t* name, const xml_attribute& attr);`
[lbr]
* `xml_node `[link xml_node::append_child append_child]`(xml_node_type type = node_element);`
+ * `xml_node `[link xml_node::prepend_child prepend_child]`(xml_node_type type = node_element);`
* `xml_node `[link xml_node::insert_child_after insert_child_after]`(xml_node_type type, const xml_node& node);`
* `xml_node `[link xml_node::insert_child_before insert_child_before]`(xml_node_type type, const xml_node& node);`
[lbr]
* `xml_attribute `[link xml_node::append_copy append_copy]`(const xml_attribute& proto);`
+ * `xml_attribute `[link xml_node::prepend_copy prepend_copy]`(const xml_attribute& proto);`
* `xml_attribute `[link xml_node::insert_copy_after insert_copy_after]`(const xml_attribute& proto, const xml_attribute& attr);`
* `xml_attribute `[link xml_node::insert_copy_before insert_copy_before]`(const xml_attribute& proto, const xml_attribute& attr);`
[lbr]
* `xml_node `[link xml_node::append_copy append_copy]`(const xml_node& proto);`
+ * `xml_node `[link xml_node::prepend_copy prepend_copy]`(const xml_node& proto);`
* `xml_node `[link xml_node::insert_copy_after insert_copy_after]`(const xml_node& proto, const xml_node& node);`
* `xml_node `[link xml_node::insert_copy_before insert_copy_before]`(const xml_node& proto, const xml_node& node);`
[lbr]
diff --git a/src/pugixml.cpp b/src/pugixml.cpp
index f53ce1d..241eaed 100644
--- a/src/pugixml.cpp
+++ b/src/pugixml.cpp
@@ -3775,6 +3775,31 @@ namespace pugi
return a;
}
+ xml_attribute xml_node::prepend_attribute(const char_t* name)
+ {
+ if (type() != node_element && type() != node_declaration) return xml_attribute();
+
+ xml_attribute a(allocate_attribute(get_allocator(_root)));
+ if (!a) return xml_attribute();
+
+ a.set_name(name);
+
+ xml_attribute_struct* head = _root->first_attribute;
+
+ if (head)
+ {
+ a._attr->prev_attribute_c = head->prev_attribute_c;
+ head->prev_attribute_c = a._attr;
+ }
+ else
+ a._attr->prev_attribute_c = a._attr;
+
+ a._attr->next_attribute = head;
+ _root->first_attribute = a._attr;
+
+ return a;
+ }
+
xml_attribute xml_node::insert_attribute_before(const char_t* name, const xml_attribute& attr)
{
if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute();
@@ -3841,6 +3866,16 @@ namespace pugi
return result;
}
+ xml_attribute xml_node::prepend_copy(const xml_attribute& proto)
+ {
+ if (!proto) return xml_attribute();
+
+ xml_attribute result = prepend_attribute(proto.name());
+ result.set_value(proto.value());
+
+ return result;
+ }
+
xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr)
{
if (!proto) return xml_attribute();
@@ -3872,6 +3907,33 @@ namespace pugi
return n;
}
+ xml_node xml_node::prepend_child(xml_node_type type)
+ {
+ if (!allow_insert_child(this->type(), type)) return xml_node();
+
+ xml_node n(allocate_node(get_allocator(_root), type));
+ if (!n) return xml_node();
+
+ n._root->parent = _root;
+
+ xml_node_struct* head = _root->first_child;
+
+ if (head)
+ {
+ n._root->prev_sibling_c = head->prev_sibling_c;
+ head->prev_sibling_c = n._root;
+ }
+ else
+ n._root->prev_sibling_c = n._root;
+
+ n._root->next_sibling = head;
+ _root->first_child = n._root;
+
+ if (type == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
+
+ return n;
+ }
+
xml_node xml_node::insert_child_before(xml_node_type type, const xml_node& node)
{
if (!allow_insert_child(this->type(), type)) return xml_node();
@@ -3929,6 +3991,15 @@ namespace pugi
return result;
}
+ xml_node xml_node::prepend_copy(const xml_node& proto)
+ {
+ xml_node result = prepend_child(proto.type());
+
+ if (result) recursive_copy_skip(result, proto, result);
+
+ return result;
+ }
+
xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node)
{
xml_node result = insert_child_after(proto.type(), node);
diff --git a/src/pugixml.hpp b/src/pugixml.hpp
index 81fc6c3..cccfb7f 100644
--- a/src/pugixml.hpp
+++ b/src/pugixml.hpp
@@ -412,21 +412,25 @@ namespace pugi
// Add attribute with specified name. Returns added attribute, or empty attribute on errors.
xml_attribute append_attribute(const char_t* name);
+ xml_attribute prepend_attribute(const char_t* name);
xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr);
xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr);
// Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors.
xml_attribute append_copy(const xml_attribute& proto);
+ xml_attribute prepend_copy(const xml_attribute& proto);
xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr);
xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr);
// Add child node with specified type. Returns added node, or empty node on errors.
xml_node append_child(xml_node_type type = node_element);
+ xml_node prepend_child(xml_node_type type = node_element);
xml_node insert_child_after(xml_node_type type, const xml_node& node);
xml_node insert_child_before(xml_node_type type, const xml_node& node);
// Add a copy of the specified node as a child. Returns added node, or empty node on errors.
xml_node append_copy(const xml_node& proto);
+ xml_node prepend_copy(const xml_node& proto);
xml_node insert_copy_after(const xml_node& proto, const xml_node& node);
xml_node insert_copy_before(const xml_node& proto, const xml_node& node);
diff --git a/tests/test_dom_modify.cpp b/tests/test_dom_modify.cpp
index eefda58..b6155c1 100644
--- a/tests/test_dom_modify.cpp
+++ b/tests/test_dom_modify.cpp
@@ -78,6 +78,26 @@ TEST_XML(dom_node_set_value_allocated, "<node>text</node>")
CHECK_NODE(doc, STR("<node>no text at all</node>"));
}
+TEST_XML(dom_node_prepend_attribute, "<node><child/></node>")
+{
+ CHECK(xml_node().prepend_attribute(STR("a")) == xml_attribute());
+ CHECK(doc.prepend_attribute(STR("a")) == xml_attribute());
+
+ xml_attribute a1 = doc.child(STR("node")).prepend_attribute(STR("a1"));
+ CHECK(a1);
+ a1 = STR("v1");
+
+ xml_attribute a2 = doc.child(STR("node")).prepend_attribute(STR("a2"));
+ CHECK(a2 && a1 != a2);
+ a2 = STR("v2");
+
+ xml_attribute a3 = doc.child(STR("node")).child(STR("child")).prepend_attribute(STR("a3"));
+ CHECK(a3 && a1 != a3 && a2 != a3);
+ a3 = STR("v3");
+
+ CHECK_NODE(doc, STR("<node a2=\"v2\" a1=\"v1\"><child a3=\"v3\" /></node>"));
+}
+
TEST_XML(dom_node_append_attribute, "<node><child/></node>")
{
CHECK(xml_node().append_attribute(STR("a")) == xml_attribute());
@@ -158,6 +178,42 @@ TEST_XML(dom_node_insert_attribute_before, "<node a1='v1'><child a2='v2'/></node
CHECK_NODE(doc, STR("<node a5=\"v5\" a3=\"v3\" a4=\"v4\" a1=\"v1\"><child a2=\"v2\" /></node>"));
}
+TEST_XML(dom_node_prepend_copy_attribute, "<node a1='v1'><child a2='v2'/><child/></node>")
+{
+ CHECK(xml_node().prepend_copy(xml_attribute()) == xml_attribute());
+ CHECK(xml_node().prepend_copy(doc.child(STR("node")).attribute(STR("a1"))) == xml_attribute());
+ CHECK(doc.prepend_copy(doc.child(STR("node")).attribute(STR("a1"))) == xml_attribute());
+ CHECK(doc.child(STR("node")).prepend_copy(xml_attribute()) == xml_attribute());
+
+ xml_node node = doc.child(STR("node"));
+ xml_node child = node.child(STR("child"));
+
+ xml_attribute a1 = node.attribute(STR("a1"));
+ xml_attribute a2 = child.attribute(STR("a2"));
+
+ xml_attribute a3 = node.prepend_copy(a1);
+ CHECK(a3 && a3 != a2 && a3 != a1);
+
+ xml_attribute a4 = node.prepend_copy(a2);
+ CHECK(a4 && a4 != a3 && a4 != a2 && a4 != a1);
+
+ xml_attribute a5 = node.last_child().prepend_copy(a1);
+ CHECK(a5 && a5 != a4 && a5 != a3 && a5 != a2 && a5 != a1);
+
+ CHECK_NODE(doc, STR("<node a2=\"v2\" a1=\"v1\" a1=\"v1\"><child a2=\"v2\" /><child a1=\"v1\" /></node>"));
+
+ a3.set_name(STR("a3"));
+ a3 = STR("v3");
+
+ a4.set_name(STR("a4"));
+ a4 = STR("v4");
+
+ a5.set_name(STR("a5"));
+ a5 = STR("v5");
+
+ CHECK_NODE(doc, STR("<node a4=\"v4\" a3=\"v3\" a1=\"v1\"><child a2=\"v2\" /><child a5=\"v5\" /></node>"));
+}
+
TEST_XML(dom_node_append_copy_attribute, "<node a1='v1'><child a2='v2'/><child/></node>")
{
CHECK(xml_node().append_copy(xml_attribute()) == xml_attribute());
@@ -293,6 +349,32 @@ TEST_XML(dom_node_remove_attribute, "<node a1='v1' a2='v2' a3='v3'><child a4='v4
CHECK_NODE(doc, STR("<node a2=\"v2\"><child /></node>"));
}
+TEST_XML(dom_node_prepend_child, "<node>foo<child/></node>")
+{
+ CHECK(xml_node().prepend_child() == xml_node());
+ CHECK(doc.child(STR("node")).first_child().prepend_child() == xml_node());
+ CHECK(doc.prepend_child(node_document) == xml_node());
+ CHECK(doc.prepend_child(node_null) == xml_node());
+
+ xml_node n1 = doc.child(STR("node")).prepend_child();
+ CHECK(n1);
+ CHECK(n1.set_name(STR("n1")));
+
+ xml_node n2 = doc.child(STR("node")).prepend_child();
+ CHECK(n2 && n1 != n2);
+ CHECK(n2.set_name(STR("n2")));
+
+ xml_node n3 = doc.child(STR("node")).child(STR("child")).prepend_child(node_pcdata);
+ CHECK(n3 && n1 != n3 && n2 != n3);
+ CHECK(n3.set_value(STR("n3")));
+
+ xml_node n4 = doc.prepend_child(node_comment);
+ CHECK(n4 && n1 != n4 && n2 != n4 && n3 != n4);
+ CHECK(n4.set_value(STR("n4")));
+
+ CHECK_NODE(doc, STR("<!--n4--><node><n2 /><n1 />foo<child>n3</child></node>"));
+}
+
TEST_XML(dom_node_append_child, "<node>foo<child/></node>")
{
CHECK(xml_node().append_child() == xml_node());
@@ -429,6 +511,29 @@ TEST_XML(dom_node_remove_child_complex_allocated, "<node id='1'><n1 id1='1' id2=
CHECK_NODE(doc, STR(""));
}
+TEST_XML(dom_node_prepend_copy, "<node>foo<child/></node>")
+{
+ CHECK(xml_node().prepend_copy(xml_node()) == xml_node());
+ CHECK(doc.child(STR("node")).first_child().prepend_copy(doc.child(STR("node"))) == xml_node());
+ CHECK(doc.prepend_copy(doc) == xml_node());
+ CHECK(doc.prepend_copy(xml_node()) == xml_node());
+
+ xml_node n1 = doc.child(STR("node")).prepend_copy(doc.child(STR("node")).first_child());
+ CHECK(n1);
+ CHECK_STRING(n1.value(), STR("foo"));
+ CHECK_NODE(doc, STR("<node>foofoo<child /></node>"));
+
+ xml_node n2 = doc.child(STR("node")).prepend_copy(doc.child(STR("node")).child(STR("child")));
+ CHECK(n2 && n2 != n1);
+ CHECK_STRING(n2.name(), STR("child"));
+ CHECK_NODE(doc, STR("<node><child />foofoo<child /></node>"));
+
+ xml_node n3 = doc.child(STR("node")).child(STR("child")).prepend_copy(doc.child(STR("node")).first_child().next_sibling());
+ CHECK(n3 && n3 != n1 && n3 != n2);
+ CHECK_STRING(n3.value(), STR("foo"));
+ CHECK_NODE(doc, STR("<node><child>foo</child>foofoo<child /></node>"));
+}
+
TEST_XML(dom_node_append_copy, "<node>foo<child/></node>")
{
CHECK(xml_node().append_copy(xml_node()) == xml_node());