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.
7 #include <libxml++/nodes/element.h>
8 #include <libxml++/exceptions/internal_error.h>
10 #include <libxml/tree.h>
12 namespace // anonymous
14 // Common part of all add_child_element*() methods.
15 xmlpp::Element* add_child_element_common(const Glib::ustring& name, xmlNode* child, xmlNode* node)
20 throw xmlpp::internal_error("Could not add child element node " + name);
22 xmlpp::Node::create_wrapper(node);
23 return static_cast<xmlpp::Element*>(node->_private);
26 } // anonymous namespace
31 Element::Element(xmlNode* node)
38 Element::AttributeList Element::get_attributes()
40 AttributeList attributes;
41 for (auto attr = cobj()->properties; attr; attr = attr->next)
43 Node::create_wrapper(reinterpret_cast<xmlNode*>(attr));
44 attributes.push_back(reinterpret_cast<Attribute*>(attr->_private));
49 Element::const_AttributeList Element::get_attributes() const
51 const_AttributeList attributes;
52 for (auto attr = cobj()->properties; attr; attr = attr->next)
54 Node::create_wrapper(reinterpret_cast<xmlNode*>(attr));
55 attributes.push_back(reinterpret_cast<const Attribute*>(attr->_private));
60 Attribute* Element::get_attribute(const Glib::ustring& name,
61 const Glib::ustring& ns_prefix)
63 // An empty ns_prefix means "use no namespace".
64 // The default namespace never applies to an attribute.
66 if (!ns_prefix.empty())
68 ns_uri = get_namespace_uri_for_prefix(ns_prefix);
70 return nullptr; // No such prefix.
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());
81 Node::create_wrapper(reinterpret_cast<xmlNode*>(attr));
82 return reinterpret_cast<Attribute*>(attr->_private);
88 const Attribute* Element::get_attribute(const Glib::ustring& name,
89 const Glib::ustring& ns_prefix) const
91 return const_cast<Element*>(this)->get_attribute(name, ns_prefix);
94 Glib::ustring Element::get_attribute_value(const Glib::ustring& name, const Glib::ustring& ns_prefix) const
96 const auto attr = get_attribute(name, ns_prefix);
97 return attr ? attr->get_value() : Glib::ustring();
100 Attribute* Element::set_attribute(const Glib::ustring& name, const Glib::ustring& value,
101 const Glib::ustring& ns_prefix)
103 xmlAttr* attr = nullptr;
105 //Ignore the namespace if none was specified:
106 if(ns_prefix.empty())
108 attr = xmlSetProp(cobj(), (const xmlChar*)name.c_str(), (const xmlChar*)value.c_str());
112 //If the namespace exists, then use it:
113 auto ns = xmlSearchNs(cobj()->doc, cobj(), (const xmlChar*)ns_prefix.c_str());
116 attr = xmlSetNsProp(cobj(), ns, (const xmlChar*)name.c_str(),
117 (const xmlChar*)value.c_str());
121 throw exception("The namespace prefix (" + ns_prefix + ") has not been declared.");
127 Node::create_wrapper(reinterpret_cast<xmlNode*>(attr));
128 return reinterpret_cast<Attribute*>(attr->_private);
134 void Element::remove_attribute(const Glib::ustring& name, const Glib::ustring& ns_prefix)
136 if (ns_prefix.empty())
137 xmlUnsetProp(cobj(), (const xmlChar*)name.c_str());
140 auto ns = xmlSearchNs(cobj()->doc, cobj(), (const xmlChar*)ns_prefix.c_str());
142 xmlUnsetNsProp(cobj(), ns, (const xmlChar*)name.c_str());
146 Element* Element::add_child_element(const Glib::ustring& name,
147 const Glib::ustring& ns_prefix)
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);
154 Element* Element::add_child_element(xmlpp::Node* previous_sibling,
155 const Glib::ustring& name, const Glib::ustring& ns_prefix)
157 if (!previous_sibling)
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);
165 Element* Element::add_child_element_before(xmlpp::Node* next_sibling,
166 const Glib::ustring& name, const Glib::ustring& ns_prefix)
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);
176 Element* Element::add_child_element_with_new_ns(const Glib::ustring& name,
177 const Glib::ustring& ns_uri, const Glib::ustring& ns_prefix)
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);
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)
188 if (!previous_sibling)
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);
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)
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);
208 _xmlNode* Element::create_new_child_element_node(const Glib::ustring& name,
209 const Glib::ustring& ns_prefix)
213 if (cobj()->type != XML_ELEMENT_NODE)
214 throw internal_error("You can only add child nodes to element nodes");
216 if (ns_prefix.empty())
218 //Retrieve default namespace if it exists
219 ns = xmlSearchNs(cobj()->doc, cobj(), nullptr);
223 //Use the existing namespace if one exists:
224 ns = xmlSearchNs(cobj()->doc, cobj(), (const xmlChar*)ns_prefix.c_str());
226 throw exception("The namespace prefix (" + ns_prefix + ") has not been declared.");
229 return xmlNewNode(ns, (const xmlChar*)name.c_str());
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)
235 if (cobj()->type != XML_ELEMENT_NODE)
236 throw internal_error("You can only add child nodes to element nodes.");
238 auto child = xmlNewNode(nullptr, (const xmlChar*)name.c_str());
240 throw internal_error("Could not create new element node.");
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")
248 ns = xmlSearchNs(cobj()->doc, cobj(), (const xmlChar*)ns_prefix.c_str());
249 if (ns && (ns_uri != (ns->href ? (const char*)ns->href : "")))
255 throw internal_error("Could not create new namespace node.");
263 TextNode* Element::get_first_child_text()
265 for (auto child = cobj()->children; child; child = child->next)
266 if (child->type == XML_TEXT_NODE)
268 Node::create_wrapper(child);
269 return static_cast<TextNode*>(child->_private);
275 const TextNode* Element::get_first_child_text() const
277 return const_cast<Element*>(this)->get_first_child_text();
280 void Element::set_first_child_text(const Glib::ustring& content)
282 auto node = get_first_child_text();
284 node->set_content(content);
286 add_child_text(content);
289 TextNode* Element::add_child_text(const Glib::ustring& content)
291 if(cobj()->type == XML_ELEMENT_NODE)
293 auto child = xmlNewText((const xmlChar*)content.c_str());
295 // Use the result, because child can be freed when merging text nodes:
296 auto node = xmlAddChild(cobj(), child);
300 throw internal_error("Could not add text node \"" + content + "\"");
302 Node::create_wrapper(node);
303 return static_cast<TextNode*>(node->_private);
308 TextNode* Element::add_child_text(xmlpp::Node* previous_sibling, const Glib::ustring& content)
310 if(!previous_sibling)
313 if(cobj()->type == XML_ELEMENT_NODE)
315 auto child = xmlNewText((const xmlChar*)content.c_str());
317 // Use the result, because child can be freed when merging text nodes:
318 auto node = xmlAddNextSibling(previous_sibling->cobj(), child);
322 throw internal_error("Could not add text node \"" + content + "\"");
324 Node::create_wrapper(node);
325 return static_cast<TextNode*>(node->_private);
330 TextNode* Element::add_child_text_before(xmlpp::Node* next_sibling, const Glib::ustring& content)
335 if(cobj()->type == XML_ELEMENT_NODE)
337 auto child = xmlNewText((const xmlChar*)content.c_str());
339 // Use the result, because child can be freed when merging text nodes:
340 auto node = xmlAddPrevSibling(next_sibling->cobj(), child);
344 throw internal_error("Could not add text node \"" + content + "\"");
346 Node::create_wrapper(node);
347 return static_cast<TextNode*>(node->_private);
352 bool Element::has_child_text() const
354 return get_first_child_text() != nullptr;
357 void Element::set_namespace_declaration(const Glib::ustring& ns_uri, const Glib::ustring& ns_prefix)
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()) );
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);
372 if (ns_prefix == get_namespace_prefix())
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);
378 //We ignore the returned xmlNs*. It's owned by the XML_ELEMENT_NODE.
381 Glib::ustring Element::get_namespace_uri_for_prefix(const Glib::ustring& ns_prefix) const
383 Glib::ustring result;
385 //Find the namespace:
386 const auto ns = xmlSearchNs( cobj()->doc, const_cast<xmlNode*>(cobj()), (xmlChar*)ns_prefix.c_str() );
389 //Get the namespace URI associated with this prefix:
391 result = (const char*)ns->href;
397 CommentNode* Element::add_child_comment(const Glib::ustring& content)
399 auto child = xmlNewComment((const xmlChar*)content.c_str());
401 // Use the result, because child can be freed when merging text nodes:
402 auto node = xmlAddChild(cobj(), child);
406 throw internal_error("Could not add comment node \"" + content + "\"");
408 Node::create_wrapper(node);
409 return static_cast<CommentNode*>(node->_private);
412 CdataNode* Element::add_child_cdata(const Glib::ustring& content)
414 auto child = xmlNewCDataBlock(cobj()->doc, (const xmlChar*)content.c_str(), content.bytes());
415 auto node = xmlAddChild(cobj(), child);
419 throw internal_error("Could not add CDATA node \"" + content + "\"");
421 Node::create_wrapper(node);
422 return static_cast<CdataNode*>(node->_private);
425 EntityReference* Element::add_child_entity_reference(const Glib::ustring& name)
427 const auto extended_name = name + " "; // This is at least two chars long.
429 if (extended_name[ichar] == '&')
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());
438 child = xmlNewReference(cobj()->doc, (const xmlChar*)name.c_str());
439 auto node = xmlAddChild(cobj(), child);
443 throw internal_error("Could not add entity reference node " + name);
445 Node::create_wrapper(node);
446 return static_cast<EntityReference*>(node->_private);
449 ProcessingInstructionNode* Element::add_child_processing_instruction(
450 const Glib::ustring& name, const Glib::ustring& content)
452 auto child = xmlNewDocPI(cobj()->doc, (const xmlChar*)name.c_str(), (const xmlChar*)content.c_str());
453 auto node = xmlAddChild(cobj(), child);
457 throw internal_error("Could not add processing instruction node " + name);
459 Node::create_wrapper(node);
460 return static_cast<ProcessingInstructionNode*>(node->_private);