Imported Upstream version 2.91.2
[platform/upstream/libxml++.git] / libxml++ / nodes / element.cc
1 /* element.cc
2  * libxml++ and this file are copyright (C) 2000 by Ari Johnson, and
3  * are covered by the GNU Lesser General Public License, which should be
4  * included with libxml++ as the file COPYING.
5  */
6
7 #include <libxml++/nodes/element.h>
8 #include <libxml++/exceptions/internal_error.h>
9
10 #include <libxml/tree.h>
11
12 namespace // anonymous
13 {
14 // Common part of all add_child_element*() methods.
15 xmlpp::Element* add_child_element_common(const Glib::ustring& name, xmlNode* child, xmlNode* node)
16 {
17   if (!node)
18   {
19     xmlFreeNode(child);
20     throw xmlpp::internal_error("Could not add child element node " + name);
21   }
22   xmlpp::Node::create_wrapper(node);
23   return static_cast<xmlpp::Element*>(node->_private);
24 }
25
26 } // anonymous namespace
27
28 namespace xmlpp
29 {
30
31 Element::Element(xmlNode* node)
32 : Node(node)
33 {}
34
35 Element::~Element()
36 {}
37
38 Element::AttributeList Element::get_attributes()
39 {
40   AttributeList attributes;
41   for (auto attr = cobj()->properties; attr; attr = attr->next)
42   {
43     Node::create_wrapper(reinterpret_cast<xmlNode*>(attr));
44     attributes.push_back(reinterpret_cast<Attribute*>(attr->_private));
45   }
46   return attributes;
47 }
48
49 Element::const_AttributeList Element::get_attributes() const
50 {
51   const_AttributeList attributes;
52   for (auto attr = cobj()->properties; attr; attr = attr->next)
53   {
54     Node::create_wrapper(reinterpret_cast<xmlNode*>(attr));
55     attributes.push_back(reinterpret_cast<const Attribute*>(attr->_private));
56   }
57   return attributes;
58 }
59
60 Attribute* Element::get_attribute(const Glib::ustring& name,
61                                   const Glib::ustring& ns_prefix)
62 {
63   // An empty ns_prefix means "use no namespace".
64   // The default namespace never applies to an attribute.
65   Glib::ustring ns_uri;
66   if (!ns_prefix.empty())
67   {
68     ns_uri = get_namespace_uri_for_prefix(ns_prefix);
69     if (ns_uri.empty())
70       return nullptr; // No such prefix.
71   }
72
73   // The return value of xmlHasNsProp() may be either an xmlAttr*, pointing to an
74   // explicitly set attribute (XML_ATTRIBUTE_NODE), or an xmlAttribute*,
75   // cast to an xmlAttr*, pointing to the declaration of an attribute with a
76   // default value (XML_ATTRIBUTE_DECL).
77   auto attr = xmlHasNsProp(const_cast<xmlNode*>(cobj()), (const xmlChar*)name.c_str(),
78                                ns_uri.empty() ? nullptr : (const xmlChar*)ns_uri.c_str());
79   if (attr)
80   {
81     Node::create_wrapper(reinterpret_cast<xmlNode*>(attr));
82     return reinterpret_cast<Attribute*>(attr->_private);
83   }
84
85   return nullptr;
86 }
87
88 const Attribute* Element::get_attribute(const Glib::ustring& name,
89                                         const Glib::ustring& ns_prefix) const
90 {
91   return const_cast<Element*>(this)->get_attribute(name, ns_prefix);
92 }
93
94 Glib::ustring Element::get_attribute_value(const Glib::ustring& name, const Glib::ustring& ns_prefix) const
95 {
96   const auto attr = get_attribute(name, ns_prefix);
97   return attr ? attr->get_value() : Glib::ustring();
98 }
99
100 Attribute* Element::set_attribute(const Glib::ustring& name, const Glib::ustring& value,
101                                   const Glib::ustring& ns_prefix)
102 {
103   xmlAttr* attr = nullptr;
104
105   //Ignore the namespace if none was specified:
106   if(ns_prefix.empty())
107   {
108     attr = xmlSetProp(cobj(), (const xmlChar*)name.c_str(), (const xmlChar*)value.c_str());
109   }
110   else
111   {
112     //If the namespace exists, then use it:
113     auto ns = xmlSearchNs(cobj()->doc, cobj(), (const xmlChar*)ns_prefix.c_str());
114     if (ns)
115     {
116       attr = xmlSetNsProp(cobj(), ns, (const xmlChar*)name.c_str(),
117                           (const xmlChar*)value.c_str());
118     }
119     else
120     {
121       throw exception("The namespace prefix (" + ns_prefix + ") has not been declared.");
122     }
123   }
124
125   if(attr)
126   {
127     Node::create_wrapper(reinterpret_cast<xmlNode*>(attr));
128     return reinterpret_cast<Attribute*>(attr->_private);
129   }
130   else
131     return nullptr;
132 }
133
134 void Element::remove_attribute(const Glib::ustring& name, const Glib::ustring& ns_prefix)
135 {
136   if (ns_prefix.empty())
137     xmlUnsetProp(cobj(), (const xmlChar*)name.c_str());
138   else
139   {
140     auto ns = xmlSearchNs(cobj()->doc, cobj(), (const xmlChar*)ns_prefix.c_str());
141     if (ns)
142       xmlUnsetNsProp(cobj(), ns, (const xmlChar*)name.c_str());
143   }
144 }
145
146 Element* Element::add_child_element(const Glib::ustring& name,
147   const Glib::ustring& ns_prefix)
148 {
149   auto child = create_new_child_element_node(name, ns_prefix);
150   auto node = xmlAddChild(cobj(), child);
151   return add_child_element_common(name, child, node);
152 }
153
154 Element* Element::add_child_element(xmlpp::Node* previous_sibling, 
155   const Glib::ustring& name, const Glib::ustring& ns_prefix)
156 {
157   if (!previous_sibling)
158     return nullptr;
159
160   auto child = create_new_child_element_node(name, ns_prefix);
161   auto node = xmlAddNextSibling(previous_sibling->cobj(), child);
162   return add_child_element_common(name, child, node);
163 }
164
165 Element* Element::add_child_element_before(xmlpp::Node* next_sibling, 
166   const Glib::ustring& name, const Glib::ustring& ns_prefix)
167 {
168   if (!next_sibling)
169     return nullptr;
170
171   auto child = create_new_child_element_node(name, ns_prefix);
172   auto node = xmlAddPrevSibling(next_sibling->cobj(), child);
173   return add_child_element_common(name, child, node);
174 }
175
176 Element* Element::add_child_element_with_new_ns(const Glib::ustring& name,
177   const Glib::ustring& ns_uri, const Glib::ustring& ns_prefix)
178 {
179   auto child = create_new_child_element_node_with_new_ns(name, ns_uri, ns_prefix);
180   auto node = xmlAddChild(cobj(), child);
181   return add_child_element_common(name, child, node);
182 }
183
184 Element* Element::add_child_element_with_new_ns(xmlpp::Node* previous_sibling,
185   const Glib::ustring& name,
186   const Glib::ustring& ns_uri, const Glib::ustring& ns_prefix)
187 {
188   if (!previous_sibling)
189     return nullptr;
190
191   auto child = create_new_child_element_node_with_new_ns(name, ns_uri, ns_prefix);
192   auto node = xmlAddNextSibling(previous_sibling->cobj(), child);
193   return add_child_element_common(name, child, node);
194 }
195
196 Element* Element::add_child_element_before_with_new_ns(xmlpp::Node* next_sibling,
197   const Glib::ustring& name,
198   const Glib::ustring& ns_uri, const Glib::ustring& ns_prefix)
199 {
200   if (!next_sibling)
201     return nullptr;
202
203   auto child = create_new_child_element_node_with_new_ns(name, ns_uri, ns_prefix);
204   auto node = xmlAddPrevSibling(next_sibling->cobj(), child);
205   return add_child_element_common(name, child, node);
206 }
207
208 _xmlNode* Element::create_new_child_element_node(const Glib::ustring& name,
209   const Glib::ustring& ns_prefix)
210 {
211    xmlNs* ns = nullptr;
212
213    if (cobj()->type != XML_ELEMENT_NODE)
214      throw internal_error("You can only add child nodes to element nodes");
215
216    if (ns_prefix.empty())
217    {
218      //Retrieve default namespace if it exists
219      ns = xmlSearchNs(cobj()->doc, cobj(), nullptr);
220    }
221    else
222    {
223      //Use the existing namespace if one exists:
224      ns = xmlSearchNs(cobj()->doc, cobj(), (const xmlChar*)ns_prefix.c_str());
225      if (!ns)
226        throw exception("The namespace prefix (" + ns_prefix + ") has not been declared.");
227    }
228
229    return xmlNewNode(ns, (const xmlChar*)name.c_str());
230 }
231
232 _xmlNode* Element::create_new_child_element_node_with_new_ns(const Glib::ustring& name,
233   const Glib::ustring& ns_uri, const Glib::ustring& ns_prefix)
234 {
235   if (cobj()->type != XML_ELEMENT_NODE)
236     throw internal_error("You can only add child nodes to element nodes.");
237
238   auto child = xmlNewNode(nullptr, (const xmlChar*)name.c_str());
239   if (!child)
240     throw internal_error("Could not create new element node.");
241
242   auto ns = xmlNewNs(child, (const xmlChar*)(ns_uri.empty() ? nullptr : ns_uri.c_str()),
243                        (const xmlChar*)(ns_prefix.empty() ? nullptr : ns_prefix.c_str()) );
244   // xmlNewNs() does not create a namespace node for the predefined xml prefix.
245   // It's usually defined in the document and not in any specific node.
246   if (!ns && ns_prefix == "xml")
247   {
248     ns = xmlSearchNs(cobj()->doc, cobj(), (const xmlChar*)ns_prefix.c_str());
249     if (ns && (ns_uri != (ns->href ? (const char*)ns->href : "")))
250       ns = nullptr;
251   }
252   if (!ns)
253   {
254     xmlFreeNode(child);
255     throw internal_error("Could not create new namespace node.");
256   }
257
258   xmlSetNs(child, ns);
259
260   return child;
261 }
262
263 TextNode* Element::get_first_child_text()
264 {
265   for (auto child = cobj()->children; child; child = child->next)
266      if (child->type == XML_TEXT_NODE)
267      {
268        Node::create_wrapper(child);
269        return static_cast<TextNode*>(child->_private);
270      }
271
272   return nullptr;
273 }
274
275 const TextNode* Element::get_first_child_text() const
276 {
277   return const_cast<Element*>(this)->get_first_child_text();
278 }
279
280 void Element::set_first_child_text(const Glib::ustring& content)
281 {
282   auto node = get_first_child_text();
283   if(node)
284     node->set_content(content);
285   else
286     add_child_text(content);
287 }
288
289 TextNode* Element::add_child_text(const Glib::ustring& content)
290 {
291   if(cobj()->type == XML_ELEMENT_NODE)
292   {
293     auto child = xmlNewText((const xmlChar*)content.c_str());
294
295     // Use the result, because child can be freed when merging text nodes:
296     auto node = xmlAddChild(cobj(), child);
297     if (!node)
298     {
299       xmlFreeNode(child);
300       throw internal_error("Could not add text node \"" + content + "\"");
301     }
302     Node::create_wrapper(node);
303     return static_cast<TextNode*>(node->_private);
304   }
305   return nullptr;
306 }
307
308 TextNode* Element::add_child_text(xmlpp::Node* previous_sibling, const Glib::ustring& content)
309 {
310   if(!previous_sibling)
311     return nullptr;
312
313   if(cobj()->type == XML_ELEMENT_NODE)
314   {
315     auto child = xmlNewText((const xmlChar*)content.c_str());
316
317     // Use the result, because child can be freed when merging text nodes:
318     auto node = xmlAddNextSibling(previous_sibling->cobj(), child);
319     if (!node)
320     {
321       xmlFreeNode(child);
322       throw internal_error("Could not add text node \"" + content + "\"");
323     }
324     Node::create_wrapper(node);
325     return static_cast<TextNode*>(node->_private);
326   }
327   return nullptr;
328 }
329
330 TextNode* Element::add_child_text_before(xmlpp::Node* next_sibling, const Glib::ustring& content)
331 {
332   if(!next_sibling)
333     return nullptr;
334
335   if(cobj()->type == XML_ELEMENT_NODE)
336   {
337     auto child = xmlNewText((const xmlChar*)content.c_str());
338
339     // Use the result, because child can be freed when merging text nodes:
340     auto node = xmlAddPrevSibling(next_sibling->cobj(), child);
341     if (!node)
342     {
343       xmlFreeNode(child);
344       throw internal_error("Could not add text node \"" + content + "\"");
345     }
346     Node::create_wrapper(node);
347     return static_cast<TextNode*>(node->_private);
348   }
349   return nullptr;
350 }
351
352 bool Element::has_child_text() const
353 {
354   return get_first_child_text() != nullptr;
355 }
356
357 void Element::set_namespace_declaration(const Glib::ustring& ns_uri, const Glib::ustring& ns_prefix)
358 {
359   //Create a new namespace declaration for this element:
360   auto ns = xmlNewNs(cobj(), (const xmlChar*)(ns_uri.empty() ? nullptr : ns_uri.c_str()),
361                        (const xmlChar*)(ns_prefix.empty() ? nullptr : ns_prefix.c_str()) );
362   if (!ns)
363   {
364     // Not an error, if we try to assign the same uri to the prefix once again.
365     ns = xmlSearchNs(cobj()->doc, cobj(),
366                      (const xmlChar*)(ns_prefix.empty() ? nullptr : ns_prefix.c_str()));
367     const char* const previous_href = (ns && ns->href) ? (const char*)ns->href : "";
368     if (!ns || ns_uri != previous_href)
369       throw exception("Could not add namespace declaration with URI=" + ns_uri +
370                       ", prefix=" + ns_prefix);
371   }
372   if (ns_prefix == get_namespace_prefix())
373   {
374     // Assign namespace directly if the prefix equals the node's own namespace prefix.
375     // This is not done by xmlNewNs(). (See https://bugzilla.gnome.org/show_bug.cgi?id=737682)
376     set_namespace(ns_prefix);
377   }
378   //We ignore the returned xmlNs*. It's owned by the XML_ELEMENT_NODE.
379 }
380
381 Glib::ustring Element::get_namespace_uri_for_prefix(const Glib::ustring& ns_prefix) const
382 {
383   Glib::ustring result;
384   
385   //Find the namespace:
386   const auto ns = xmlSearchNs( cobj()->doc, const_cast<xmlNode*>(cobj()), (xmlChar*)ns_prefix.c_str() );
387   if(ns)
388   {
389     //Get the namespace URI associated with this prefix:
390     if(ns && ns->href)
391       result = (const char*)ns->href;
392   }
393   
394   return result;
395 }
396
397 CommentNode* Element::add_child_comment(const Glib::ustring& content)
398 {
399   auto child = xmlNewComment((const xmlChar*)content.c_str());
400  
401   // Use the result, because child can be freed when merging text nodes:
402   auto node = xmlAddChild(cobj(), child);
403   if (!node)
404   {
405     xmlFreeNode(child);
406     throw internal_error("Could not add comment node \"" + content + "\"");
407   }
408   Node::create_wrapper(node);
409   return static_cast<CommentNode*>(node->_private);
410 }
411
412 CdataNode* Element::add_child_cdata(const Glib::ustring& content)
413 {
414   auto child = xmlNewCDataBlock(cobj()->doc, (const xmlChar*)content.c_str(), content.bytes());
415   auto node = xmlAddChild(cobj(), child);
416   if (!node)
417   {
418     xmlFreeNode(child);
419     throw internal_error("Could not add CDATA node \"" + content + "\"");
420   }
421   Node::create_wrapper(node);
422   return static_cast<CdataNode*>(node->_private);
423 }
424
425 EntityReference* Element::add_child_entity_reference(const Glib::ustring& name)
426 {
427   const auto extended_name = name + "  "; // This is at least two chars long.
428   int ichar = 0;
429   if (extended_name[ichar] == '&')
430     ++ichar;
431
432   // Is it an entity reference or a character reference?
433   // libxml uses xmlNode::type == XML_ENTITY_REF_NODE for both.
434   xmlNode* child = nullptr;
435   if (extended_name[ichar] == '#')
436     child = xmlNewCharRef(cobj()->doc, (const xmlChar*)name.c_str());
437   else
438     child = xmlNewReference(cobj()->doc, (const xmlChar*)name.c_str());
439   auto node = xmlAddChild(cobj(), child);
440   if (!node)
441   {
442     xmlFreeNode(child);
443     throw internal_error("Could not add entity reference node " + name);
444   }
445   Node::create_wrapper(node);
446   return static_cast<EntityReference*>(node->_private);
447 }
448
449 ProcessingInstructionNode* Element::add_child_processing_instruction(
450   const Glib::ustring& name, const Glib::ustring& content)
451 {
452   auto child = xmlNewDocPI(cobj()->doc, (const xmlChar*)name.c_str(), (const xmlChar*)content.c_str());
453   auto node = xmlAddChild(cobj(), child);
454   if (!node)
455   {
456     xmlFreeNode(child);
457     throw internal_error("Could not add processing instruction node " + name);
458   }
459   Node::create_wrapper(node);
460   return static_cast<ProcessingInstructionNode*>(node->_private);
461 }
462
463 } //namespace xmlpp