/* Copyright (C) 2012-2014 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include "cxml.h" 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 () { } cxml::Node::Node (xmlpp::Node const * node) { read (node); } void cxml::Node::read (xmlpp::Node const * node) { _name = node->get_name (); _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 (*i); if (v) { _content += v->get_content (); } else { _children.push_back (cxml::NodePtr (new cxml::Node (*i))); } } xmlpp::Element const * element = dynamic_cast (node); if (element) { xmlpp::Element::AttributeList attributes = element->get_attributes (); for (list::const_iterator i = attributes.begin(); i != attributes.end(); ++i) { _attributes.push_back (make_pair ((*i)->get_name(), (*i)->get_value ())); } } } void cxml::Node::write (xmlpp::Node* parent, map* mapping) const { xmlpp::Element* node = parent->add_child (_name, _namespace_prefix); if (mapping) { (*mapping)[shared_from_this()] = node; } if (!_content.empty ()) { node->add_child_text (_content); } for (KeyValueList::const_iterator i = _namespace_declarations.begin(); i != _namespace_declarations.end(); ++i) { node->set_namespace_declaration (i->first, i->second); } for (KeyValueList::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); } } cxml::NodePtr cxml::Node::child (string name) const { NodeList n = 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 " + _name); } return n.front (); } cxml::NodePtr cxml::Node::optional_child (string name) const { NodeList n = children (name); if (n.size() > 1) { throw cxml::Error ("duplicate XML tag " + name); } else if (n.empty ()) { return NodePtr (); } return n.front (); } cxml::NodeList cxml::Node::children () const { return _children; } cxml::NodeList cxml::Node::children (string name) const { NodeList n; 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; } string cxml::Node::string_child (string c) const { return child(c)->content (); } optional cxml::Node::optional_string_child (string c) const { NodeList nodes = children (c); if (nodes.size() > 1) { throw cxml::Error ("duplicate XML tag " + c); } if (nodes.empty ()) { return optional (); } return nodes.front()->content(); } bool cxml::Node::bool_child (string c) const { string const s = string_child (c); return (s == "1" || s == "yes"); } optional cxml::Node::optional_bool_child (string c) const { optional s = optional_string_child (c); if (!s) { return optional (); } return (s.get() == "1" || s.get() == "yes"); } void cxml::Node::ignore_child (string name) const { _taken.push_back (name); } string cxml::Node::string_attribute (string name) const { KeyValueList::const_iterator i = _attributes.begin (); while (i != _attributes.end() && i->first != name) { ++i; } if (i == _attributes.end ()) { throw cxml::Error ("missing attribute"); } return i->second; } optional cxml::Node::optional_string_attribute (string name) const { KeyValueList::const_iterator i; while (i != _attributes.end() && i->first != name) { ++i; } if (i == _attributes.end ()) { return optional (); } return i->second; } bool cxml::Node::bool_attribute (string name) const { string const s = string_attribute (name); return (s == "1" || s == "yes"); } optional cxml::Node::optional_bool_attribute (string name) const { optional s = optional_string_attribute (name); if (!s) { return optional (); } return (s.get() == "1" || s.get() == "yes"); } void cxml::Node::done () const { 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 { return _content; } string cxml::Node::namespace_prefix () const { return _namespace_prefix; } void cxml::Node::set_attribute (string name, string value) { KeyValueList::iterator i = _attributes.begin (); while (i != _attributes.end() && i->first != name) { ++i; } if (i != _attributes.end ()) { _attributes.erase (i); } _attributes.push_back (make_pair (name, value)); } void cxml::Node::set_namespace_declaration (string uri, string ns) { KeyValueList::iterator i = _namespace_declarations.begin (); while (i != _namespace_declarations.end() && i->second != ns) { ++i; } if (i != _namespace_declarations.end ()) { _namespace_declarations.erase (i); } _namespace_declarations.push_back (make_pair (uri, ns)); } cxml::NodePtr cxml::read_file (boost::filesystem::path file) { if (!boost::filesystem::exists (file)) { throw cxml::Error ("XML file does not exist"); } xmlpp::DomParser parser; parser.parse_file (file.string ()); cxml::NodePtr node (new cxml::Node); node->read (parser.get_document()->get_root_node ()); return node; } cxml::NodePtr cxml::read_stream (istream& stream) { xmlpp::DomParser parser; parser.parse_stream (stream); cxml::NodePtr node (new cxml::Node); node->read (parser.get_document()->get_root_node ()); return node; } cxml::NodePtr cxml::read_string (string s) { xmlpp::DomParser parser; stringstream t (s); parser.parse_stream (t); cxml::NodePtr node (new cxml::Node); node->read (parser.get_document()->get_root_node ()); return node; } void cxml::write_to_xmlpp_document (cxml::ConstNodePtr node, xmlpp::Document& doc, map* mapping) { xmlpp::Element* root = doc.create_root_node (node->name ()); if (mapping) { (*mapping)[node] = root; } cxml::KeyValueList namespace_declarations = node->namespace_declarations (); for (cxml::KeyValueList::const_iterator i = namespace_declarations.begin(); i != namespace_declarations.end(); ++i) { root->set_namespace_declaration (i->first, i->second); } cxml::NodeList children = node->children (); for (cxml::NodeList::const_iterator i = children.begin(); i != children.end(); ++i) { (*i)->write (root, mapping); } } void cxml::write_to_file (cxml::ConstNodePtr node, boost::filesystem::path path) { xmlpp::Document doc; write_to_xmlpp_document (node, doc); doc.write_to_file (path.string ()); } void cxml::write_to_file_formatted (cxml::ConstNodePtr node, boost::filesystem::path path) { xmlpp::Document doc; write_to_xmlpp_document (node, doc); doc.write_to_file_formatted (path.string ()); } void cxml::write_to_stream_formatted (cxml::ConstNodePtr node, ostream& stream) { xmlpp::Document doc; write_to_xmlpp_document (node, doc); doc.write_to_stream_formatted (stream); } string cxml::write_to_string (cxml::ConstNodePtr node) { xmlpp::Document doc; write_to_xmlpp_document (node, doc); return doc.write_to_string (); } string cxml::write_to_string_formatted (cxml::ConstNodePtr node) { xmlpp::Document doc; write_to_xmlpp_document (node, doc); return doc.write_to_string_formatted (); }