blob: d85bfd88ed3f1e3edff2dd46df74c41cb40eb6da [file] [log] [blame]
/*
*
* D-Bus++ - C++ bindings for D-Bus
*
* Copyright (C) 2005-2007 Paolo Durante <shackan@gmail.com>
*
*
* 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 <dbus-c++/debug.h>
#include <expat.h>
namespace DBus {
namespace Xml {
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();
}
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("</"+name+">\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--;
}
} /* namespace Xml */
} /* namespace DBus */