From fd7326fb9153807bc415d960ef33f2041ac722c7 Mon Sep 17 00:00:00 2001 From: Tuan Anh Tran Date: Wed, 18 Sep 2019 10:18:41 +0700 Subject: [PATCH] feat: add remove_attributes() and remove_children() (#296) These functions remove all attributes / child nodes in bulk which is faster than removing them one at a time. --- docs/manual.adoc | 8 ++++++-- src/pugixml.cpp | 42 +++++++++++++++++++++++++++++++++++++++ src/pugixml.hpp | 6 ++++++ tests/test_dom_modify.cpp | 26 ++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 2 deletions(-) diff --git a/docs/manual.adoc b/docs/manual.adoc index def9ced..5a391ed 100644 --- a/docs/manual.adoc +++ b/docs/manual.adoc @@ -1378,16 +1378,18 @@ include::samples/modify_add.cpp[tags=code] [[modify.remove]] === Removing nodes/attributes -[[xml_node::remove_attribute]][[xml_node::remove_child]] +[[xml_node::remove_attribute]][[xml_node::remove_attributes]][[xml_node::remove_child]][[xml_node::remove_children]] If you do not want your document to contain some node or attribute, you can remove it with one of the following functions: [source] ---- bool xml_node::remove_attribute(const xml_attribute& a); +bool xml_node::remove_attributes(); bool xml_node::remove_child(const xml_node& n); +bool xml_node::remove_children(); ---- -`remove_attribute` removes the attribute from the attribute list of the node, and returns the operation result. `remove_child` removes the child node with the entire subtree (including all descendant nodes and attributes) from the document, and returns the operation result. Removing fails if one of the following is true: +`remove_attribute` removes the attribute from the attribute list of the node, and returns the operation result. `remove_child` removes the child node with the entire subtree (including all descendant nodes and attributes) from the document, and returns the operation result. `remove_attributes` removes all the attributes of the node, and returns the operation result. `remove_children` removes all the child nodes of the node, and returns the operation result. Removing fails if one of the following is true: * The node the function is called on is null; * The attribute/node to be removed is null; @@ -2868,8 +2870,10 @@ const unsigned int +++parse_wnorm_attribute bool +++remove_attribute+++(const xml_attribute& a); bool +++remove_attribute+++(const char_t* name); + bool +++remove_attributes+++(); bool +++remove_child+++(const xml_node& n); bool +++remove_child+++(const char_t* name); + bool +++remove_children+++(); xml_parse_result +++append_buffer+++(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 90c48b2..39e07f1 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -6059,6 +6059,27 @@ namespace pugi return true; } + PUGI__FN bool xml_node::remove_attributes() + { + if (!_root) return false; + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return false; + + for (xml_attribute_struct* attr = _root->first_attribute; attr;) + { + xml_attribute_struct* next = attr->next_attribute; + + impl::destroy_attribute(attr, alloc); + + attr = next; + } + + _root->first_attribute = 0; + + return true; + } + PUGI__FN bool xml_node::remove_child(const char_t* name_) { return remove_child(child(name_)); @@ -6077,6 +6098,27 @@ namespace pugi return true; } + PUGI__FN bool xml_node::remove_children() + { + if (!_root) return false; + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return false; + + for (xml_node_struct* child = _root->first_child; child; ) + { + xml_node_struct* next = child->next_sibling; + + destroy_node(child, alloc); + + child = next; + } + + _root->first_child = 0; + + return true; + } + PUGI__FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) { // append_buffer is only valid for elements/documents diff --git a/src/pugixml.hpp b/src/pugixml.hpp index da8ff36..8986d2a 100644 --- a/src/pugixml.hpp +++ b/src/pugixml.hpp @@ -576,10 +576,16 @@ namespace pugi bool remove_attribute(const xml_attribute& a); bool remove_attribute(const char_t* name); + // Remove all attributes + bool remove_attributes(); + // Remove specified child bool remove_child(const xml_node& n); bool remove_child(const char_t* name); + // Remove all children + bool remove_children(); + // Parses buffer as an XML document fragment and appends all nodes as children of the current node. // Copies/converts the buffer, so it may be deleted or changed after the function returns. // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory. diff --git a/tests/test_dom_modify.cpp b/tests/test_dom_modify.cpp index 9696827..d2f1a3c 100644 --- a/tests/test_dom_modify.cpp +++ b/tests/test_dom_modify.cpp @@ -492,6 +492,19 @@ TEST_XML(dom_node_remove_attribute, "") +{ + CHECK(!xml_node().remove_attributes()); + xml_node node = doc.child(STR("node")); + xml_node child = node.child(STR("child")); + + CHECK(child.remove_attributes()); + CHECK_NODE(child, STR("")); + + CHECK(node.remove_attributes()); + CHECK_NODE(node, STR("")); +} + TEST_XML(dom_node_prepend_child, "foo") { CHECK(xml_node().prepend_child() == xml_node()); @@ -707,6 +720,19 @@ TEST_XML(dom_node_remove_child, "")); } +TEST_XML(dom_node_remove_children, "") +{ + CHECK(!xml_node().remove_children()); + xml_node node = doc.child(STR("node")); + xml_node child = node.child(STR("child")); + + CHECK(child.remove_children()); + CHECK_NODE(child, STR("")); + + CHECK(node.remove_children()); + CHECK_NODE(node, STR("")); +} + TEST_XML(dom_node_remove_child_complex, "") { CHECK(doc.child(STR("node")).remove_child(STR("n1")));