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.

743 lines
26 KiB

16 years ago
/*! @file
@id $Id$
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
16 years ago
#include <xml-cxx/xml.hxx>
16 years ago
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <cassert>
16 years ago
#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
16 years ago
namespace xml {
//================================================================= EXCEPTIONS
//----------------------------------------------------------------------------
exception::exception(std::string reason) throw():
_what(reason), _node(0) {
}
16 years ago
exception::exception(std::string reason, const Node& t) throw():
_what(reason), _node(t.clone().release()) {
}
exception::~exception() throw() {
delete _node;
}
16 years ago
void exception::line(unsigned long line) throw() {
std::stringstream ss;
ss<<line;
_what += "\nat line: "+ss.str();
}
16 years ago
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(const std::string& reason, const Node& t,
std::istream& is) throw():
exception(reason, t), _pos(is.tellg()), _tag(0), _char(0) {
}
stream_error::~stream_error() throw() {
delete _tag;
16 years ago
}
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<<")";
if (_tag) {
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;
}
16 years ago
w = ss.str();
}
return w.c_str();
}
//============================================================================
//----------------------------------------------------------------------------
16 years ago
//! Copy an attribute.
Attributes::Value::Value(const value_type& o) throw():
Attributes::value_type(o) {
}
//! Construct an empty attribute.
Attributes::Value::Value(const std::string& name) throw():
Attributes::value_type(name, std::string()) {
}
16 years ago
//! Construct an attribute with name an value.
Attributes::Value::Value(const std::string& name,
const std::string& value) throw():
Attributes::value_type(name, value) {
}
16 years ago
//! Assign a value.
Attributes::Value& Attributes::Value::operator=(const std::string& value)
throw() {
second = value;
return *this;
}
//! Get the attribute name.
const std::string& Attributes::Value::name() const throw() {
return first;
}
16 years ago
//! Get the attribute value.
const std::string& Attributes::Value::value() const throw() {
return second;
}
16 years ago
//! Get the attribute value.
std::string& Attributes::Value::value() throw() {
return second;
}
16 years ago
//! Convert the attribute to a boolean.
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");
}
16 years ago
//! Convert the attribute to a number.
Attributes::Value::operator unsigned long() const throw() {
std::stringstream ss(second);
int i(0);
ss>>i;
return i;
}
16 years ago
//! Convert the attribute to a space separated list.
Attributes::Value::operator List() const throw() {
return toList();
}
16 years ago
//! Convert the attribute to a space separated list.
Attributes::List Attributes::Value::toList(const std::string& separators)
const throw() {
List l;
for (std::string::size_type it(0), pos(0);
it!=std::string::npos &&
((pos=second.find_first_of(separators, it)), true);
it=pos!=std::string::npos?++pos:std::string::npos)
if (pos==std::string::npos)
l.push_back(second.substr(it));
else
l.push_back(second.substr(it, pos-it));
return l;
}
std::string Attributes::Value::front(const std::string& separators) const
throw(empty_attribute_list) {
List l(toList(separators));
if (!l.size()) throw empty_attribute_list(first);
return l.front();
}
//----------------------------------------------------------------------------
16 years ago
//! Empty attribute list
Attributes::Attributes() throw() {}
//! Attribute list with first one empty attribute given.
Attributes::Attributes(const std::string& empty) throw() {
16 years ago
insert(Value(empty));
}
16 years ago
//! Attribute list with first attribute given.
Attributes::Attributes(const std::string& key,
const std::string& value) throw() {
insert(Value(key, value));
}
16 years ago
//! Add a new key-value pair to the attribute list.
Attributes& Attributes::operator<<(const Attributes::Value& v) throw() {
insert(v);
return *this;
}
16 years ago
//! Add a new empty key to the attribute list.
Attributes& Attributes::operator<<(const std::string& key) throw() {
insert(Value(key));
return *this;
}
16 years ago
//----------------------------------------------------------------------------
16 years ago
//! Nodes must be given a name.
/*! Unnamed nodes are not allowed, therefore there's no default
constructor. */
Node::Node(std::string name,
Node::size_type min, Node::size_type max) throw():
_name(name), _parent(0), _min(min), _max(max) {
16 years ago
}
Node::Node(const Node& o) throw():
_attributes(o._attributes), _name(o.name()), _parent(0),
_min(o._min), _max(o._max) {
16 years ago
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;
_min = o._min;
_max = o._max;
16 years ago
for (Contents::const_iterator it(o._contents.begin());
it!=o._contents.end(); ++it)
_contents.push_back((*it)->clone(this).release());
}
Node::~Node() throw() {
clear();
}
16 years ago
//! Clones But clears the parent.
16 years ago
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) {
16 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;
}
Node& Node::min(Node::size_type m) throw() {
_min = m;
return *this;
}
Node::size_type Node::min() const throw() {
return _min;
}
Node& Node::max(Node::size_type m) throw() {
_max = m;
return *this;
}
Node::size_type Node::max() const throw() {
return _max;
}
16 years ago
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?"xml::mandatory":"xml::optional";
return *this;
}
Node& Node::attr(const std::string& name, const std::string& deflt)
throw() {
_attributes[name] = deflt;
16 years ago
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() {
16 years ago
return _attributes[name];
}
const Attributes::Value Node::attribute(const std::string& name)
const throw(attribute_not_available) {
Attributes::const_iterator it(_attributes.find(name));
if (it!=_attributes.end()) return *it;
throw attribute_not_available(*this, name);
}
const Attributes& Node::attributes() const throw() {
return _attributes;
}
Attributes& Node::attributes() throw() {
return _attributes;
}
Node& Node::limits(size_type min, size_type max) throw() {
_min = min;
16 years ago
_max = max;
return *this;
}
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;
}
16 years ago
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(out_of_range) try {
16 years ago
return *_contents.at(child);
} catch (...) {
throw out_of_range(*this, child);
}
Node& Node::operator[](Node::size_type child) throw(out_of_range) try {
16 years ago
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) {
16 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,
Node::size_type min, Node::size_type max) throw():
Node(name, min, max) {
}
String::String(std::string name, const std::string& text,
Node::size_type min, Node::size_type max) throw():
Node(name, min, max), _text(text) {
16 years ago
}
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) {
16 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);
}
//----------------------------------------------------------------------------
UnsignedInteger::UnsignedInteger(std::string name, unsigned long i,
size_type min, size_type max) throw():
16 years ago
String(name,
dynamic_cast<std::stringstream&>(std::stringstream()<<i).str(),
min, max) {
16 years ago
}
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_not_of(" \t\n\r"));
16 years ago
if (start==std::string::npos) { // empty - set to 0
_text = "0";
return *this;
}
std::string::size_type pos(txt.find_first_not_of("0123456789", start));
if (pos<last || pos==last && last==start)
16 years ago
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;
}
16 years ago
unsigned long UnsignedInteger::number() const throw() {
return number(*this);
}
16 years ago
unsigned long UnsignedInteger::number(const Node& node) throw() {
unsigned long i(0);
std::stringstream ss(node.text());
ss>>i;
return i;
}
16 years ago
//----------------------------------------------------------------------------
Factory::Factory(const Node& t) throw():
_template(xml::Node("<xml::start>")<<t), _line(0) {
_template[0].min(1);
_template[0].max(1);
}
16 years ago
const Node& Factory::operator*() const throw() {
return _template[0];
16 years ago
}
std::ostream& operator<<(std::ostream& os, const Factory& factory) throw() {
return factory.print(os, *factory);
}
std::ostream& Factory::print(std::ostream& os, const Node& node,
unsigned int level) throw() {
if (node.children()) {
os<<std::string(level, '\t')<<'<'<<node.name();
for (Attributes::const_iterator it(node.attributes().begin());
it!=node.attributes().end(); ++it)
os<<' '<<it->first<<"=\""<<it->second<<'"';
os<<'>';
if (dynamic_cast<const UnsignedInteger*>(&node))
os<<" (Unsigned Integer";
else if (dynamic_cast<const String*>(&node))
os<<" (String";
else
os<<" (Node";
if (node.min()==1 && node.max()==1)
os<<", required exactly once)";
else if (node.min()==1 && node.max()==0)
os<<", required at least once)";
else if (node.min()==0 && node.max()==0)
os<<", 0..n)";
else if (node.min()==0 && node.max()==1)
os<<", optional)";
else
os<<", "<<node.min()<<".."<<node.max()<<")";
os<<std::endl;
for (Node::size_type i(0); i<node.children(); ++i)
print(os, node[i], level+1);
16 years ago
os<<std::string(level, '\t')<<"</"<<node.name()<<'>'<<std::endl;
} else {
os<<std::string(level, '\t')<<'<'<<node.name();
for (Attributes::const_iterator it(node.attributes().begin());
it!=node.attributes().end(); ++it)
os<<' '<<it->first<<"=\""<<it->second<<'"';
os<<"/>";
if (dynamic_cast<const UnsignedInteger*>(&node))
os<<" (Unsigned Integer";
else if (dynamic_cast<const String*>(&node))
os<<" (String";
else
os<<" (Node";
if (node.min()==1 && node.max()==1)
os<<", required exactly once)";
else if (node.min()==1 && node.max()==0)
os<<", required at least once)";
else if (node.min()==0 && node.max()==0)
os<<", 0..n)";
else if (node.min()==0 && node.max()==1)
os<<", optional)";
else
os<<", "<<node.min()<<".."<<node.max()<<")";
os<<std::endl;
}
return os;
}
16 years ago
std::auto_ptr<Node> Factory::read(std::istream& is)
throw(wrong_end_tag, wrong_start_tag, tag_expected, type_mismatch,
second_slash_in_tag,
16 years ago
character_after_slash, missing_end_tag, attribute_value_not_quoted,
access_error, duplicate_attribute,
attributes_in_end_tag,
illegal_attribute, mandatory_attribute_missing,
wrong_node_number) try {
16 years ago
_line=1;
_open=0;
std::auto_ptr<Node> node(read(is, _template));
if (node->children()==0)
throw tag_expected(_template[0], _template[0].name());
return (*node)[0].clone();
16 years ago
} catch (exception& e) {
e.line(_line);
throw;
}
bool Factory::ws(char c) throw() {
static char last(0);
if ((c=='\n'||c=='\r')&&last!='\n'&&last!='\r') ++_line;
last = c;
16 years ago
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,
16 years ago
character_after_slash, missing_end_tag, attribute_value_not_quoted,
access_error, duplicate_attribute, attributes_in_end_tag,
illegal_attribute, mandatory_attribute_missing,
wrong_node_number) {
16 years ago
std::auto_ptr<Node> result(node.clone());
result->clear();
while (true) {
16 years ago
Tag res(tag(is, node));
if (!res.found) return result;
16 years ago
if (res.text.size()) result->text(res.text);
switch (res.type) {
case END: {
--_open;
16 years ago
if (res.attributes.size()) throw attributes_in_end_tag(node, is, res);
if (res.name!=node.name()) throw wrong_end_tag(node, is, res);
} return result;
16 years ago
case EMPTY: {
std::auto_ptr<Node> current(node[res.name].clone());
current->clear()<<res.attributes;
*result<<*checkChildren(node[res.name], current, is);
16 years ago
} break;
case START: {
++_open;
*result<<(*checkChildren(node[res.name], read(is, node[res.name]), is)
<<res.attributes);
16 years ago
} break;
case SPECIAL: break; //! @todo ignored could be stored
16 years ago
}
}
}
std::auto_ptr<Node> Factory::checkChildren(const xml::Node& tpl,
std::auto_ptr<Node> node,
std::istream& is) const
throw(wrong_node_number) {
for (Node::size_type i(0); i<tpl.children(); ++i)
if (tpl[i].min()>0 && node->list(tpl[i].name()).size()<tpl[i].min()
|| 0<tpl[i].max() && tpl[i].max()<node->list(tpl[i].name()).size())
throw wrong_node_number(*node, is, tpl[i].name(),
node->list(tpl[i].name()).size(),
tpl[i].min(), tpl[i].max());
return node;
}
16 years ago
Tag Factory::tag(std::istream& is, const Node& position)
throw(second_slash_in_tag, character_after_slash, tag_expected,
16 years ago
missing_end_tag, attribute_value_not_quoted,
access_error, duplicate_attribute, attributes_in_end_tag,
illegal_attribute, mandatory_attribute_missing,
wrong_start_tag) {
16 years ago
char c(0);
Tag tag((Tag){"", START, "", Attributes(), "", false});
16 years ago
while (is && is.get(c) && ws(c)); // skip ws
if (is.eof()) {
if (_open>0)
throw missing_end_tag(position, is, tag);
else
return tag;
}
tag.found = true;
16 years ago
if (c!='<') do tag.text+=c; while (is && is.get(c) && c!='<');
bool nameRead(false);
16 years ago
for (char last(c); is && is.get(c) && c!='>'; last=c) switch (c) {
case '\n': case '\r':
if (last!='\n'&&last!='\r') ++_line;
case ' ': case '\t':
16 years ago
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
16 years ago
tag.special+=last;
do tag.special+=c; while (c!='>' && is && is.get(c));
16 years ago
tag.type=SPECIAL;
return tag;
}
16 years ago
default:
if (tag.type==EMPTY) throw character_after_slash(position, is, tag, c);
if (nameRead) { // read attribute
if (tag.type!=START) throw attributes_in_end_tag(position, is, tag);
16 years ago
std::string attrname(1, c), attrvalue;
while (is && is.get(c) && c!='=' && c!='>' && c!='/'
&& !ws(c)) attrname+=c;
while (ws(c) && is && is.get(c)); // skip ws, search '='
16 years ago
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
16 years ago
if (tag.attributes.find(attrname)!=tag.attributes.end())
throw duplicate_attribute(position, is, tag, attrname);
if (!position(tag.name)) {
if (tag.name.size())
throw wrong_start_tag(position, is, tag);
else
throw tag_expected(position, tag.text);
}
if (position[tag.name].attributes().find(attrname)
==position[tag.name].attributes().end())
throw illegal_attribute(position, is, tag, attrname);
16 years ago
tag.attributes[attrname] = attrvalue;
} else { // part of a tag name
tag.name+=c;
}
}
if (tag.type==START||tag.type==EMPTY) {
if (!position(tag.name)) {
if (tag.name.size())
throw wrong_start_tag(position, is, tag);
else
throw tag_expected(position, tag.text);
}
const xml::Node& node(position[tag.name]);
for (Attributes::const_iterator it(node.attributes().begin());
it!=node.attributes().end(); ++it)
if (tag.attributes.find(it->first)==tag.attributes.end()) {
if (it->second=="xml::mandatory")
throw mandatory_attribute_missing(position, is, tag, it->first);
else if (it->second!="xml::optional")
tag.attributes.insert(*it);
}
}
16 years ago
return tag;
}
}