some documentation and new feartures for attributes
This commit is contained in:
@@ -17,7 +17,7 @@
|
|||||||
/*! @page limitations Known Limitations
|
/*! @page limitations Known Limitations
|
||||||
|
|
||||||
- Templates cannot specify number of sub-elements.
|
- 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
|
- Mixed tags and text is not supported. Tags may either contain
|
||||||
other tags only (type xml::Node) or text only (type
|
other tags only (type xml::Node) or text only (type
|
||||||
xml::String).
|
xml::String).
|
||||||
@@ -25,45 +25,78 @@
|
|||||||
(e.g. <p><p><p></p></p></p>)
|
(e.g. <p><p><p></p></p></p>)
|
||||||
- No check yet for optional and mandatory attributes in xml::Factory
|
- No check yet for optional and mandatory attributes in xml::Factory
|
||||||
- Exceptions should be optional, best effort otherwise (option "strict") */
|
- 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};
|
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 mandatory = true;
|
||||||
const bool optional = false;
|
const bool optional = false;
|
||||||
|
|
||||||
//================================================================= EXCEPTIONS
|
//================================================================= EXCEPTIONS
|
||||||
|
struct Tag;
|
||||||
|
class Attributes;
|
||||||
class Node;
|
class Node;
|
||||||
class Factory;
|
class Factory;
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
class exception: public std::exception {
|
class exception: public std::exception {
|
||||||
public:
|
public:
|
||||||
|
exception(std::string reason) throw();
|
||||||
exception(std::string reason, const Node& t) throw();
|
exception(std::string reason, const Node& t) throw();
|
||||||
~exception() throw();
|
~exception() throw();
|
||||||
const char* what() const throw();
|
const char* what() const throw();
|
||||||
@@ -121,53 +154,55 @@ namespace xml {
|
|||||||
class stream_error: public exception {
|
class stream_error: public exception {
|
||||||
public:
|
public:
|
||||||
stream_error(const std::string& reason, const Node& t,
|
stream_error(const std::string& reason, const Node& t,
|
||||||
std::istream& is, Tag tag, char c=0) throw();
|
std::istream& is, const Tag& tag, char c=0) throw();
|
||||||
~stream_error() throw() {}
|
~stream_error() throw();
|
||||||
const char* what() const throw();
|
const char* what() const throw();
|
||||||
private:
|
private:
|
||||||
std::istream::streampos _pos;
|
std::istream::streampos _pos;
|
||||||
Tag _tag;
|
Tag* _tag;
|
||||||
char _char;
|
char _char;
|
||||||
};
|
};
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
class wrong_end_tag: public stream_error {
|
class wrong_end_tag: public stream_error {
|
||||||
public:
|
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():
|
throw():
|
||||||
stream_error("mismatching end tag", t, is, tag, c) {
|
stream_error("mismatching end tag", t, is, tag, c) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
class missing_end_tag: public stream_error {
|
class missing_end_tag: public stream_error {
|
||||||
public:
|
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():
|
throw():
|
||||||
stream_error("missing end tag, end of file reached", t, is, tag, c) {
|
stream_error("missing end tag, end of file reached", t, is, tag, c) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
class empty_stream: public stream_error {
|
class empty_stream: public stream_error {
|
||||||
public:
|
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():
|
throw():
|
||||||
stream_error("no xml tag found, stream is empty", t, is, tag, c) {
|
stream_error("no xml tag found, stream is empty", t, is, tag, c) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
class wrong_start_tag: public stream_error {
|
class wrong_start_tag: public stream_error {
|
||||||
public:
|
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():
|
throw():
|
||||||
stream_error("start tag does not match expected tag", t, is, tag, c) {
|
stream_error("start tag does not match expected tag", t, is, tag, c) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
class second_slash_in_tag: public stream_error {
|
class second_slash_in_tag: public stream_error {
|
||||||
public:
|
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():
|
throw():
|
||||||
stream_error("a tag may have no more than one slash", t, is, tag, c) {
|
stream_error("a tag may have no more than one slash", t, is, tag, c) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
class character_after_slash: public stream_error {
|
class character_after_slash: public stream_error {
|
||||||
public:
|
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():
|
throw():
|
||||||
stream_error("unexpected character after empty-slash",
|
stream_error("unexpected character after empty-slash",
|
||||||
t, is, tag, c) {
|
t, is, tag, c) {
|
||||||
@@ -175,14 +210,15 @@ namespace xml {
|
|||||||
};
|
};
|
||||||
class attributes_in_end_tag: public stream_error {
|
class attributes_in_end_tag: public stream_error {
|
||||||
public:
|
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():
|
throw():
|
||||||
stream_error("attributes are not allowed in end tags",t, is, tag) {
|
stream_error("attributes are not allowed in end tags",t, is, tag) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
class attribute_value_not_quoted: public stream_error {
|
class attribute_value_not_quoted: public stream_error {
|
||||||
public:
|
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():
|
char c, std::string attr) throw():
|
||||||
stream_error("attribute values must be quoted (\")\nattribute: "+attr,
|
stream_error("attribute values must be quoted (\")\nattribute: "+attr,
|
||||||
t, is, tag, c) {
|
t, is, tag, c) {
|
||||||
@@ -190,7 +226,7 @@ namespace xml {
|
|||||||
};
|
};
|
||||||
class duplicate_attribute: public stream_error {
|
class duplicate_attribute: public stream_error {
|
||||||
public:
|
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():
|
std::string attr) throw():
|
||||||
stream_error("attribute duplicated\nattribute: "+attr,
|
stream_error("attribute duplicated\nattribute: "+attr,
|
||||||
t, is, tag, 0) {
|
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 {
|
class Node {
|
||||||
private:
|
private:
|
||||||
@@ -284,9 +380,6 @@ namespace xml {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
class List: public Node {
|
|
||||||
};
|
|
||||||
|
|
||||||
class Factory {
|
class Factory {
|
||||||
public:
|
public:
|
||||||
Factory(const Node& t) throw();
|
Factory(const Node& t) throw();
|
||||||
|
112
src/xml.cxx
112
src/xml.cxx
@@ -37,37 +37,12 @@ unsigned int MethodTrace::_level(0);
|
|||||||
|
|
||||||
namespace xml {
|
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
|
//================================================================= EXCEPTIONS
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
exception::exception(std::string reason) throw():
|
||||||
|
_what(reason), _node(0) {
|
||||||
|
}
|
||||||
exception::exception(std::string reason, const Node& t) throw():
|
exception::exception(std::string reason, const Node& t) throw():
|
||||||
_what(reason), _node(t.clone().release()) {
|
_what(reason), _node(t.clone().release()) {
|
||||||
}
|
}
|
||||||
@@ -122,8 +97,11 @@ namespace xml {
|
|||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
stream_error::stream_error(const std::string& reason, const Node& t,
|
stream_error::stream_error(const std::string& reason, const Node& t,
|
||||||
std::istream& is, Tag tag, char c) throw():
|
std::istream& is, const Tag& tag, char c) throw():
|
||||||
exception(reason, t), _pos(is.tellg()), _tag(tag), _char(c) {
|
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() {
|
const char* stream_error::what() const throw() {
|
||||||
static std::string w;
|
static std::string w;
|
||||||
@@ -134,12 +112,12 @@ namespace xml {
|
|||||||
if (_char)
|
if (_char)
|
||||||
ss<<"\nactual character: "<<(_char>31?_char:'?')
|
ss<<"\nactual character: "<<(_char>31?_char:'?')
|
||||||
<<" (ascii="<<(int)_char<<")";
|
<<" (ascii="<<(int)_char<<")";
|
||||||
ss<<"\ntag type: "<<(_tag.type==START?"START"
|
ss<<"\ntag type: "<<(_tag->type==START?"START"
|
||||||
:(_tag.type==END?"END"
|
:(_tag->type==END?"END"
|
||||||
:(_tag.type==EMPTY?"EMPTY"
|
:(_tag->type==EMPTY?"EMPTY"
|
||||||
:"???? <UNKNOWN>")));
|
:"???? <UNKNOWN>")));
|
||||||
if (_tag.name.size()) ss<<"\nnode name read: "<<_tag.name;
|
if (_tag->name.size()) ss<<"\nnode name read: "<<_tag->name;
|
||||||
if (_tag.text.size()) ss<<"\ncontained text: "<<_tag.text;
|
if (_tag->text.size()) ss<<"\ncontained text: "<<_tag->text;
|
||||||
w = ss.str();
|
w = ss.str();
|
||||||
}
|
}
|
||||||
return w.c_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():
|
Node::Node(std::string name) throw():
|
||||||
_name(name), _parent(0) {
|
_name(name), _parent(0) {
|
||||||
|
Reference in New Issue
Block a user