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 vector<shared_ptr<cxml::Node>> n;
109 for (auto i: _node->get_children()) {
110 if (i->get_name() == name) {
111 n.push_back(make_shared<Node>(i));
115 _taken.push_back (name);
120 cxml::Node::string_child (string c) const
122 return node_child(c)->content ();
126 cxml::Node::optional_string_child (string c) const
128 auto const nodes = node_children (c);
129 if (nodes.size() > 1) {
130 throw cxml::Error ("duplicate XML tag " + c);
133 if (nodes.empty ()) {
137 return nodes.front()->content();
141 cxml::Node::bool_child (string c) const
143 auto const s = string_child (c);
144 return (s == "1" || s == "yes" || s == "True");
148 cxml::Node::optional_bool_child (string c) const
150 auto const s = optional_string_child (c);
155 return (s.get() == "1" || s.get() == "yes" || s.get() == "True");
159 cxml::Node::ignore_child (string name) const
161 _taken.push_back (name);
165 cxml::Node::string_attribute (string name) const
167 auto e = dynamic_cast<const xmlpp::Element *> (_node);
169 throw cxml::Error ("missing attribute " + name);
172 auto a = e->get_attribute (name);
174 throw cxml::Error ("missing attribute " + name);
177 return a->get_value ();
181 cxml::Node::optional_string_attribute (string name) const
183 auto e = dynamic_cast<const xmlpp::Element *> (_node);
188 auto a = e->get_attribute (name);
193 return string (a->get_value ());
197 cxml::Node::bool_attribute (string name) const
199 auto const s = string_attribute (name);
200 return (s == "1" || s == "yes");
204 cxml::Node::optional_bool_attribute (string name) const
206 auto s = optional_string_attribute (name);
211 return (s.get() == "1" || s.get() == "yes");
215 cxml::Node::done () const
217 for (auto i: _node->get_children()) {
218 if (dynamic_cast<xmlpp::Element *> (i) && find (_taken.begin(), _taken.end(), i->get_name()) == _taken.end ()) {
219 throw cxml::Error ("unexpected XML node " + i->get_name());
225 cxml::Node::content () const
229 for (auto i: _node->get_children()) {
230 auto v = dynamic_cast<xmlpp::ContentNode const *> (i);
231 if (v && dynamic_cast<xmlpp::TextNode const *>(v)) {
232 content += v->get_content ();
240 cxml::Node::namespace_uri () const
242 return _node->get_namespace_uri ();
246 cxml::Node::namespace_prefix () const
248 return _node->get_namespace_prefix ();
253 cxml::Node::is_text() const
255 return dynamic_cast<const xmlpp::TextNode*>(_node);
258 cxml::Document::Document (string root_name)
259 : _root_name (root_name)
261 _parser = new xmlpp::DomParser;
264 cxml::Document::Document (string root_name, boost::filesystem::path file)
265 : _root_name (root_name)
267 _parser = new xmlpp::DomParser ();
271 cxml::Document::Document ()
273 _parser = new xmlpp::DomParser ();
276 cxml::Document::~Document ()
282 cxml::Document::read_file (boost::filesystem::path file)
284 if (!boost::filesystem::exists (file)) {
285 throw cxml::Error ("XML file " + file.string() + " does not exist");
288 _parser->parse_file (file.string ());
293 cxml::Document::read_string (string s)
295 _parser->parse_memory (s);
300 cxml::Document::take_root_node ()
303 throw cxml::Error ("could not parse XML");
306 _node = _parser->get_document()->get_root_node ();
307 if (!_root_name.empty() && _node->get_name() != _root_name) {
308 throw cxml::Error ("unrecognised root node " + _node->get_name() + " (expecting " + _root_name + ")");
309 } else if (_root_name.empty ()) {
310 _root_name = _node->get_name ();
316 make_local (string v)
318 auto lc = localeconv ();
319 boost::algorithm::replace_all (v, ".", lc->decimal_point);
320 /* We hope it's ok not to add in thousands separators here */
324 template <typename P, typename Q>
328 /* We can't write a generic version of locale_convert; all required
329 versions must be specialised.
331 BOOST_STATIC_ASSERT (sizeof(Q) == 0);
336 locale_convert (string x)
339 sscanf (x.c_str(), "%d", &y);
345 locale_convert (string x)
348 sscanf (x.c_str(), "%u", &y);
354 locale_convert (string x)
357 sscanf (x.c_str(), "%ld", &y);
363 locale_convert (string x)
365 long unsigned int y = 0;
366 #ifdef LIBCXML_WINDOWS
367 __mingw_sscanf (x.c_str(), "%lud", &y);
369 sscanf (x.c_str(), "%lud", &y);
376 locale_convert (string x)
379 #ifdef LIBCXML_WINDOWS
380 __mingw_sscanf (x.c_str(), "%lld", &y);
382 sscanf (x.c_str(), "%lld", &y);
389 locale_convert (string x)
391 long long unsigned y = 0;
392 #ifdef LIBCXML_WINDOWS
393 __mingw_sscanf (x.c_str(), "%llud", &y);
395 sscanf (x.c_str(), "%llud", &y);
402 locale_convert (string x)
405 sscanf (x.c_str(), "%f", &y);
411 locale_convert (string x)
414 sscanf (x.c_str(), "%lf", &y);
420 cxml::raw_convert (string v)
422 return locale_convert<int> (make_local(v));
427 cxml::raw_convert (string v)
429 return locale_convert<unsigned int> (make_local(v));
434 cxml::raw_convert (string v)
436 return locale_convert<long int> (make_local(v));
441 cxml::raw_convert (string v)
443 return locale_convert<long unsigned int> (make_local(v));
448 cxml::raw_convert (string v)
450 return locale_convert<long long> (make_local(v));
455 cxml::raw_convert (string v)
457 return locale_convert<long long unsigned> (make_local(v));
462 cxml::raw_convert (string v)
464 return locale_convert<float> (make_local(v));
469 cxml::raw_convert (string v)
471 return locale_convert<double> (make_local(v));
476 cxml::add_child(xmlpp::Element* parent, string const& name)
478 return parent->add_child(name);
483 cxml::add_text_child(xmlpp::Element* parent, string const& name, string const& text)
485 parent->add_child(name)->add_child_text(text);