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 <boost/filesystem.hpp>
24 #include <boost/algorithm/string.hpp>
28 using std::make_shared;
29 using std::shared_ptr;
32 using boost::optional;
41 cxml::Node::Node (xmlpp::Node* node)
48 cxml::Node::name () const
51 throw Error ("No node to read name from");
53 return _node->get_name ();
56 shared_ptr<cxml::Node>
57 cxml::Node::node_child (string name) const
59 auto const n = node_children (name);
61 throw cxml::Error ("duplicate XML tag " + name);
62 } else if (n.empty ()) {
63 throw cxml::Error ("missing XML tag " + name + " in " + _node->get_name());
69 shared_ptr<cxml::Node>
70 cxml::Node::optional_node_child (string name) const
72 auto const n = node_children (name);
74 throw cxml::Error ("duplicate XML tag " + name);
75 } else if (n.empty ()) {
82 vector<shared_ptr<cxml::Node>>
83 cxml::Node::node_children () const
86 throw Error ("No node to read children from");
89 vector<shared_ptr<cxml::Node>> n;
90 for (auto i: _node->get_children()) {
91 n.push_back(make_shared<Node>(i));
97 vector<shared_ptr<cxml::Node>>
98 cxml::Node::node_children (string name) const
100 /* XXX: using find / get_path should work here, but I can't follow
105 throw cxml::Error("Node has no internal xmlpp node; did you forget to call a read method on cxml::Document?");
108 auto const glib_name = Glib::ustring(name);
110 vector<shared_ptr<cxml::Node>> n;
111 for (auto i: _node->get_children()) {
112 if (i->get_name() == glib_name) {
113 n.push_back(make_shared<Node>(i));
117 _taken.push_back(glib_name);
122 cxml::Node::string_child (string c) const
124 return node_child(c)->content ();
128 cxml::Node::optional_string_child (string c) const
130 auto const nodes = node_children (c);
131 if (nodes.size() > 1) {
132 throw cxml::Error ("duplicate XML tag " + c);
135 if (nodes.empty ()) {
139 return nodes.front()->content();
143 cxml::Node::bool_child (string c) const
145 auto const s = string_child (c);
146 return (s == "1" || s == "yes" || s == "True");
150 cxml::Node::optional_bool_child (string c) const
152 auto const s = optional_string_child (c);
157 return (s.get() == "1" || s.get() == "yes" || s.get() == "True");
161 cxml::Node::ignore_child (string name) const
163 _taken.push_back (name);
167 cxml::Node::string_attribute (string name) const
169 auto e = dynamic_cast<const xmlpp::Element *> (_node);
171 throw cxml::Error ("missing attribute " + name);
174 auto a = e->get_attribute (name);
176 throw cxml::Error ("missing attribute " + name);
179 return a->get_value ();
183 cxml::Node::optional_string_attribute (string name) const
185 auto e = dynamic_cast<const xmlpp::Element *> (_node);
190 auto a = e->get_attribute (name);
195 return string (a->get_value ());
199 cxml::Node::bool_attribute (string name) const
201 auto const s = string_attribute (name);
202 return (s == "1" || s == "yes");
206 cxml::Node::optional_bool_attribute (string name) const
208 auto s = optional_string_attribute (name);
213 return (s.get() == "1" || s.get() == "yes");
217 cxml::Node::done () const
219 for (auto i: _node->get_children()) {
220 if (dynamic_cast<xmlpp::Element*>(i) && find(_taken.begin(), _taken.end(), i->get_name()) == _taken.end()) {
221 throw cxml::Error ("unexpected XML node " + i->get_name());
227 cxml::Node::content () const
231 for (auto i: _node->get_children()) {
232 auto v = dynamic_cast<xmlpp::ContentNode const *> (i);
233 if (v && dynamic_cast<xmlpp::TextNode const *>(v)) {
234 content += v->get_content ();
242 cxml::Node::namespace_uri () const
244 return _node->get_namespace_uri ();
248 cxml::Node::namespace_prefix () const
250 return _node->get_namespace_prefix ();
255 cxml::Node::is_text() const
257 return dynamic_cast<const xmlpp::TextNode*>(_node);
260 cxml::Document::Document (string root_name)
261 : _root_name (root_name)
263 _parser = new xmlpp::DomParser;
266 cxml::Document::Document (string root_name, boost::filesystem::path file)
267 : _root_name (root_name)
269 _parser = new xmlpp::DomParser ();
273 cxml::Document::Document ()
275 _parser = new xmlpp::DomParser ();
278 cxml::Document::~Document ()
284 cxml::Document::read_file (boost::filesystem::path file)
286 if (!boost::filesystem::exists (file)) {
287 throw cxml::Error ("XML file " + file.string() + " does not exist");
290 _parser->parse_file (file.string ());
295 cxml::Document::read_string (string s)
297 _parser->parse_memory (s);
302 cxml::Document::take_root_node ()
305 throw cxml::Error ("could not parse XML");
308 _node = _parser->get_document()->get_root_node ();
309 if (!_root_name.empty() && _node->get_name() != Glib::ustring(_root_name)) {
310 throw cxml::Error ("unrecognised root node " + _node->get_name() + " (expecting " + _root_name + ")");
311 } else if (_root_name.empty ()) {
312 _root_name = _node->get_name ();
318 make_local (string v)
320 auto lc = localeconv ();
321 boost::algorithm::replace_all (v, ".", lc->decimal_point);
322 /* We hope it's ok not to add in thousands separators here */
326 template <typename P, typename Q>
330 /* We can't write a generic version of locale_convert; all required
331 versions must be specialised.
333 BOOST_STATIC_ASSERT (sizeof(Q) == 0);
338 locale_convert (string x)
341 sscanf (x.c_str(), "%d", &y);
347 locale_convert (string x)
350 sscanf (x.c_str(), "%u", &y);
356 locale_convert (string x)
359 sscanf (x.c_str(), "%ld", &y);
365 locale_convert (string x)
367 long unsigned int y = 0;
368 #ifdef LIBCXML_WINDOWS
369 __mingw_sscanf (x.c_str(), "%lud", &y);
371 sscanf (x.c_str(), "%lud", &y);
378 locale_convert (string x)
381 #ifdef LIBCXML_WINDOWS
382 __mingw_sscanf (x.c_str(), "%lld", &y);
384 sscanf (x.c_str(), "%lld", &y);
391 locale_convert (string x)
393 long long unsigned y = 0;
394 #ifdef LIBCXML_WINDOWS
395 __mingw_sscanf (x.c_str(), "%llud", &y);
397 sscanf (x.c_str(), "%llud", &y);
404 locale_convert (string x)
407 sscanf (x.c_str(), "%f", &y);
413 locale_convert (string x)
416 sscanf (x.c_str(), "%lf", &y);
422 cxml::raw_convert (string v)
424 return locale_convert<int> (make_local(v));
429 cxml::raw_convert (string v)
431 return locale_convert<unsigned int> (make_local(v));
436 cxml::raw_convert (string v)
438 return locale_convert<long int> (make_local(v));
443 cxml::raw_convert (string v)
445 return locale_convert<long unsigned int> (make_local(v));
450 cxml::raw_convert (string v)
452 return locale_convert<long long> (make_local(v));
457 cxml::raw_convert (string v)
459 return locale_convert<long long unsigned> (make_local(v));
464 cxml::raw_convert (string v)
466 return locale_convert<float> (make_local(v));
471 cxml::raw_convert (string v)
473 return locale_convert<double> (make_local(v));
478 cxml::add_child(xmlpp::Element* parent, string const& name, string const& ns_prefix)
480 #if LIBXMLXX_MAJOR_VERSION == 2
481 return parent->add_child(name, ns_prefix);
483 return parent->add_child_element(name, ns_prefix);
489 cxml::add_text_child(xmlpp::Element* parent, string const& name, string const& text)
491 add_child(parent, name)->add_child_text(text);