2 Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of libcxml.
6 libcxml is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 libcxml is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with libcxml. If not, see <http://www.gnu.org/licenses/>.
22 #include <libxml++/libxml++.h>
23 #include <libxml++config.h>
24 #include <boost/filesystem.hpp>
25 #include <boost/algorithm/string.hpp>
29 using std::make_shared;
30 using std::shared_ptr;
33 using boost::optional;
42 cxml::Node::Node (xmlpp::Node* node)
49 cxml::Node::name () const
52 throw Error ("No node to read name from");
54 return _node->get_name ();
57 shared_ptr<cxml::Node>
58 cxml::Node::node_child (string name) const
60 auto const n = node_children (name);
62 throw cxml::Error ("duplicate XML tag " + name);
63 } else if (n.empty ()) {
64 throw cxml::Error ("missing XML tag " + name + " in " + _node->get_name());
70 shared_ptr<cxml::Node>
71 cxml::Node::optional_node_child (string name) const
73 auto const n = node_children (name);
75 throw cxml::Error ("duplicate XML tag " + name);
76 } else if (n.empty ()) {
83 vector<shared_ptr<cxml::Node>>
84 cxml::Node::node_children () const
87 throw Error ("No node to read children from");
90 vector<shared_ptr<cxml::Node>> n;
91 for (auto i: _node->get_children()) {
92 n.push_back(make_shared<Node>(i));
98 vector<shared_ptr<cxml::Node>>
99 cxml::Node::node_children (string name) const
101 /* XXX: using find / get_path should work here, but I can't follow
106 throw cxml::Error("Node has no internal xmlpp node; did you forget to call a read method on cxml::Document?");
109 auto const glib_name = Glib::ustring(name);
111 vector<shared_ptr<cxml::Node>> n;
112 for (auto i: _node->get_children()) {
113 if (i->get_name() == glib_name) {
114 n.push_back(make_shared<Node>(i));
118 _taken.push_back(glib_name);
123 cxml::Node::string_child (string c) const
125 return node_child(c)->content ();
129 cxml::Node::optional_string_child (string c) const
131 auto const nodes = node_children (c);
132 if (nodes.size() > 1) {
133 throw cxml::Error ("duplicate XML tag " + c);
136 if (nodes.empty ()) {
140 return nodes.front()->content();
144 cxml::Node::bool_child (string c) const
146 auto const s = string_child (c);
147 return (s == "1" || s == "yes" || s == "True");
151 cxml::Node::optional_bool_child (string c) const
153 auto const s = optional_string_child (c);
158 return (s.get() == "1" || s.get() == "yes" || s.get() == "True");
162 cxml::Node::ignore_child (string name) const
164 _taken.push_back (name);
168 cxml::Node::string_attribute (string name) const
170 auto e = dynamic_cast<const xmlpp::Element *> (_node);
172 throw cxml::Error ("missing attribute " + name);
175 auto a = e->get_attribute (name);
177 throw cxml::Error ("missing attribute " + name);
180 return a->get_value ();
184 cxml::Node::optional_string_attribute (string name) const
186 auto e = dynamic_cast<const xmlpp::Element *> (_node);
191 auto a = e->get_attribute (name);
196 return string (a->get_value ());
200 cxml::Node::bool_attribute (string name) const
202 auto const s = string_attribute (name);
203 return (s == "1" || s == "yes");
207 cxml::Node::optional_bool_attribute (string name) const
209 auto s = optional_string_attribute (name);
214 return (s.get() == "1" || s.get() == "yes");
218 cxml::Node::done () const
220 for (auto i: _node->get_children()) {
221 if (dynamic_cast<xmlpp::Element*>(i) && find(_taken.begin(), _taken.end(), i->get_name()) == _taken.end()) {
222 throw cxml::Error ("unexpected XML node " + i->get_name());
228 cxml::Node::content () const
232 for (auto i: _node->get_children()) {
233 auto v = dynamic_cast<xmlpp::ContentNode const *> (i);
234 if (v && dynamic_cast<xmlpp::TextNode const *>(v)) {
235 content += v->get_content ();
243 cxml::Node::namespace_uri () const
245 return _node->get_namespace_uri ();
249 cxml::Node::namespace_prefix () const
251 return _node->get_namespace_prefix ();
256 cxml::Node::is_text() const
258 return dynamic_cast<const xmlpp::TextNode*>(_node);
261 cxml::Document::Document (string root_name)
262 : _root_name (root_name)
264 _parser = new xmlpp::DomParser;
267 cxml::Document::Document (string root_name, boost::filesystem::path file)
268 : _root_name (root_name)
270 _parser = new xmlpp::DomParser ();
274 cxml::Document::Document ()
276 _parser = new xmlpp::DomParser ();
279 cxml::Document::~Document ()
285 cxml::Document::read_file (boost::filesystem::path file)
287 if (!boost::filesystem::exists (file)) {
288 throw cxml::Error ("XML file " + file.string() + " does not exist");
291 _parser->parse_file (file.string ());
296 cxml::Document::read_string (string s)
298 _parser->parse_memory (s);
303 cxml::Document::take_root_node ()
306 throw cxml::Error ("could not parse XML");
309 _node = _parser->get_document()->get_root_node ();
310 if (!_root_name.empty() && _node->get_name() != Glib::ustring(_root_name)) {
311 throw cxml::Error ("unrecognised root node " + _node->get_name() + " (expecting " + _root_name + ")");
312 } else if (_root_name.empty ()) {
313 _root_name = _node->get_name ();
319 make_local (string v)
321 auto lc = localeconv ();
322 boost::algorithm::replace_all (v, ".", lc->decimal_point);
323 /* We hope it's ok not to add in thousands separators here */
327 template <typename P, typename Q>
331 /* We can't write a generic version of locale_convert; all required
332 versions must be specialised.
334 BOOST_STATIC_ASSERT (sizeof(Q) == 0);
339 locale_convert (string x)
342 sscanf (x.c_str(), "%d", &y);
348 locale_convert (string x)
351 sscanf (x.c_str(), "%u", &y);
357 locale_convert (string x)
360 sscanf (x.c_str(), "%ld", &y);
366 locale_convert (string x)
368 long unsigned int y = 0;
369 #ifdef LIBCXML_WINDOWS
370 __mingw_sscanf (x.c_str(), "%lud", &y);
372 sscanf (x.c_str(), "%lud", &y);
379 locale_convert (string x)
382 #ifdef LIBCXML_WINDOWS
383 __mingw_sscanf (x.c_str(), "%lld", &y);
385 sscanf (x.c_str(), "%lld", &y);
392 locale_convert (string x)
394 long long unsigned y = 0;
395 #ifdef LIBCXML_WINDOWS
396 __mingw_sscanf (x.c_str(), "%llud", &y);
398 sscanf (x.c_str(), "%llud", &y);
405 locale_convert (string x)
408 sscanf (x.c_str(), "%f", &y);
414 locale_convert (string x)
417 sscanf (x.c_str(), "%lf", &y);
423 cxml::raw_convert (string v)
425 return locale_convert<int> (make_local(v));
430 cxml::raw_convert (string v)
432 return locale_convert<unsigned int> (make_local(v));
437 cxml::raw_convert (string v)
439 return locale_convert<long int> (make_local(v));
444 cxml::raw_convert (string v)
446 return locale_convert<long unsigned int> (make_local(v));
451 cxml::raw_convert (string v)
453 return locale_convert<long long> (make_local(v));
458 cxml::raw_convert (string v)
460 return locale_convert<long long unsigned> (make_local(v));
465 cxml::raw_convert (string v)
467 return locale_convert<float> (make_local(v));
472 cxml::raw_convert (string v)
474 return locale_convert<double> (make_local(v));
479 cxml::add_child(xmlpp::Element* parent, string const& name, string const& ns_prefix)
481 #if LIBXMLXX_MAJOR_VERSION == 2
482 return parent->add_child(name, ns_prefix);
484 return parent->add_child_element(name, ns_prefix);
490 cxml::add_text_child(xmlpp::Element* parent, string const& name, string const& text)
492 add_child(parent, name)->add_child_text(text);