/* * * D-Bus++ - C++ bindings for D-Bus * * Copyright (C) 2005-2007 Paolo Durante * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "xml.h" #include std::istream &operator >> (std::istream &in, DBus::Xml::Document &doc) { std::stringbuf xmlbuf; in.get(xmlbuf, '\0'); doc.from_xml(xmlbuf.str()); return in; } std::ostream &operator << (std::ostream &out, const DBus::Xml::Document &doc) { return out << doc.to_xml(); } using namespace DBus; using namespace DBus::Xml; Error::Error(const char *error, int line, int column) { std::ostringstream estream; estream << "line " << line << ", column " << column << ": " << error; _error = estream.str(); } Node::Node(const char *n, const char **a) : name(n) { if (a) for (int i = 0; a[i]; i += 2) { _attrs[a[i]] = a[i + 1]; //debug_log("xml:\t%s = %s", a[i], a[i+1]); } } Nodes Nodes::operator[](const std::string &key) { Nodes result; for (iterator i = begin(); i != end(); ++i) { Nodes part = (**i)[key]; result.insert(result.end(), part.begin(), part.end()); } return result; } Nodes Nodes::select(const std::string &attr, const std::string &value) { Nodes result; for (iterator i = begin(); i != end(); ++i) { if ((*i)->get(attr) == value) result.insert(result.end(), *i); } return result; } Nodes Node::operator[](const std::string &key) { Nodes result; if (key.length() == 0) return result; for (Children::iterator i = children.begin(); i != children.end(); ++i) { if (i->name == key) result.push_back(&(*i)); } return result; } std::string Node::get(const std::string &attribute) { if (_attrs.find(attribute) != _attrs.end()) return _attrs[attribute]; else return ""; } void Node::set(const std::string &attribute, std::string value) { if (value.length()) _attrs[attribute] = value; else _attrs.erase(value); } std::string Node::to_xml() const { std::string xml; int depth = 0; _raw_xml(xml, depth); return xml; } void Node::_raw_xml(std::string &xml, int &depth) const { xml.append(depth * 2, ' '); xml.append("<" + name); for (Attributes::const_iterator i = _attrs.begin(); i != _attrs.end(); ++i) { xml.append(" " + i->first + "=\"" + i->second + "\""); } if (cdata.length() == 0 && children.size() == 0) { xml.append("/>\n"); } else { xml.append(">"); if (cdata.length()) { xml.append(cdata); } if (children.size()) { xml.append("\n"); depth++; for (Children::const_iterator i = children.begin(); i != children.end(); ++i) { i->_raw_xml(xml, depth); } depth--; xml.append(depth * 2, ' '); } xml.append("\n"); } } Document::Document() : root(0), _depth(0) { } Document::Document(const std::string &xml) : root(0), _depth(0) { from_xml(xml); } Document::~Document() { delete root; } struct Document::Expat { static void start_doctype_decl_handler( void *data, const XML_Char *name, const XML_Char *sysid, const XML_Char *pubid, int has_internal_subset ); static void end_doctype_decl_handler(void *data); static void start_element_handler(void *data, const XML_Char *name, const XML_Char **atts); static void character_data_handler(void *data, const XML_Char *chars, int len); static void end_element_handler(void *data, const XML_Char *name); }; void Document::from_xml(const std::string &xml) { _depth = 0; delete root; root = 0; XML_Parser parser = XML_ParserCreate("UTF-8"); XML_SetUserData(parser, this); XML_SetDoctypeDeclHandler( parser, Document::Expat::start_doctype_decl_handler, Document::Expat::end_doctype_decl_handler ); XML_SetElementHandler( parser, Document::Expat::start_element_handler, Document::Expat::end_element_handler ); XML_SetCharacterDataHandler( parser, Document::Expat::character_data_handler ); XML_Status status = XML_Parse(parser, xml.c_str(), xml.length(), true); if (status == XML_STATUS_ERROR) { const char *error = XML_ErrorString(XML_GetErrorCode(parser)); int line = XML_GetCurrentLineNumber(parser); int column = XML_GetCurrentColumnNumber(parser); XML_ParserFree(parser); throw Error(error, line, column); } else { XML_ParserFree(parser); } } std::string Document::to_xml() const { return root->to_xml(); } void Document::Expat::start_doctype_decl_handler( void *data, const XML_Char *name, const XML_Char *sysid, const XML_Char *pubid, int has_internal_subset ) { } void Document::Expat::end_doctype_decl_handler(void *data) { } void Document::Expat::start_element_handler(void *data, const XML_Char *name, const XML_Char **atts) { Document *doc = (Document *)data; //debug_log("xml:%d -> %s", doc->_depth, name); if (!doc->root) { doc->root = new Node(name, atts); } else { Node::Children *cld = &(doc->root->children); for (int i = 1; i < doc->_depth; ++i) { cld = &(cld->back().children); } cld->push_back(Node(name, atts)); //std::cerr << doc->to_xml() << std::endl; } doc->_depth++; } void Document::Expat::character_data_handler(void *data, const XML_Char *chars, int len) { Document *doc = (Document *)data; Node *nod = doc->root; for (int i = 1; i < doc->_depth; ++i) { nod = &(nod->children.back()); } int x, y; x = 0; y = len - 1; while (isspace(chars[y]) && y > 0) --y; while (isspace(chars[x]) && x < y) ++x; nod->cdata = std::string(chars, x, y + 1); } void Document::Expat::end_element_handler(void *data, const XML_Char *name) { Document *doc = (Document *)data; //debug_log("xml:%d <- %s", doc->_depth, name); doc->_depth--; }