|
|
|
@ -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") */ |
|
|
|
|
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 <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(); |
|
|
|
|