some documentation and new feartures for attributes
This commit is contained in:
		| @@ -17,7 +17,7 @@ | ||||
| /*! @page limitations Known Limitations | ||||
|  | ||||
|     - Templates cannot specify number of sub-elements. | ||||
|     - XML-Comments are not supported. | ||||
|     - XML-Comments are only ignored. | ||||
|     - Mixed tags and text is not supported. Tags may either contain | ||||
|       other tags only (type xml::Node) or text only (type | ||||
|       xml::String). | ||||
| @@ -25,45 +25,78 @@ | ||||
|       (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") */ | ||||
|  | ||||
| //! Represents classes for handling C++ access to XML files with strict schema. | ||||
| /*! The schema ist not presented through xsd, but it can be declared in C++. | ||||
|  | ||||
|     A xml::Factory represents a factory that owns a template and can | ||||
|     instanciate XML trees that are valid for the given template from | ||||
|     streams. If anything is not valid, an exception is thrown. The @c | ||||
|     what() method of the exception gives additional information about | ||||
|     the problem. | ||||
|  | ||||
|     In the following example, we want to represent XML data that are | ||||
|     contained in a <persons> tag, and may contain a list of @c | ||||
|     person. Each @c person has a mandatory attribute @c id and | ||||
|     optional @c member-of. @c person has a @c name and may contain a | ||||
|     list of @c friends, where each @c friend has an attribute @c | ||||
|     id. (The @c id attribute of course should reference to the @c id | ||||
|     of another @c name, but this relationship cannot be declared.) | ||||
|  | ||||
|     All tags are by default specified as 0..n (optional and any number | ||||
|     there of). | ||||
|  | ||||
|     <code> | ||||
|     #include <xml-cxx/xml.hxx> | ||||
|     #include <iostream> | ||||
|     #include <<stream> | ||||
|     [...] | ||||
|     xml::Factory test(xml::Node("persons")             // root node | ||||
|                       <<(xml::Node("person")           // child of persons | ||||
|                          .attr("id", xml::mandatory) | ||||
|                          .attr("member-of", xml::optional)) | ||||
|                          <<xml::String("name")         // the person's name | ||||
|                          <<(xml::Node("friends")       // friends of person | ||||
|                             <<(xml::Node("friend")     // a friend | ||||
|                                .attr("id", xml::mandatory))))); | ||||
|     [...] | ||||
|     try { | ||||
|       std::auto_ptr<xml::Node> persons(test.read(std::ifstream("file.xml))); | ||||
|       // Here we can be sure, that our structure is valid, | ||||
|       // but we must check optional elements before access, otherwise | ||||
|       // we get an exception. | ||||
|       [...] | ||||
|       for (xml::Node::size_type i(0); i<persons.children(); ++i) { | ||||
|         std::cout<<"Person: "<<*persons[i]["name"]; // exception if no "name" | ||||
|         if (persons[i]("friends")) // check if "friends" is set | ||||
|           std::cout<<" has "<<persons[i]["friends"].children()<<" friends" | ||||
|         else | ||||
|           std::cout<<" has no friend list"; | ||||
|         std::cout<<std::endl; | ||||
|       } | ||||
|       [...] | ||||
|     } catch (const std::exception& x) { | ||||
|       std::cerr<<"**** Error in file \"file.xml\":"<<std::endl | ||||
|                <<x.what()<<std::endl; | ||||
|     } | ||||
|     </code> */ | ||||
| namespace xml { | ||||
|    | ||||
|   //============================================================================ | ||||
|     class  Attributes: public std::map<std::string, std::string> { | ||||
|     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, SPECIAL}; | ||||
|   struct Tag { | ||||
|       std::string name; | ||||
|       NodeType type; | ||||
|       std::string text; | ||||
|       Attributes attributes; | ||||
|       std::string special; | ||||
|   }; | ||||
|   const bool mandatory = true; | ||||
|   const bool optional = false; | ||||
|  | ||||
|   //================================================================= EXCEPTIONS | ||||
|   struct Tag; | ||||
|   class Attributes; | ||||
|   class Node; | ||||
|   class Factory; | ||||
|  | ||||
|   //---------------------------------------------------------------------------- | ||||
|   class exception: public std::exception { | ||||
|     public: | ||||
|       exception(std::string reason) throw(); | ||||
|       exception(std::string reason, const Node& t) throw(); | ||||
|       ~exception() throw(); | ||||
|       const char* what() const throw(); | ||||
| @@ -121,53 +154,55 @@ namespace xml { | ||||
|   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() {} | ||||
|                    std::istream& is, const Tag& tag, char c=0) throw(); | ||||
|       ~stream_error() throw(); | ||||
|       const char* what() const throw(); | ||||
|     private: | ||||
|       std::istream::streampos _pos; | ||||
|       Tag _tag; | ||||
|       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) | ||||
|       wrong_end_tag(const Node& t, std::istream& is, const 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) | ||||
|       missing_end_tag(const Node& t, std::istream& is, const 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) | ||||
|       empty_stream(const Node& t, std::istream& is, const 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) | ||||
|       wrong_start_tag(const Node& t, std::istream& is, const 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) | ||||
|       second_slash_in_tag(const Node& t, std::istream& is, const 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) | ||||
|       character_after_slash(const Node& t, std::istream& is, const Tag& tag, | ||||
|                             char c=0) | ||||
|           throw(): | ||||
|           stream_error("unexpected character after empty-slash", | ||||
|                        t, is, tag, c) { | ||||
| @@ -175,14 +210,15 @@ namespace xml { | ||||
|   }; | ||||
|   class attributes_in_end_tag: public stream_error { | ||||
|     public: | ||||
|       attributes_in_end_tag(const Node& t, std::istream& is, Tag tag) | ||||
|       attributes_in_end_tag(const Node& t, std::istream& is, const 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, | ||||
|       attribute_value_not_quoted(const Node& t, std::istream& is, | ||||
|                                  const Tag& tag, | ||||
|                                  char c, std::string attr) throw(): | ||||
|           stream_error("attribute values must be quoted (\")\nattribute: "+attr, | ||||
|                        t, is, tag, c) { | ||||
| @@ -190,7 +226,7 @@ namespace xml { | ||||
|   }; | ||||
|   class duplicate_attribute: public stream_error { | ||||
|     public: | ||||
|       duplicate_attribute(const Node& t, std::istream& is, Tag tag, | ||||
|       duplicate_attribute(const Node& t, std::istream& is, const Tag& tag, | ||||
|                           std::string attr) throw(): | ||||
|           stream_error("attribute duplicated\nattribute: "+attr, | ||||
|                        t, is, tag, 0) { | ||||
| @@ -199,6 +235,66 @@ namespace xml { | ||||
|  | ||||
|   //============================================================================ | ||||
|  | ||||
|   //---------------------------------------------------------------------------- | ||||
|   //! Map for attribute values. | ||||
|   /*! Attributes can be set using method xml::Node::attr(). Check for | ||||
|       an attribute with xml::Node::hasAttr(). Attributes must be | ||||
|       unique, which means that every attribute must be set at maximum | ||||
|       once. This is corect: <code><node | ||||
|       attribute="value"></code>, this is not allowed: | ||||
|       <code><node attribute="value" attribute="value"></code> */ | ||||
|   class  Attributes: public std::map<std::string, std::string> { | ||||
|     public: | ||||
|       //! Attributes may contain a list of space separated values. | ||||
|       typedef std::vector<std::string> List; | ||||
|       //! Attribute values ar mainly a std::pair. | ||||
|       /*! In addition to a normal std::pair, attributes offer an | ||||
|           assignment operator to set the value, and can be constructed | ||||
|           as empty attribute, given only a key. */ | ||||
|       class Value: public value_type { | ||||
|         public: | ||||
|           //! Construct an empty attribute. | ||||
|           Value(const std::string& name) throw(); | ||||
|           //! Construct an attribute with name an value. | ||||
|           Value(const std::string& name, const std::string& namevalue) throw(); | ||||
|           //! Assign a value. | ||||
|           value_type& operator=(const std::string& value) throw(); | ||||
|           //! Get the attribute name. | ||||
|           const std::string& name() const throw(); | ||||
|           //! Get the attribute value. | ||||
|           const std::string& value() const throw(); | ||||
|           //! Get the attribute value. | ||||
|           std::string& value() throw(); | ||||
|           //! Convert the attribute to a boolean. | ||||
|           operator bool() const throw(); | ||||
|           //! Convert the attribute to a number. | ||||
|           operator unsigned long() const throw(); | ||||
|           //! Convert the attribute to a space separated list. | ||||
|           operator List() const throw(); | ||||
|           List toList(const std::string& separators=" \t\n\r") const throw(); | ||||
|         private: | ||||
|           Value(); // not implemented, key must always be given | ||||
|       }; | ||||
|       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; | ||||
|  | ||||
|  | ||||
|   //---------------------------------------------------------------------------- | ||||
|   struct Tag { | ||||
|       std::string name; | ||||
|       NodeType type; | ||||
|       std::string text; | ||||
|       Attributes attributes; | ||||
|       std::string special; | ||||
|   }; | ||||
|  | ||||
|   //---------------------------------------------------------------------------- | ||||
|   class Node { | ||||
|     private: | ||||
| @@ -284,9 +380,6 @@ namespace xml { | ||||
|   }; | ||||
|  | ||||
|   //---------------------------------------------------------------------------- | ||||
|   class List: public Node { | ||||
|   }; | ||||
|  | ||||
|   class Factory { | ||||
|     public: | ||||
|       Factory(const Node& t) throw(); | ||||
|   | ||||
							
								
								
									
										112
									
								
								src/xml.cxx
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								src/xml.cxx
									
									
									
									
									
								
							| @@ -37,37 +37,12 @@ unsigned int MethodTrace::_level(0); | ||||
|  | ||||
| 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) throw(): | ||||
|       _what(reason), _node(0) { | ||||
|   } | ||||
|   exception::exception(std::string reason, const Node& t) throw(): | ||||
|       _what(reason), _node(t.clone().release()) { | ||||
|   } | ||||
| @@ -122,8 +97,11 @@ namespace xml { | ||||
|   } | ||||
|   //---------------------------------------------------------------------------- | ||||
|   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) { | ||||
|                              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; | ||||
| @@ -134,12 +112,12 @@ namespace xml { | ||||
|       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" | ||||
|       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; | ||||
|       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(); | ||||
| @@ -147,6 +125,70 @@ namespace xml { | ||||
|  | ||||
|   //============================================================================ | ||||
|  | ||||
|   //---------------------------------------------------------------------------- | ||||
|   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) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user