some documentation and new feartures for attributes

This commit is contained in:
Marc Wäckerlin
2009-04-06 07:08:52 +00:00
parent 2f533e99b8
commit 38e1b60ef1
2 changed files with 212 additions and 77 deletions

View File

@@ -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. &ltp>&ltp>&ltp>&lt/p>&lt/p>&lt/p>)
- No check yet for optional and mandatory attributes in xml::Factory
- Exceptions should be optional, best effort otherwise (option "strict") */
namespace xml {
//! 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 &lt;xml-cxx/xml.hxx&gt;
#include &lt;iostream&gt;
#include <&lt;stream&gt;
[...]
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>&lt;node
attribute="value"&gt;</code>, this is not allowed:
<code>&lt;node attribute="value" attribute="value"&gt;</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();