Files
libxml-cxx/src/xml.cxx
2009-04-06 14:57:27 +00:00

537 lines
19 KiB
C++

/*! @file
@id $Id$
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
#include <xml-cxx/xml.hxx>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <iomanip>
class MethodTrace {
public:
MethodTrace(const void* addr, const std::string& name) throw():
_addr(addr), _name(name) {
std::clog<<std::hex<<std::setw(15)<<_addr<<": "
<<std::dec<<std::setw(2+_level)
<<std::setfill(' ')<<"\\ "<<_name<<std::endl;
++_level;
}
~MethodTrace() throw() {
--_level;
std::clog<<std::hex<<std::setw(15)<<_addr<<": "<<std::dec
<<std::setw(2+_level)<<std::setfill(' ')
<<"/ "<<_name<<std::endl;
}
private:
const void* _addr;
const std::string _name;
static unsigned int _level;
};
unsigned int MethodTrace::_level(0);
#define TRACE MethodTrace XXX_METHOD(this, __PRETTY_FUNCTION__)
#define LOG(X) std::clog<<__PRETTY_FUNCTION__<<"\t**** "<<X<<std::endl
namespace xml {
//================================================================= EXCEPTIONS
//----------------------------------------------------------------------------
exception::exception(std::string reason) throw():
_what(reason), _node(0) {
}
exception::exception(std::string reason, const Node& t) throw():
_what(reason), _node(t.clone().release()) {
}
exception::~exception() throw() {
delete _node;
}
const char* exception::what() const throw() {
static std::string w;
if (!w.size()) {
if (_node)
if (_node->isChild())
w = _what+"\ntag: "+_node->name()+"\nparent: "+_node->parent().name();
else
w = _what+"\ntag: "+_node->name()+" (root element)";
else
w = _what;
}
return w.c_str();
}
//----------------------------------------------------------------------------
access_error::access_error(const Node& t, const std::string& name) throw():
exception("child not available", t), _name(name) {
}
const char* access_error::what() const throw() {
static std::string w;
if (!w.size()) w = std::string(exception::what())+"\nchild name: "+_name;
return w.c_str();
}
//----------------------------------------------------------------------------
out_of_range::out_of_range(const Node& t, size_t pos) throw():
exception("child not available", t), _pos(pos) {
}
const char* out_of_range::what() const throw() {
static std::stringstream w;
if (!w.str().size()) w<<exception::what()<<"\nchild number: "<<_pos;
return w.str().c_str();
}
//----------------------------------------------------------------------------
cannot_have_children::cannot_have_children(const Node& parent,
const Node& child) throw():
exception("node does not accept child nodes", parent),
_child(child.clone().release()) {
}
cannot_have_children::~cannot_have_children() throw() {
delete _child;
}
const char* cannot_have_children::what() const throw() {
static std::string w;
if (!w.size())
w = std::string(exception::what())+"\nchild name: "+_child->name();
return w.c_str();
}
//----------------------------------------------------------------------------
stream_error::stream_error(const std::string& reason, const Node& t,
std::istream& is, const Tag& tag, char c) throw():
exception(reason, t), _pos(is.tellg()), _tag(new Tag(tag)), _char(c) {
}
stream_error::~stream_error() throw() {
delete _tag;
}
const char* stream_error::what() const throw() {
static std::string w;
if (!w.size()) {
std::stringstream ss;
ss<<exception::what()
<<"\nposition in stream: "<<_pos;
if (_char)
ss<<"\nactual character: "<<(_char>31?_char:'?')
<<" (ascii="<<(int)_char<<")";
ss<<"\ntag type: "<<(_tag->type==START?"START"
:(_tag->type==END?"END"
:(_tag->type==EMPTY?"EMPTY"
:"???? <UNKNOWN>")));
if (_tag->name.size()) ss<<"\nnode name read: "<<_tag->name;
if (_tag->text.size()) ss<<"\ncontained text: "<<_tag->text;
w = ss.str();
}
return w.c_str();
}
//============================================================================
//----------------------------------------------------------------------------
Attributes::Value::Value(const std::string& name) throw():
Attributes::value_type(name, std::string()) {
}
Attributes::Value::Value(const std::string& name,
const std::string& value) throw():
Attributes::value_type(name, value) {
}
const std::string& Attributes::Value::name() const throw() {
return first;
}
const std::string& Attributes::Value::value() const throw() {
return second;
}
std::string& Attributes::Value::value() throw() {
return second;
}
Attributes::Value::operator bool() const throw() {
/*! @return @c true if the value is set and not equal to one of:
@c false @c no @c 0. */
return !(second.size()||second=="false"||second=="no"||second=="0");
}
Attributes::Value::operator unsigned long() const throw() {
std::stringstream ss(second);
int i(0);
ss>>i;
return i;
}
Attributes::Value::operator List() const throw() {
return toList();
}
Attributes::List Attributes::Value::toList(const std::string& separators)
const throw() {
List l;
for (std::string::size_type it(0), pos(0);
(pos=second.find_first_of(separators, it)), it!=std::string::npos;
it=pos!=std::string::npos?++pos:std::string::npos)
l.push_back(std::string(second.begin()+it, second.begin()+pos));
return l;
}
//----------------------------------------------------------------------------
Attributes::value_type& Attributes::Value::operator=(const std::string& value)
throw() {
second = value;
return *this;
}
Attributes::Attributes() throw(): _active(end()) {}
Attributes::Attributes(const std::string& empty) throw() {
_active = insert(Value(empty)).first;
}
Attributes& Attributes::operator<<(const value_type& v) throw() {
_active = insert(v).first;
return *this;
}
Attributes& Attributes::operator<<(const std::string& key) throw() {
_active = insert(Value(key)).first;
return *this;
}
Attributes& Attributes::operator=(const std::string& value) throw() {
if (_active==end()) return *this;
_active->second = value;
_active = end();
return *this;
}
//----------------------------------------------------------------------------
Node::Node(std::string name) throw():
_name(name), _parent(0) {
}
Node::Node(const Node& o) throw():
_attributes(o._attributes), _name(o.name()), _parent(0) {
for (Contents::const_iterator it(o._contents.begin());
it!=o._contents.end(); ++it)
_contents.push_back((*it)->clone(this).release());
}
Node& Node::operator=(const Node& o) throw() {
_attributes=o._attributes;
_name = o.name();
_parent = 0;
for (Contents::const_iterator it(o._contents.begin());
it!=o._contents.end(); ++it)
_contents.push_back((*it)->clone(this).release());
}
Node::~Node() throw() {
clear();
}
std::auto_ptr<Node> Node::clone() const throw() {
std::auto_ptr<Node> res(new Node(*this));
res->_parent = 0;
return res;
}
std::ostream& Node::out(std::ostream& o, unsigned int level) const throw() {
if (_contents.size()) {
o<<std::string(level, '\t')<<'<'<<name();
for (Attributes::const_iterator it(_attributes.begin());
it!=_attributes.end(); ++it)
o<<' '<<it->first<<"=\""<<it->second<<'"';
o<<'>'<<std::endl;
for (Contents::const_iterator it(_contents.begin());
it!=_contents.end(); ++it)
(*it)->out(o, level+1)<<std::endl;
o<<std::string(level, '\t')<<"</"<<name()<<'>';
} else {
o<<std::string(level, '\t')<<'<'<<name();
for (Attributes::const_iterator it(_attributes.begin());
it!=_attributes.end(); ++it)
o<<' '<<it->first<<"=\""<<it->second<<'"';
o<<"/>";
}
return o;
}
std::string Node::text() const throw() {
std::string s;
for (Contents::const_iterator it(_contents.begin());
it!=_contents.end(); ++it)
s+=***it;
return s;
}
Node& Node::text(const std::string& txt) throw(tag_expected, type_mismatch) {
throw tag_expected(*this, txt);
}
Node& Node::append(const Node& o) throw(cannot_have_children) {
_contents.push_back(o.clone(this).release());
return *this;
}
Node& Node::set(const Attributes& o) throw() {
_attributes.insert(o.begin(), o.end());
return *this;
}
Node& Node::clear() throw () {
for (Contents::const_iterator it(_contents.begin());
it!=_contents.end(); ++it)
delete *it;
_contents.clear();
_attributes.clear();
return *this;
}
std::string Node::name() const throw() {
return _name;
}
bool Node::isChild() const throw() {
return _parent;
}
Node& Node::parent() const throw(no_parent) {
if (!_parent) throw no_parent(*this);
return *_parent;
}
bool Node::hasAttr(const std::string& name) const throw() {
return _attributes.find(name)!=_attributes.end();
}
Node& Node::attr(const std::string& name, bool mandatory) throw() {
_attributes[name] = mandatory?"mandatory":"optional";
return *this;
}
std::string Node::attr(const std::string& name) const throw() {
Attributes::const_iterator it(_attributes.find(name));
if (it!=_attributes.end()) return it->second;
return std::string();
}
std::string& Node::attr(const std::string& name) throw() {
return _attributes[name];
}
Node::List Node::list(const std::string& name) const throw() {
List res;
for (Contents::const_iterator it(_contents.begin());
it!=_contents.end(); ++it)
if ((*it)->_name==name) res.push_back(*it);
return res;
}
bool Node::operator()(const std::string& child) const throw() {
return find(child);
}
Node& Node::operator<<(const Node& o) throw(cannot_have_children) {
return append(o);
}
Node& Node::operator<<(const Attributes& o) throw() {
return set(o);
}
Node::size_type Node::children() const throw() {
return _contents.size();
}
const Node& Node::operator[](Node::size_type child) const
throw(access_error) try {
return *_contents.at(child);
} catch (...) {
throw out_of_range(*this, child);
}
Node& Node::operator[](Node::size_type child) throw(access_error) try {
return *_contents.at(child);
} catch (...) {
throw out_of_range(*this, child);
}
const Node& Node::operator[](const std::string& child) const
throw(access_error) {
const Node* const t(find(child));
if (!t) throw access_error(*this, child);
return *t;
}
Node& Node::operator[](const std::string& child) throw(access_error) {
Node* const t(find(child));
if (!t) throw access_error(*this, child);
return *t;
}
std::string Node::operator*() const throw() {
return text();
}
Node& Node::operator=(const std::string& contents) throw(tag_expected,
type_mismatch) {
return text(contents);
}
std::ostream& operator<<(std::ostream& o, const Node& t) throw() {
return t.out(o);
}
Node* Node::find(const std::string& child) const throw() {
for (Contents::const_iterator it(_contents.begin());
it!=_contents.end(); ++it) {
if ((*it)->name()==child) return *it;
}
return 0;
}
std::auto_ptr<Node> Node::clone(Node* p) const throw() {
std::auto_ptr<Node> c(clone());
c->_parent = p;
return c;
}
//----------------------------------------------------------------------------
String::String(std::string name, const std::string& text) throw():
Node(name), _text(text) {
}
std::auto_ptr<Node> String::clone() const throw() {
return std::auto_ptr<Node>(new String(*this));
}
std::string String::text() const throw() {
return _text;
}
String& String::text(const std::string& txt) throw(tag_expected,
type_mismatch) {
_text = txt;
return *this;
}
std::ostream& String::out(std::ostream& o, unsigned int level) const throw() {
if (_text.size()) {
o<<std::string(level, '\t')<<'<'<<name();
for (Attributes::const_iterator it(_attributes.begin());
it!=_attributes.end(); ++it)
o<<' '<<it->first<<"=\""<<it->second<<'"';
o<<'>'<<_text<<"</"<<name()<<'>';
} else {
o<<std::string(level, '\t')<<'<'<<name();
for (Attributes::const_iterator it(_attributes.begin());
it!=_attributes.end(); ++it)
o<<' '<<it->first<<"=\""<<it->second<<'"';
o<<"/>";
}
return o;
}
String& String::append(const Node& o) throw(cannot_have_children) {
throw cannot_have_children(*this, o);
}
Node& String::operator=(const std::string& contents) throw() {
return text(contents);
}
//----------------------------------------------------------------------------
UnsignedInteger::UnsignedInteger(std::string name, unsigned long i) throw():
String(name,
dynamic_cast<std::stringstream&>(std::stringstream()<<i).str()) {
}
std::auto_ptr<Node> UnsignedInteger::clone() const throw() {
return std::auto_ptr<Node>(new UnsignedInteger(*this));
}
UnsignedInteger& UnsignedInteger::text(const std::string& txt)
throw(tag_expected, type_mismatch) {
std::string::size_type
start(txt.find_first_not_of(" \t\n\r")),
last(txt.find_last_of(" \t\n\r"));
if (start==std::string::npos) { // empty - set to 0
_text = "0";
return *this;
}
if (txt.find_first_not_of
("0123456789", start,
last!=std::string::npos?last-start+1:txt.size()-start)
!=std::string::npos)
throw type_mismatch(*this, txt,
"unsigned integer may only contain numbers");
unsigned long i(0);
std::stringstream(txt)>>i;
std::stringstream ss;
ss<<i;
_text = ss.str();
return *this;
}
unsigned long UnsignedInteger::number() const throw() {
return number(*this);
}
unsigned long UnsignedInteger::number(const Node& node) throw() {
unsigned long i(0);
std::stringstream ss(node.text());
ss>>i;
return i;
}
//----------------------------------------------------------------------------
Factory::Factory(const Node& t) throw(): _template(t.clone()) {}
const Node& Factory::operator*() const throw() {
return *_template;
}
std::auto_ptr<Node> Factory::read(std::istream& is)
throw(wrong_end_tag, wrong_start_tag, tag_expected, type_mismatch,
second_slash_in_tag,
character_after_slash, missing_end_tag, attribute_value_not_quoted,
access_error, empty_stream, duplicate_attribute,
attributes_in_end_tag) {
std::auto_ptr<Node> node(_template->clone());
node->clear();
Tag res;
try {
res = tag(is, *node);
} catch (missing_end_tag&) {
throw empty_stream(*node, is, res);
}
*node<<res.attributes;
while (true) switch (res.type) {
case END: throw wrong_end_tag(*node, is, res);
case EMPTY: return node; // empty
case START: return read(is, *_template);
case SPECIAL: break; //! @todo ignored could be stored
}
}
bool Factory::ws(char c) const throw() {
return c==' '||c=='\t'||c=='\n'||c=='\r';
}
std::auto_ptr<Node> Factory::read(std::istream& is, const Node& node)
throw(wrong_end_tag, wrong_start_tag, tag_expected, type_mismatch,
second_slash_in_tag,
character_after_slash, missing_end_tag, attribute_value_not_quoted,
access_error, duplicate_attribute, attributes_in_end_tag) {
std::auto_ptr<Node> result(node.clone());
result->clear();
for (bool finished(false); is && !finished;) {
Tag res(tag(is, node));
if (res.text.size()) result->text(res.text);
switch (res.type) {
case END: {
if (res.attributes.size()) throw attributes_in_end_tag(node, is, res);
if (res.name!=node.name()) throw wrong_end_tag(node, is, res);
finished = true;
} break;
case EMPTY: {
*result<<(node[res.name].clone()->clear()<<res.attributes);
} break;
case START: {
*result<<(*read(is, node[res.name])<<res.attributes);
} break;
case SPECIAL: break; //! @todo ignored could be stored
}
}
return result;
}
Tag Factory::tag(std::istream& is, const Node& position)
throw(second_slash_in_tag, character_after_slash,
missing_end_tag, attribute_value_not_quoted,
access_error, duplicate_attribute) {
char c(0);
Tag tag((Tag){"", START, "", Attributes()});
while (is && is.get(c) && ws(c)); // skip ws
if (!is) throw missing_end_tag(position, is, tag);
if (c!='<') do tag.text+=c; while (is && is.get(c) && c!='<');
bool nameRead(false);
for (char last(c); is && is.get(c) && c!='>'; last = c) switch (c) {
case ' ': case '\t': case '\n': case '\r':
if (!nameRead && tag.name.size()) nameRead=true;
break;
case '/':
if (!nameRead && tag.name.size()) nameRead=true;
if (tag.type!=START) throw second_slash_in_tag(position, is, tag, c);
tag.type = nameRead?EMPTY:END;
break;
case '!': case '?':
if (last=='<') { // <! tag or <?xml.. starts
do tag.special+=c; while (c!='>' && is && is.get(c));
return tag;
}
default:
if (tag.type==EMPTY) throw character_after_slash(position, is, tag, c);
if (nameRead) { // read attribute
std::string attrname(1, c), attrvalue;
while (is && is.get(c) && c!='=' && !ws(c)) attrname+=c;
while (c!='=' && is && is.get(c) && ws(c)); // skip ws, search '='
if (c=='=') { // get the value
while (is && is.get(c) && ws(c)); // skip ws
if (c!='"')
throw attribute_value_not_quoted(position, is, tag, c, attrname);
while (is && is.get(c) && c!='"') attrvalue+=c;
if (c!='"')
throw attribute_value_not_quoted(position, is, tag, c, attrname);
} else is.unget(); // read too far
if (tag.attributes.find(attrname)!=tag.attributes.end())
throw duplicate_attribute(position, is, tag, attrname);
tag.attributes[attrname] = attrvalue;
} else { // part of a tag name
tag.name+=c;
}
}
return tag;
}
}