1 /*
2  * Copyright (C) 2001-2003 Peter J Jones (pjones@pmade.org)
3  * All Rights Reserved
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  * 3. Neither the name of the Author nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR
23  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /*
34  * $Id: node_manip.cpp 430203 2014-03-24 18:01:17Z satskyse $
35  * NOTE: This file was modified from its original version 0.6.0
36  *       to fit the NCBI C++ Toolkit build framework and
37  *       API and functionality requirements.
38  * Most importantly, it adds support for XML namespaces (see "namespace.hpp").
39  */
40 
41 /** @file
42  * This file contains the implementation of the xml::node manipulation
43  * functions.
44 **/
45 
46 // xmlwrapp includes
47 #include <misc/xmlwrapp/exception.hpp>
48 #include "node_manip.hpp"
49 
50 // libxml includes
51 #include <libxml/tree.h>
52 
53 //####################################################################
node_insert(xmlNodePtr parent,xmlNodePtr before,xmlNodePtr to_add)54 xmlNodePtr xml::impl::node_insert (xmlNodePtr parent, xmlNodePtr before, xmlNodePtr to_add) {
55     xmlNodePtr new_xml_node =  xmlCopyNode(to_add, 1);
56     if (!new_xml_node) throw std::bad_alloc();
57 
58     if (before == 0) { // insert at the end of the child list
59         if (xmlAddChild(parent, new_xml_node) == 0) {
60             xmlFreeNode(new_xml_node);
61             throw xml::exception("failed to insert xml::node; xmlAddChild failed");
62         }
63     } else {
64         if (xmlAddPrevSibling(before, new_xml_node) == 0) {
65             xmlFreeNode(new_xml_node);
66             throw xml::exception("failed to insert xml::node; xmlAddPrevSibling failed");
67         }
68     }
69     if (!new_xml_node->ns) new_xml_node->ns = xmlSearchNs(NULL, parent, NULL);
70     if (new_xml_node->ns) set_children_default_ns(new_xml_node, new_xml_node->ns);
71 
72     return new_xml_node;
73 }
74 //####################################################################
node_replace(xmlNodePtr old_node,xmlNodePtr new_node)75 xmlNodePtr xml::impl::node_replace (xmlNodePtr old_node, xmlNodePtr new_node) {
76     xmlNodePtr copied_node =  xmlCopyNode(new_node, 1);
77     if (!copied_node) throw std::bad_alloc();
78 
79     // hack to see if xmlReplaceNode was successful
80     copied_node->doc = reinterpret_cast<xmlDocPtr>(old_node);
81     xmlReplaceNode(old_node, copied_node);
82 
83     if (copied_node->doc == reinterpret_cast<xmlDocPtr>(old_node)) {
84         xmlFreeNode(copied_node);
85         throw xml::exception("failed to replace xml::node; xmlReplaceNode() failed");
86     }
87 
88     xmlFreeNode(old_node);
89 
90     if (!copied_node->ns) copied_node->ns = xmlSearchNs(NULL, copied_node->parent, NULL);
91     if (copied_node->ns) set_children_default_ns(copied_node, copied_node->ns);
92 
93     return copied_node;
94 }
95 //####################################################################
node_erase(xmlNodePtr to_erase)96 xmlNodePtr xml::impl::node_erase (xmlNodePtr to_erase) {
97     xmlNodePtr after = to_erase->next;
98 
99     xmlUnlinkNode(to_erase);
100     xmlFreeNode(to_erase);
101 
102     return after;
103 }
104 //####################################################################
set_children_default_ns(xmlNodePtr node,xmlNsPtr default_ns)105 void xml::impl::set_children_default_ns (xmlNodePtr node, xmlNsPtr default_ns) {
106     if (!node->ns)  node->ns = default_ns;
107     node = node->children;
108     while (node) {
109         if (!has_default_ns_definition(node)) {
110             set_children_default_ns(node, default_ns);
111             if (!node->ns) node->ns = default_ns;
112         }
113         node = node->next;
114     }
115 }
116 //####################################################################
has_default_ns_definition(xmlNodePtr node)117 bool xml::impl::has_default_ns_definition (xmlNodePtr node) {
118     if (!node ||!node->nsDef) return false;
119     xmlNs *     current(node->nsDef);
120     while (current) {
121         if (!current->prefix) return true;
122         current = current->next;
123     }
124     return false;
125 }
126 //####################################################################
is_ns_used(xmlNodePtr node,xmlNsPtr ns)127 bool xml::impl::is_ns_used (xmlNodePtr node, xmlNsPtr ns) {
128     if (!node) return false;
129 
130     // Does the node itself use namespace
131     if (node->ns == ns) return true;
132 
133     // Do the node attributes use namespace
134     for (xmlAttrPtr current = node->properties; current; current = current->next)
135         if (current->ns == ns) return true;
136 
137     node = node->children;
138     while (node) {
139         if (is_ns_used(node, ns)) return true;
140         node = node->next;
141     }
142     return false;
143 }
144 //####################################################################
update_children_default_ns(xmlNodePtr node,xmlNsPtr newns)145 void xml::impl::update_children_default_ns (xmlNodePtr node, xmlNsPtr newns) {
146     if (!node) return;
147     node = node->children;
148     while (node) {
149         if (!has_default_ns_definition(node)) {
150             update_children_default_ns(node, newns);
151             if (!node->ns || !node->ns->prefix)
152                 node->ns = newns;
153         }
154         node = node->next;
155     }
156 }
157 //####################################################################
erase_ns_definition(xmlNodePtr node,xmlNsPtr definition)158 void xml::impl::erase_ns_definition (xmlNodePtr node, xmlNsPtr definition) {
159     if (!node->nsDef) return;
160     if (node->nsDef != definition) {
161         xmlNs *prev(node->nsDef);
162         while (prev && prev->next != definition)
163             prev = prev->next;
164         if (!prev) return;
165         prev->next = definition->next;
166     }
167     else {
168         node->nsDef = definition->next;
169     }
170     xmlFreeNs(definition);
171 }
172 //####################################################################
lookup_ns_definition(xmlNodePtr node,const char * prefix)173 xmlNsPtr xml::impl::lookup_ns_definition (xmlNodePtr node, const char *prefix) {
174     xmlNs *current(node->nsDef);
175     while (current) {
176         if (!prefix && !current->prefix) return current;
177         if (prefix && current->prefix) {
178             if (xmlStrEqual(reinterpret_cast<const xmlChar*>(prefix), current->prefix))
179                 return current;
180         }
181         current = current->next;
182     }
183     return NULL;
184 }
185 //####################################################################
lookup_default_ns_above(xmlNodePtr node)186 xmlNsPtr xml::impl::lookup_default_ns_above (xmlNodePtr node) {
187     if (!node)
188         return NULL;
189 
190     xmlNs *current(node->nsDef);
191     while (current) {
192         if (!current->prefix)
193             return current;
194         current = current->next;
195     }
196     return lookup_default_ns_above(node->parent);
197 }
198 //####################################################################
replace_ns(xmlNodePtr node,xmlNsPtr oldNs,xmlNsPtr newNs)199 void xml::impl::replace_ns (xmlNodePtr node, xmlNsPtr oldNs, xmlNsPtr newNs) {
200     if (!node) return;
201 
202     // Does the node itself use namespace
203     if (node->ns == oldNs) node->ns = newNs;
204 
205     // Do the node attributes use namespace
206     for (xmlAttrPtr current = node->properties; current; current = current->next)
207         if (current->ns == oldNs) {
208             // Attributes may not have a default namspace
209             if (newNs && newNs->prefix)
210                 current->ns = newNs;
211             else
212                 current->ns = NULL;
213         }
214 
215     node = node->children;
216     while (node) {
217         replace_ns(node, oldNs, newNs);
218         node = node->next;
219     }
220 }
221 //####################################################################
222 
223