/*! @file @id $Id$ */ // 1 2 3 4 5 6 7 8 // 45678901234567890123456789012345678901234567890123456789012345678901234567890 #ifndef XML_HXX #define XML_HXX #include #include #include #include #include /*! @page limitations Known Limitations - Templates cannot specify number of sub-elements. - XML-Comments are not supported. - Mixed tags and text is not supported. Tags may either contain other tags only (type xml::Node) or text only (type xml::String). - Unlimited recursion is not possible (e.g. <p><p><p></p></p></p>) - No check yet for optional and mandatory attributes in xml::Factory - Exceptions should be optional, best effort otherwise (option "strict") */ namespace xml { //============================================================================ class Attributes: public std::map { public: class Value: public value_type { public: Value(const std::string& name) throw(); value_type& operator=(const std::string& value) throw(); private: Value(); // not implemented }; Attributes() throw(); Attributes(const std::string& empty) throw(); Attributes& operator<<(const value_type& v) throw(); Attributes& operator<<(const std::string& key) throw(); Attributes& operator=(const std::string& value) throw(); private: iterator _active; }; typedef Attributes::Value Attr; enum NodeType {START, END, EMPTY}; struct Tag { std::string name; NodeType type; std::string text; Attributes attributes; }; const bool mandatory = true; const bool optional = false; //================================================================= EXCEPTIONS class Node; class Factory; //---------------------------------------------------------------------------- class exception: public std::exception { public: exception(std::string reason, const Node& t) throw(); ~exception() throw(); const char* what() const throw(); private: std::string _what; Node* _node; }; //---------------------------------------------------------------------------- class no_parent: public exception { public: no_parent(const Node& t) throw(): exception("node has no parent", t) {} }; //---------------------------------------------------------------------------- class tag_expected: public exception { public: tag_expected(const Node& t, const std::string& txt) throw(): exception("tag ('<') expected, text not allowed\ntext: "+txt, t) {} }; //---------------------------------------------------------------------------- class access_error: public exception { public: access_error(const Node& t, const std::string& name) throw(); ~access_error() throw() {} const char* what() const throw(); private: std::string _name; }; //---------------------------------------------------------------------------- class out_of_range: public exception { public: out_of_range(const Node& t, size_t pos) throw(); ~out_of_range() throw() {} const char* what() const throw(); private: size_t _pos; }; //---------------------------------------------------------------------------- class cannot_have_children: public exception { public: cannot_have_children(const Node& parent, const Node& child) throw(); ~cannot_have_children() throw(); const char* what() const throw(); private: Node* _child; }; //---------------------------------------------------------------------------- class stream_error: public exception { public: stream_error(const std::string& reason, const Node& t, std::istream& is, Tag tag, char c=0) throw(); ~stream_error() throw() {} const char* what() const throw(); private: std::istream::streampos _pos; Tag _tag; char _char; }; //---------------------------------------------------------------------------- class wrong_end_tag: public stream_error { public: wrong_end_tag(const Node& t, std::istream& is, Tag tag, char c=0) throw(): stream_error("mismatching end tag", t, is, tag, c) { } }; class missing_end_tag: public stream_error { public: missing_end_tag(const Node& t, std::istream& is, Tag tag, char c=0) throw(): stream_error("missing end tag, end of file reached", t, is, tag, c) { } }; class empty_stream: public stream_error { public: empty_stream(const Node& t, std::istream& is, Tag tag, char c=0) throw(): stream_error("no xml tag found, stream is empty", t, is, tag, c) { } }; class wrong_start_tag: public stream_error { public: wrong_start_tag(const Node& t, std::istream& is, Tag tag, char c=0) throw(): stream_error("start tag does not match expected tag", t, is, tag, c) { } }; class second_slash_in_tag: public stream_error { public: second_slash_in_tag(const Node& t, std::istream& is, Tag tag, char c=0) throw(): stream_error("a tag may have no more than one slash", t, is, tag, c) { } }; class character_after_slash: public stream_error { public: character_after_slash(const Node& t, std::istream& is, Tag tag, char c=0) throw(): stream_error("unexpected character after empty-slash", t, is, tag, c) { } }; class attributes_in_end_tag: public stream_error { public: attributes_in_end_tag(const Node& t, std::istream& is, Tag tag) throw(): stream_error("attributes are not allowed in end tags",t, is, tag) { } }; class attribute_value_not_quoted: public stream_error { public: attribute_value_not_quoted(const Node& t, std::istream& is, Tag tag, char c, std::string attr) throw(): stream_error("attribute values must be quoted (\")\nattribute: "+attr, t, is, tag, c) { } }; class duplicate_attribute: public stream_error { public: duplicate_attribute(const Node& t, std::istream& is, Tag tag, std::string attr) throw(): stream_error("attribute duplicated\nattribute: "+attr, t, is, tag, 0) { } }; //============================================================================ //---------------------------------------------------------------------------- class Node { private: typedef std::vector Contents; public: typedef Contents::size_type size_type; Node(std::string name) throw(); Node(const Node& o) throw(); Node& operator=(const Node& o) throw(); virtual ~Node() throw(); virtual std::auto_ptr clone() const throw(); virtual std::ostream& out(std::ostream& o, unsigned int level=0) const throw(); virtual std::string text() const throw(); virtual Node& text(const std::string& txt) throw(tag_expected); virtual Node& append(const Node& o) throw(cannot_have_children); virtual Node& set(const Attributes& o) throw(); Node& clear() throw (); std::string name() const throw(); bool isChild() const throw(); Node& parent() const throw(no_parent); bool hasAttr(const std::string& name) const throw(); Node& attr(const std::string& name, bool mandatory) throw(); std::string attr(const std::string& name) const throw(); std::string& attr(const std::string& name) throw(); bool operator()(const std::string& child) const throw(); Node& operator<<(const Node& o) throw(cannot_have_children); Node& operator<<(const Attributes& o) throw(); //! Get the number of children. size_type children() const throw(); //! Get the n-th child. const Node& operator[](size_type child) const throw(access_error); //! Get the n-th child. Node& operator[](size_type child) throw(access_error); //! Get the first named child. const Node& operator[](const std::string& child) const throw(access_error); //! Get the first named child. Node& operator[](const std::string& child) throw(access_error); std::string operator*() const throw(); Node& operator=(const std::string& contents) throw(tag_expected); friend std::ostream& operator<<(std::ostream& o, const Node& t) throw(); protected: Attributes _attributes; private: Node* find(const std::string& child) const throw(); virtual std::auto_ptr clone(Node* p) const throw(); Node(); // not implemented Contents _contents; std::string _name; Node* _parent; }; //---------------------------------------------------------------------------- class String: public Node { public: String(std::string name, const std::string& text = std::string()) throw(); virtual std::auto_ptr clone() const throw(); virtual ~String() throw() {} virtual std::string text() const throw(); virtual String& text(const std::string& txt) throw(tag_expected); virtual std::ostream& out(std::ostream& o, unsigned int level=0) const throw(); virtual String& append(const Node& o) throw(cannot_have_children); Node& operator=(const std::string& contents) throw(); private: std::string _text; }; //---------------------------------------------------------------------------- class List: public Node { }; class Factory { public: Factory(const Node& t) throw(); const Node& operator*() const throw(); std::auto_ptr read(std::istream& is) throw(wrong_end_tag, wrong_start_tag, tag_expected, second_slash_in_tag, character_after_slash, missing_end_tag, attribute_value_not_quoted, access_error, empty_stream, duplicate_attribute, attributes_in_end_tag); private: friend class stream_error; Factory(); // not implemented Factory(const Factory&); // not implemented bool ws(char c) const throw(); std::auto_ptr read(std::istream& is, const Node& position) throw(wrong_end_tag, wrong_start_tag, tag_expected, second_slash_in_tag, character_after_slash, missing_end_tag, attribute_value_not_quoted, access_error, duplicate_attribute, attributes_in_end_tag); Tag 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); std::auto_ptr _template; }; } #endif