Various fixes and missing bits.
[libcxml.git] / src / cxml.cc
index b251ae79610a45e4b27f3d31d19c8c0a566a210c..810cf92aaeafde75534955a7b457acec8a4ffa48 100644 (file)
 #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 std::ostream;
+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;
 }
@@ -101,7 +139,7 @@ cxml::Node::string_child (string c) const
 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);
        }
@@ -140,33 +178,31 @@ cxml::Node::ignore_child (string name) const
 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
@@ -190,10 +226,9 @@ cxml::Node::optional_bool_attribute (string name) const
 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());
                }
        }
 }
@@ -201,77 +236,127 @@ cxml::Node::done () const
 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 ? "1" : "0";
 }
 
-cxml::Document::~Document ()
+void
+cxml::Node::add_string_attribute (string name, string value)
 {
-       delete _parser;
+       _attributes.push_back (make_pair (name, value));
 }
 
 void
-cxml::Document::read_file (filesystem::path file)
+cxml::Node::add_bool_attribute (string name, bool value)
 {
-       if (!filesystem::exists (file)) {
+       add_string_attribute (name, value ? "1" : "0");
+}
+
+cxml::NodePtr
+cxml::Node::add_string_child (string name, string content)
+{
+       NodePtr n = add_child (name);
+       n->set_string_content (content);
+       return n;
+}
+       
+cxml::NodePtr
+cxml::Node::add_bool_child (std::string name, bool content)
+{
+       NodePtr n = add_child (name);
+       n->set_bool_content (content);
+       return n;
+}
+
+void
+cxml::Document::read_file (boost::filesystem::path 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::take_root_node ()
+cxml::Document::read_string (string s)
 {
-       if (!_parser) {
-               throw cxml::Error ("could not parse XML");
-       }
+       xmlpp::DomParser parser;
+       stringstream t (s);
+       parser.parse_stream (t);
+       read (parser.get_document()->get_root_node ());
+}
 
-       _node = _parser->get_document()->get_root_node ();
-       if (_node->get_name() != _root_name) {
+void
+cxml::Document::check_root_name (string r)
+{
+       if (name() != r) {
                throw cxml::Error ("unrecognised root node");
        }
 }
 
+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_stream_formatted (ostream& stream, string coding) const
+{
+       xmlpp::Document doc;
+       write_to_xmlpp_document (doc);
+       doc.write_to_stream_formatted (stream, coding);
+}
+
+string
+cxml::Document::write_to_string (std::string coding) const
+{
+       xmlpp::Document doc;
+       write_to_xmlpp_document (doc);
+       return 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);
+       }
+}