summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2013-01-13 23:55:59 +0000
committerCarl Hetherington <cth@carlh.net>2013-01-13 23:55:59 +0000
commitd14bbc2088ed9c0f9d77f15cb943968a70bb8198 (patch)
tree5c1c766d3e82de3139710d76618293d610145f63 /src
Initial.
Diffstat (limited to 'src')
-rw-r--r--src/cxml.cc200
-rw-r--r--src/cxml.h170
-rw-r--r--src/wscript9
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")