diff options
| author | Carl Hetherington <cth@carlh.net> | 2013-01-13 23:55:59 +0000 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2013-01-13 23:55:59 +0000 |
| commit | d14bbc2088ed9c0f9d77f15cb943968a70bb8198 (patch) | |
| tree | 5c1c766d3e82de3139710d76618293d610145f63 /src | |
Initial.
Diffstat (limited to 'src')
| -rw-r--r-- | src/cxml.cc | 200 | ||||
| -rw-r--r-- | src/cxml.h | 170 | ||||
| -rw-r--r-- | src/wscript | 9 |
3 files changed, 379 insertions, 0 deletions
diff --git a/src/cxml.cc b/src/cxml.cc new file mode 100644 index 0000000..2a755bf --- /dev/null +++ b/src/cxml.cc @@ -0,0 +1,200 @@ +#include <sstream> +#include <iostream> +#include <boost/lexical_cast.hpp> +#include <boost/filesystem.hpp> +#include <boost/algorithm/string.hpp> +#include <libxml++/libxml++.h> +#include "cxml.h" + +using namespace std; +using namespace boost; + +cxml::Node::Node () + : _node (0) +{ + +} + +cxml::Node::Node (xmlpp::Node const * node) + : _node (node) +{ + +} + +shared_ptr<cxml::Node> +cxml::Node::node_child (string name) +{ + list<shared_ptr<cxml::Node> > n = node_children (name); + if (n.size() > 1) { + throw cxml::Error ("duplicate XML tag " + name); + } else if (n.empty ()) { + throw cxml::Error ("missing XML tag " + name + " in " + _node->get_name()); + } + + return n.front (); +} + +list<shared_ptr<cxml::Node> > +cxml::Node::node_children (string name) +{ + /* XXX: using find / get_path should work here, but I can't follow + how get_path works. + */ + + xmlpp::Node::NodeList c = _node->get_children (); + + list<shared_ptr<cxml::Node> > n; + for (xmlpp::Node::NodeList::iterator i = c.begin (); i != c.end(); ++i) { + if ((*i)->get_name() == name) { + n.push_back (shared_ptr<Node> (new Node (*i))); + } + } + + _taken.push_back (name); + return n; +} + +string +cxml::Node::string_child (string c) +{ + return node_child(c)->content (); +} + +optional<string> +cxml::Node::optional_string_child (string c) +{ + list<shared_ptr<Node> > nodes = node_children (c); + if (nodes.size() > 1) { + throw cxml::Error ("duplicate XML tag " + c); + } + + if (nodes.empty ()) { + return optional<string> (); + } + + return nodes.front()->content(); +} + +bool +cxml::Node::bool_child (string c) +{ + string const s = string_child (c); + return (s == "1" || s == "yes"); +} + +optional<bool> +cxml::Node::optional_bool_child (string c) +{ + optional<string> s = optional_string_child (c); + if (!s) { + return optional<bool> (); + } + + return (s.get() == "1" || s.get() == "yes"); +} + +void +cxml::Node::ignore_child (string name) +{ + _taken.push_back (name); +} + +string +cxml::Node::string_attribute (string name) +{ + xmlpp::Element const * e = dynamic_cast<const xmlpp::Element *> (_node); + if (!e) { + throw cxml::Error ("missing attribute"); + } + + xmlpp::Attribute* a = e->get_attribute (name); + if (!a) { + throw cxml::Error ("missing attribute"); + } + + return a->get_value (); +} + +optional<string> +cxml::Node::optional_string_attribute (string name) +{ + xmlpp::Element const * e = dynamic_cast<const xmlpp::Element *> (_node); + if (!e) { + return optional<string> (); + } + + xmlpp::Attribute* a = e->get_attribute (name); + if (!a) { + return optional<string> (); + } + + return string (a->get_value ()); +} + +bool +cxml::Node::bool_attribute (string name) +{ + string const s = string_attribute (name); + return (s == "1" || s == "yes"); +} + +optional<bool> +cxml::Node::optional_bool_attribute (string name) +{ + optional<string> s = optional_string_attribute (name); + if (!s) { + return optional<bool> (); + } + + return (s.get() == "1" || s.get() == "yes"); +} + +void +cxml::Node::done () +{ + xmlpp::Node::NodeList c = _node->get_children (); + for (xmlpp::Node::NodeList::iterator i = c.begin(); i != c.end(); ++i) { + if (dynamic_cast<xmlpp::Element *> (*i) && find (_taken.begin(), _taken.end(), (*i)->get_name()) == _taken.end ()) { + throw cxml::Error ("unexpected XML node " + (*i)->get_name()); + } + } +} + +string +cxml::Node::content () +{ + string content; + + xmlpp::Node::NodeList c = _node->get_children (); + for (xmlpp::Node::NodeList::const_iterator i = c.begin(); i != c.end(); ++i) { + xmlpp::ContentNode const * v = dynamic_cast<xmlpp::ContentNode const *> (*i); + if (v) { + content += v->get_content (); + } + } + + return content; +} + +cxml::File::File (string file, string root_name) +{ + if (!filesystem::exists (file)) { + throw cxml::Error ("XML file does not exist"); + } + + _parser = new xmlpp::DomParser; + _parser->parse_file (file); + if (!_parser) { + throw cxml::Error ("could not parse XML"); + } + + _node = _parser->get_document()->get_root_node (); + if (_node->get_name() != root_name) { + throw cxml::Error ("unrecognised root node"); + } +} + +cxml::File::~File () +{ + delete _parser; +} diff --git a/src/cxml.h b/src/cxml.h new file mode 100644 index 0000000..bf2a2d3 --- /dev/null +++ b/src/cxml.h @@ -0,0 +1,170 @@ +#ifndef LIBCXML_CXML_H +#define LIBCXML_CXML_H + +#include <string> +#include <list> +#include <stdint.h> +#include <boost/shared_ptr.hpp> +#include <boost/optional.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/algorithm/string/erase.hpp> + +namespace xmlpp { + class Node; + class DomParser; +} + +namespace cxml { + +/** @brief An error */ +class Error : public std::exception +{ +public: + /** Construct an Error exception. + * @param message Error message. + */ + Error (std::string const & message) : _message (message) {} + + /** Error destructor */ + ~Error () throw () {} + + /** @return error message. Caller must not free the returned + * value. + */ + char const * what () const throw () { + return _message.c_str (); + } + +private: + /** error message */ + std::string _message; +}; + +/** @brief A wrapper for a xmlpp::Node which simplifies parsing */ +class Node +{ +public: + Node (); + + /** Construct a Node from an xmlpp::Node. This class will + * not destroy the xmlpp::Node. + * @param node xmlpp::Node. + */ + Node (xmlpp::Node const * node); + + /* A set of methods which look up a child of this node by + * its name, and return its contents as some type or other. + * + * If, for example, this object has been created with + * a node named "Fred", we might have the following XML: + * + * <Fred> + * <Jim>42</Jim> + * </Fred> + * + * string_child ("Jim") would return "42" + * numerical_child<int64_t> ("Jim") would return 42. + * ...and so on. + * + * The methods not marked "optional" will throw an exception + * if the child node is not present. The "optional" methods + * will return an empty boost::optional<> in that case. + * + * All methods will also throw an exception if there is more + * than one of the specified child node. + */ + + std::string string_child (std::string c); + boost::optional<std::string> optional_string_child (std::string); + + bool bool_child (std::string); + boost::optional<bool> optional_bool_child (std::string); + + template <class T> + T numerical_child (std::string c) + { + std::string s = string_child (c); + boost::erase_all (s, " "); + return boost::lexical_cast<T> (s); + } + + template <class T> + boost::optional<T> optional_numerical_child (std::string c) + { + boost::optional<std::string> s = optional_string_child (c); + if (!s) { + return boost::optional<T> (); + } + + std::string t = s.get (); + boost::erase_all (t, " "); + return boost::optional<T> (boost::lexical_cast<T> (t)); + } + + /** This will mark a child as to be ignored when calling done() */ + void ignore_child (std::string); + + /** Check whether all children of this Node have been looked up + * or passed to ignore_child(). If not, an exception is thrown. + */ + void done (); + + /* These methods look for an attribute of this node, in the + * same way as the child methods do. + */ + + std::string string_attribute (std::string); + boost::optional<std::string> optional_string_attribute (std::string); + + bool bool_attribute (std::string); + boost::optional<bool> optional_bool_attribute (std::string); + + template <class T> + T numerical_attribute (std::string c) + { + std::string s = string_attribute (c); + boost::erase_all (s, " "); + return boost::lexical_cast<T> (s); + } + + template <class T> + boost::optional<T> optional_numerical_attribute (std::string c) + { + boost::optional<std::string> s = optional_string_attribute (c); + if (!s) { + return boost::optional<T> (); + } + + std::string t = s.get (); + boost::erase_all (t, " "); + return boost::optional<T> (boost::lexical_cast<T> (t)); + } + + /** @return The content of this node */ + std::string content (); + + boost::shared_ptr<Node> node_child (std::string); + boost::shared_ptr<Node> optional_node_child (std::string); + + std::list<boost::shared_ptr<Node> > node_children (std::string); + +protected: + xmlpp::Node const * _node; + +private: + std::list<Glib::ustring> _taken; +}; + +class File : public Node +{ +public: + File (std::string file, std::string root_name); + virtual ~File (); + +private: + xmlpp::DomParser* _parser; +}; + +} + +#endif diff --git a/src/wscript b/src/wscript new file mode 100644 index 0000000..e1c405a --- /dev/null +++ b/src/wscript @@ -0,0 +1,9 @@ +def build(bld): + obj = bld(features = 'cxx cxxshlib') + obj.name = 'libcxml' + obj.target = 'cxml' + obj.export_includes = ['.'] + obj.uselib = 'LIBXML++ BOOST_FILESYSTEM' + obj.source = "cxml.cc" + + bld.install_files('${PREFIX}/include/libcxml', "cxml.h") |
