C++ class for reading and writing XML structures. No need for a C++ code parser or special pre compiler. Specify a schema entirly in native C++. The schema is verified when XML is read and exceptions are thrown when the XML to be parse is invalid.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

486 lines
17 KiB

15 years ago
/*! @file
@id $Id$
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
15 years ago
#include <xml-cxx/xml.hxx>
15 years ago
#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 {
//============================================================================
Attributes::Value::Value(const std::string& name) throw():
Attributes::value_type(name, std::string()) {
}
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;
}
//================================================================= EXCEPTIONS
//----------------------------------------------------------------------------
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, Tag tag, char c) throw():
exception(reason, t), _pos(is.tellg()), _tag(tag), _char(c) {
}
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();
}
//============================================================================
//----------------------------------------------------------------------------
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) {
15 years ago
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];
}
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) {
15 years ago
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) {
15 years ago
_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);
}
//----------------------------------------------------------------------------
UnsingedInteger::UnsingedInteger(std::string name, const std::string& text) throw():
Node(name), _text(text) {
}
std::auto_ptr<Node> UnsingedInteger::clone() const throw() {
return std::auto_ptr<Node>(new UnsingedInteger(*this));
}
std::string UnsingedInteger::text() const throw() {
return _text;
}
UnsingedInteger& UnsingedInteger::text(const std::string& txt) throw(tag_expected,
type_mismatch) {
_text = txt;
return *this;
}
std::ostream& UnsingedInteger::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;
}
UnsingedInteger& UnsingedInteger::append(const Node& o) throw(cannot_have_children) {
throw cannot_have_children(*this, o);
}
Node& UnsingedInteger::operator=(const std::string& contents) throw() {
return text(contents);
}
15 years ago
//----------------------------------------------------------------------------
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,
15 years ago
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) {
15 years ago
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
15 years ago
}
}
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,
15 years ago
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
15 years ago
}
}
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) {
15 years ago
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;
}
15 years ago
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;
}
}
15 years ago
return tag;
}
}