/*! @file @id $Id$ */ // 1 2 3 4 5 6 7 8 // 45678901234567890123456789012345678901234567890123456789012345678901234567890 #include #include #include #include #include class MethodTrace { public: MethodTrace(const void* addr, const std::string& name) throw(): _addr(addr), _name(name) { std::clog<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<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<31?_char:'?') <<" (ascii="<<(int)_char<<")"; ss<<"\ntag type: "<<(_tag->type==START?"START" :(_tag->type==END?"END" :(_tag->type==EMPTY?"EMPTY" :"???? "))); 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(); } //============================================================================ //---------------------------------------------------------------------------- //! 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()) { } //! Construct an attribute with name an value. Attributes::Value::Value(const std::string& name, const std::string& value) throw(): Attributes::value_type(name, value) { } //! 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; } //! Get the attribute value. const std::string& Attributes::Value::value() const throw() { return second; } //! Get the attribute value. std::string& Attributes::Value::value() throw() { return second; } //! 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"); } //! Convert the attribute to a number. Attributes::Value::operator unsigned long() const throw() { std::stringstream ss(second); int i(0); ss>>i; return i; } //! Convert the attribute to a space separated list. Attributes::Value::operator List() const throw() { return toList(); } //! 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; } //---------------------------------------------------------------------------- //! Empty attribute list Attributes::Attributes() throw() {} //! Attribute list with first one empty attribute given. Attributes::Attributes(const std::string& empty) throw() { insert(Value(empty)); } //! Attribute list with first attribute given. Attributes::Attributes(const std::string& key, const std::string& value) throw() { insert(Value(key, value)); } //! Add a new key-value pair to the attribute list. Attributes& Attributes::operator<<(const Attributes::Value& v) throw() { insert(v); return *this; } //! Add a new empty key to the attribute list. Attributes& Attributes::operator<<(const std::string& key) throw() { insert(Value(key)); return *this; } //---------------------------------------------------------------------------- //! Nodes must be given a name. /*! Unnamed nodes are not allowed, therefore there's no default constructor. */ 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(); } //! Clones But clears the parent. std::auto_ptr Node::clone() const throw() { std::auto_ptr 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<first<<"=\""<second<<'"'; o<<'>'<out(o, level+1)<'; } else { o<first<<"=\""<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]; } 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); } 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::clone(Node* p) const throw() { std::auto_ptr c(clone()); c->_parent = p; return c; } //---------------------------------------------------------------------------- String::String(std::string name, const std::string& text) throw(): Node(name), _text(text) { } std::auto_ptr String::clone() const throw() { return std::auto_ptr(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<first<<"=\""<second<<'"'; o<<'>'<<_text<<"'; } else { o<first<<"=\""<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()< UnsignedInteger::clone() const throw() { return std::auto_ptr(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")); 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>i; std::stringstream ss; ss<>i; return i; } //---------------------------------------------------------------------------- Factory::Factory(const Node& t) throw(): _template(t.clone()), _line(0) {} const Node& Factory::operator*() const throw() { return *_template; } std::auto_ptr 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, duplicate_attribute, attributes_in_end_tag) try { _line=1; std::auto_ptr node(_template->clone()); node->clear(); Tag res; while (true) { res = tag(is, *node); *node<name()!=res.name) throw wrong_start_tag(*node, is, res); return read(is, *_template); //! @todo store instead of ignore case SPECIAL: break; } } } 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; return c==' '||c=='\t'||c=='\n'||c=='\r'; } std::auto_ptr 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 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()<