instead. Add short-cuts for creating XML using libcxml.
#include <libxml++/libxml++.h>
#include "cxml.h"
-using namespace std;
-using namespace boost;
+using std::pair;
+using std::list;
+using std::istream;
+using std::string;
+using std::make_pair;
+using std::map;
+using std::stringstream;
+using boost::shared_ptr;
+using boost::optional;
cxml::Node::Node ()
- : _node (0)
{
}
-cxml::Node::Node (xmlpp::Node* node)
- : _node (node)
+cxml::Node::Node (xmlpp::Node const * node)
{
+ read (node);
+}
+void
+cxml::Node::read (xmlpp::Node const * node)
+{
+ _name = node->get_name ();
+ _namespace_uri = node->get_namespace_uri ();
+ _namespace_prefix = node->get_namespace_prefix ();
+
+ xmlpp::Node::NodeList content = node->get_children ();
+ for (xmlpp::Node::NodeList::const_iterator i = content.begin(); i != content.end(); ++i) {
+ xmlpp::ContentNode const * v = dynamic_cast<xmlpp::ContentNode const *> (*i);
+ if (v) {
+ _content += v->get_content ();
+ } else {
+ _children.push_back (cxml::NodePtr (new cxml::Node (*i)));
+ }
+ }
+
+ xmlpp::Element const * element = dynamic_cast<xmlpp::Element const *> (node);
+ if (element) {
+ xmlpp::Element::AttributeList attributes = element->get_attributes ();
+ for (list<xmlpp::Attribute*>::const_iterator i = attributes.begin(); i != attributes.end(); ++i) {
+ _attributes.push_back (make_pair ((*i)->get_name(), (*i)->get_value ()));
+ }
+ }
}
-string
-cxml::Node::name () const
+void
+cxml::Node::write (xmlpp::Node* parent) const
{
- assert (_node);
- return _node->get_name ();
+ xmlpp::Element* node = parent->add_child (_name);
+
+ if (!_content.empty ()) {
+ node->add_child_text (_content);
+ }
+
+ for (list<pair<string, string> >::const_iterator i = _attributes.begin(); i != _attributes.end(); ++i) {
+ node->set_attribute (i->first, i->second);
+ }
+
+ for (NodeList::const_iterator i = _children.begin(); i != _children.end(); ++i) {
+ (*i)->write (node);
+ }
}
-shared_ptr<cxml::Node>
+cxml::NodePtr
cxml::Node::node_child (string name) const
{
- list<shared_ptr<cxml::Node> > n = node_children (name);
+ NodeList 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());
+ throw cxml::Error ("missing XML tag " + name + " in " + _name);
}
return n.front ();
}
-shared_ptr<cxml::Node>
+cxml::NodePtr
cxml::Node::optional_node_child (string name) const
{
- list<shared_ptr<cxml::Node> > n = node_children (name);
+ NodeList n = node_children (name);
if (n.size() > 1) {
throw cxml::Error ("duplicate XML tag " + name);
} else if (n.empty ()) {
- return shared_ptr<cxml::Node> ();
+ return NodePtr ();
}
return n.front ();
}
-list<shared_ptr<cxml::Node> >
+cxml::NodeList
cxml::Node::node_children (string name) const
{
- /* XXX: using find / get_path should work here, but I can't follow
- how get_path works.
- */
-
- xmlpp::Node::NodeList c = _node->get_children ();
+ NodeList n;
- 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)));
+ for (NodeList::const_iterator i = _children.begin(); i != _children.end(); ++i) {
+ if ((*i)->name() == name) {
+ n.push_back (*i);
}
}
-
+
_taken.push_back (name);
return n;
}
optional<string>
cxml::Node::optional_string_child (string c) const
{
- list<shared_ptr<Node> > nodes = node_children (c);
+ NodeList nodes = node_children (c);
if (nodes.size() > 1) {
throw cxml::Error ("duplicate XML tag " + c);
}
string
cxml::Node::string_attribute (string name) const
{
- xmlpp::Element const * e = dynamic_cast<const xmlpp::Element *> (_node);
- if (!e) {
- throw cxml::Error ("missing attribute");
+ list<pair<string, string> >::const_iterator i;
+ while (i != _attributes.end() && i->first != name) {
+ ++i;
}
- xmlpp::Attribute* a = e->get_attribute (name);
- if (!a) {
+ if (i == _attributes.end ()) {
throw cxml::Error ("missing attribute");
}
- return a->get_value ();
+ return i->second;
}
optional<string>
cxml::Node::optional_string_attribute (string name) const
{
- xmlpp::Element const * e = dynamic_cast<const xmlpp::Element *> (_node);
- if (!e) {
- return optional<string> ();
+ list<pair<string, string> >::const_iterator i;
+ while (i != _attributes.end() && i->first != name) {
+ ++i;
}
- xmlpp::Attribute* a = e->get_attribute (name);
- if (!a) {
+ if (i == _attributes.end ()) {
return optional<string> ();
}
- return string (a->get_value ());
+ return i->second;
}
bool
void
cxml::Node::done () const
{
- 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());
+ for (NodeList::const_iterator i = _children.begin(); i != _children.end(); ++i) {
+ if (find (_taken.begin(), _taken.end(), (*i)->name()) == _taken.end ()) {
+ throw cxml::Error ("unexpected XML node " + (*i)->name());
}
}
}
string
cxml::Node::content () const
{
- 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;
+ return _content;
}
string
cxml::Node::namespace_uri () const
{
- return _node->get_namespace_uri ();
+ return _namespace_uri;
}
string
cxml::Node::namespace_prefix () const
{
- return _node->get_namespace_prefix ();
+ return _namespace_prefix;
}
-cxml::Document::Document (string root_name)
- : _root_name (root_name)
+void
+cxml::Node::set_string_content (string c)
{
- _parser = new xmlpp::DomParser;
+ _content = c;
}
-cxml::Document::Document (string root_name, boost::filesystem::path file)
- : _root_name (root_name)
+void
+cxml::Node::set_bool_content (bool c)
{
- _parser = new xmlpp::DomParser ();
- read_file (file);
+ _content = c ? "yes" : "no";
}
-cxml::Document::Document ()
+cxml::NodePtr
+cxml::Node::add_string (string name, string content)
{
- _parser = new xmlpp::DomParser ();
+ NodePtr n = add (name);
+ n->set_string_content (content);
+ return n;
}
-
-cxml::Document::~Document ()
+
+cxml::NodePtr
+cxml::Node::add_bool (std::string name, bool content)
{
- delete _parser;
+ NodePtr n = add (name);
+ n->set_bool_content (content);
+ return n;
}
void
-cxml::Document::read_file (filesystem::path file)
+cxml::Document::read_file (boost::filesystem::path file)
{
- if (!filesystem::exists (file)) {
+ if (!boost::filesystem::exists (file)) {
throw cxml::Error ("XML file does not exist");
}
-
- _parser->parse_file (file.string ());
- take_root_node ();
+
+ xmlpp::DomParser parser;
+ parser.parse_file (file.string ());
+ read (parser.get_document()->get_root_node ());
}
void
cxml::Document::read_stream (istream& stream)
{
- _parser->parse_stream (stream);
- take_root_node ();
+ xmlpp::DomParser parser;
+ parser.parse_stream (stream);
+ read (parser.get_document()->get_root_node ());
}
void
cxml::Document::read_string (string s)
{
+ xmlpp::DomParser parser;
stringstream t (s);
- _parser->parse_stream (t);
- take_root_node ();
+ parser.parse_stream (t);
+ read (parser.get_document()->get_root_node ());
}
void
-cxml::Document::take_root_node ()
+cxml::Document::check_root_name (string r)
{
- if (!_parser) {
- throw cxml::Error ("could not parse XML");
- }
-
- _node = _parser->get_document()->get_root_node ();
- if (!_root_name.empty() && _node->get_name() != _root_name) {
+ if (name() != r) {
throw cxml::Error ("unrecognised root node");
- } else if (_root_name.empty ()) {
- _root_name = _node->get_name ();
}
}
+void
+cxml::Document::write_to_file_formatted (boost::filesystem::path path) const
+{
+ xmlpp::Document doc;
+ write_to_xmlpp_document (doc);
+ doc.write_to_file_formatted (path.string ());
+}
+
+void
+cxml::Document::write_to_string (std::string coding) const
+{
+ xmlpp::Document doc;
+ write_to_xmlpp_document (doc);
+ doc.write_to_string (coding);
+}
+
+void
+cxml::Document::write_to_xmlpp_document (xmlpp::Document& doc) const
+{
+ xmlpp::Element* root = doc.create_root_node (name ());
+ for (NodeList::const_iterator i = _children.begin(); i != _children.end(); ++i) {
+ (*i)->write (root);
+ }
+}
#undef check
#endif
-#include <glibmm.h>
-
namespace xmlpp {
class Node;
- class DomParser;
}
namespace cxml {
};
class Node;
-typedef std::list<boost::shared_ptr<Node> > NodeList;
+typedef boost::shared_ptr<cxml::Node> NodePtr;
+typedef std::list<NodePtr> NodeList;
+typedef boost::shared_ptr<const cxml::Node> ConstNodePtr;
-/** @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* node);
-
- std::string name () const;
+ Node (xmlpp::Node const *);
/* A set of methods which look up a child of this node by
* its name, and return its contents as some type or other.
return n;
}
- /** This will mark a child as to be ignored when calling done() */
- void ignore_child (std::string) const;
-
- /** Check whether all children of this Node have been looked up
- * or passed to ignore_child(). If not, an exception is thrown.
- */
- void done () const;
-
+
/* These methods look for an attribute of this node, in the
* same way as the child methods do.
*/
return n;
}
- /** @return The content of this node */
- std::string content () const;
- /** @return namespace URI of this node */
- std::string namespace_uri () const;
+ /* Setting content */
- /** @return namespace prefix of this node */
- std::string namespace_prefix () const;
+ void set_string_content (std::string content);
+ void set_bool_content (bool content);
+
+ template <class T>
+ void set_number_content (T content)
+ {
+ std::stringstream u;
+ u.imbue (std::locale::classic ());
+ u << content;
+ u >> _content;
+ }
+
+
+ /* Short-cuts to add nodes with content */
+
+ NodePtr add_string (std::string name, std::string content);
+ NodePtr add_bool (std::string name, bool content);
+
+ template <class T>
+ NodePtr add_number (std::string name, T content)
+ {
+ NodePtr n = add (name);
+ n->set_number_content (content);
+ return n;
+ }
+
+
+ /* Access to child nodes */
boost::shared_ptr<Node> node_child (std::string) const;
boost::shared_ptr<Node> optional_node_child (std::string) const;
-
NodeList node_children (std::string) const;
- xmlpp::Node* node () const {
- return _node;
+ /** Add a child node with a given name */
+ NodePtr add (std::string name)
+ {
+ NodePtr n (new cxml::Node ());
+ n->set_name (name);
+ _children.push_back (n);
+ return n;
}
-protected:
- xmlpp::Node* _node;
+ /** @return The content of this node */
+ std::string content () const;
+ /** @return namespace URI of this node */
+ std::string namespace_uri () const;
+ /** @return namespace prefix of this node */
+ std::string namespace_prefix () const;
+ /** This will mark a child as to be ignored when calling done() */
+ void ignore_child (std::string) const;
+ /** Check whether all children of this Node have been looked up
+ * or passed to ignore_child(). If not, an exception is thrown.
+ */
+ void done () const;
+ /** Set the name of the node.
+ * @param n New name.
+ */
+ void set_name (std::string n) {
+ _name = n;
+ }
+ /** @return Node name */
+ std::string name () const {
+ return _name;
+ }
+
+ /* We use xmlpp for parsing and writing XML out; these
+ methods help with that.
+ */
+ void read (xmlpp::Node const *);
+ void write (xmlpp::Node *) const;
+
+protected:
+ NodeList _children;
private:
- mutable std::list<Glib::ustring> _taken;
+ std::string _name;
+ std::string _content;
+ std::string _namespace_uri;
+ std::string _namespace_prefix;
+ std::list<std::pair<std::string, std::string> > _attributes;
+ mutable std::list<std::string> _taken;
};
-typedef boost::shared_ptr<cxml::Node> NodePtr;
-typedef boost::shared_ptr<const cxml::Node> ConstNodePtr;
-
class Document : public Node
{
public:
- Document ();
- Document (std::string root_name);
- Document (std::string root_name, boost::filesystem::path);
-
- virtual ~Document ();
+ Document () {}
void read_file (boost::filesystem::path);
void read_stream (std::istream &);
void read_string (std::string);
-
- std::string root_name () const {
- return _root_name;
- }
-
+
+ void check_root_name (std::string root_name);
+
+ void write_to_file_formatted (boost::filesystem::path) const;
+ void write_to_string (std::string) const;
+
private:
- void take_root_node ();
-
- xmlpp::DomParser* _parser;
- std::string _root_name;
+ void write_to_xmlpp_document (xmlpp::Document &) const;
};
}
+<?xml version="1.0" encoding="UTF-8"?>
<A>
<B>42</B>
<C>fred</C>
<E>yes</E>
<F>1</F>
<F>2</F>
- <H><I>testing</I><I>more testing</I></H>
- <J><K>jim</K></J>
+ <H>
+ <I>testing</I>
+ <I>more testing</I>
+ </H>
+ <J>
+ <K>jim</K>
+ </J>
</A>
using std::list;
using boost::shared_ptr;
-BOOST_AUTO_TEST_CASE (test)
+
+void
+check_xml (xmlpp::Element* ref, xmlpp::Element* test, list<string> ignore)
+{
+ BOOST_CHECK_EQUAL (ref->get_name (), test->get_name ());
+ BOOST_CHECK_EQUAL (ref->get_namespace_prefix (), test->get_namespace_prefix ());
+
+ if (find (ignore.begin(), ignore.end(), ref->get_name()) != ignore.end ()) {
+ return;
+ }
+
+ xmlpp::Element::NodeList ref_children = ref->get_children ();
+ xmlpp::Element::NodeList test_children = test->get_children ();
+ BOOST_CHECK_EQUAL (ref_children.size (), test_children.size ());
+
+ xmlpp::Element::NodeList::iterator k = ref_children.begin ();
+ xmlpp::Element::NodeList::iterator l = test_children.begin ();
+ while (k != ref_children.end ()) {
+
+ /* XXX: should be doing xmlpp::EntityReference, xmlpp::XIncludeEnd, xmlpp::XIncludeStart */
+
+ xmlpp::Element* ref_el = dynamic_cast<xmlpp::Element*> (*k);
+ xmlpp::Element* test_el = dynamic_cast<xmlpp::Element*> (*l);
+ BOOST_CHECK ((ref_el && test_el) || (!ref_el && !test_el));
+ if (ref_el && test_el) {
+ check_xml (ref_el, test_el, ignore);
+ }
+
+ xmlpp::ContentNode* ref_cn = dynamic_cast<xmlpp::ContentNode*> (*k);
+ xmlpp::ContentNode* test_cn = dynamic_cast<xmlpp::ContentNode*> (*l);
+ BOOST_CHECK ((ref_cn && test_cn) || (!ref_cn && !test_cn));
+ if (ref_cn && test_cn) {
+ BOOST_CHECK_EQUAL (ref_cn->get_content(), test_cn->get_content ());
+ }
+
+ xmlpp::Attribute* ref_at = dynamic_cast<xmlpp::Attribute*> (*k);
+ xmlpp::Attribute* test_at = dynamic_cast<xmlpp::Attribute*> (*l);
+ BOOST_CHECK ((ref_at && test_at) || (!ref_at && !test_at));
+ if (ref_at && test_at) {
+ BOOST_CHECK_EQUAL (ref_at->get_name(), test_at->get_name ());
+ BOOST_CHECK_EQUAL (ref_at->get_value(), test_at->get_value ());
+ }
+
+ ++k;
+ ++l;
+ }
+}
+
+void
+check_xml (boost::filesystem::path ref, boost::filesystem::path test, list<string> ignore)
{
- cxml::Document document ("A");
+ xmlpp::DomParser* ref_parser = new xmlpp::DomParser (ref.string ());
+ xmlpp::Element* ref_root = ref_parser->get_document()->get_root_node ();
+ xmlpp::DomParser* test_parser = new xmlpp::DomParser (test.string ());
+ xmlpp::Element* test_root = test_parser->get_document()->get_root_node ();
+
+ check_xml (ref_root, test_root, ignore);
+}
+
+BOOST_AUTO_TEST_CASE (read_test)
+{
+ cxml::Document document;
document.read_file ("test/ref/a.xml");
+ document.check_root_name ("A");
BOOST_CHECK_EQUAL (document.string_child("B"), "42");
BOOST_CHECK_EQUAL (document.number_child<int>("B"), 42);
BOOST_CHECK_EQUAL (document.node_children("J").front()->node_children("K").size(), 1);
BOOST_CHECK_EQUAL (document.node_children("J").front()->node_children("K").front()->content(), "jim");
}
+
+BOOST_AUTO_TEST_CASE (write_test)
+{
+ cxml::Document document;
+ document.set_name ("A");
+
+ document.add_number<int> ("B", 42);
+ document.add_string ("C", "fred");
+ document.add_number<double> ("D", 42.9);
+ document.add_bool ("E", true);
+ document.add_number<int> ("F", 1);
+ document.add_number<int> ("F", 2);
+ cxml::NodePtr h = document.add ("H");
+ h->add_string ("I", "testing");
+ h->add_string ("I", "more testing");
+ document.add ("J")->add_string ("K", "jim");
+
+ document.write_to_file_formatted ("build/test/b.xml");
+
+ check_xml ("test/ref/a.xml", "build/test/b.xml", list<string> ());
+}
def options(opt):
opt.load('compiler_cxx')
+ opt.add_option('--enable-debug', action='store_true', default=False, help='build with debugging information and without optimisation')
opt.add_option('--target-windows', action='store_true', default=False, help='set up to do a cross-compile to Windows')
opt.add_option('--static', action='store_true', default=False, help='build statically')
opt.add_option('--disable-tests', action='store_true', default=False, help='disable building of tests')
conf.load('compiler_cxx')
conf.env.append_value('CXXFLAGS', ['-Wall', '-Wextra', '-O2'])
+ if conf.options.enable_debug:
+ conf.env.append_value('CXXFLAGS', '-g')
+ else:
+ conf.env.append_value('CXXFLAGS', '-O2')
+
conf.env.TARGET_WINDOWS = conf.options.target_windows
conf.env.STATIC = conf.options.static
conf.env.DISABLE_TESTS = conf.options.disable_tests