start of xml::Optional

This commit is contained in:
Marc Wäckerlin
2009-05-04 12:47:57 +00:00
parent 13c7266c9f
commit 6d7836fefd
15 changed files with 1409 additions and 442 deletions

View File

@@ -12,7 +12,6 @@
#include <sstream>
#include <string>
#include <vector>
#include <list>
#include <set>
#include <map>
#include <memory>
@@ -20,6 +19,7 @@
#include <stdexcept>
#include <xml-cxx/any.hxx>
//! @cond DEBUG
#include <cassert>
#include <iostream>
#include <iomanip>
@@ -51,7 +51,7 @@ class MethodTrace {
} \
assert(Y); \
}
//! @endcond
/*! @mainpage
@@ -60,11 +60,90 @@ class MethodTrace {
- Specify your XML schema in C++ using common C++ syntax,
such as shift, dereference, etc.
- Verify the schema of XML files while they are read from a stream.
- Map and store your own C++ classes to XML and restore them back.
From the README file:
@section basics Basics
Include file:
@code
#include <xml-cxx/xml.hxx>
@endcode
Link option:
@code
-lxml-cxx
@endcode
@section schemaFactory Factory with Schema Declaration
Small example on how to declare an XML schema (@ref freexml), you
may then use <code>template.read(is)</code> to read XML from a
stream:
@code
// start with root element: <root id="">
xml::Factory template(xml::Node("root").attr("id", xml::optional)
// <root> contains any number of <child>
<<xml::String("child")
// must contain exactly one <other>
<<(xml::Node("other").limits(1, 1)
// <other> contains min 2 max 4 <text>
<<xml::String("text").limits(2, 4)));
@endcode
@section introMacro Using Macros Instead od Literal Text
If you prefere using constants instead of literal texts, you can
declare the node names before you use them (@ref xmlConst):
@code
XML_NODE(root);
XML_STRING(child);
[...]
@endcode
@code
xml::Factory template(xml::node::root.clone()->attr("id", xml::optional)
<<*xml::string::child.clone()
[...]
@endcode
@section introSer Serialize Classes, Join Classes with XML
When inheriting from xml::Serialize, your class inherits the
methods xml::Serialize::loadXml and
xml::Serialize::saveXml. Simply overwrite
xml::Serialize::initXmlMembers to make your class serializable
(@ref serialization):
@code
class MyClass: public xml::Serialize {
[...]
protected:
void initXmlMembers() {
className("MyClass");
persist(i, "i");
persist(s, "s");
persist(l, "l");
}
private:
int i;
std::string s;
xml::List<std::string> l; // same behaviour as std::list
};
@endcode
@section readme The README File
@include README
@page Known Limitations
@page license License is LGPL 3
File COPYING from http://www.gnu.org/licenses/lgpl-3.0.txt:
@include COPYING
@page limits Known Limitations
- XML-Comments are only ignored, not read, not stored.
- Mixed tags and text is not supported. Tags may either contain
@@ -74,6 +153,23 @@ class MethodTrace {
(e.g. &ltp&gt;&ltp&gt;&ltp&gt;&lt/p&gt;&lt/p&gt;&lt/p&gt;)
- Exceptions should be optional, best effort otherwise (option "strict")
@see serializationLimits
@page serializationLimits Limitations of Serialization
- Only the following types are intended to be serialized:\n
(It is possible to use other techniques, but that's not recommended)
- basic C++ types (except pointer)
- @c std::string
- classes derieved from xml::Serialize
- most standard containers, but in their xml-form,
e.g. xml::List instead of @c std::list
(xml::List inherits @c std::list)
- @c std::bitset, @c std::priority_queue, @c std::queue and
@c std::stack are not implemented
- Optional values are not yet implemented
- Polymorfic serialisation is not yet implemented
@page rationale Rationale - Limitations of other libraries
The initial idea was to map C++ data structures to XML files
@@ -98,7 +194,7 @@ class MethodTrace {
(http://gsoap.sf.net), boost serialization (http://boost.org) and
Qt XML (http://qtsoftware.com).
@section Qt XML, a typical DOM approach
@section qtxml Qt XML, a typical DOM approach
One is the XML part of the Qt library. These classes can read XML
into a DOM tree, but then the user has to check for every detail.
@@ -118,25 +214,83 @@ class MethodTrace {
...
@endcode
@example address.cxx */
This is a typical example of a DOM parser. The main disadvantage
here is that we cannot declare a schema. After parsing an XML
file, we cannot know whether it is valid with respect to our
definition or not. This means that every single access tested.
xml::Factory lets you specify a schema template and guarantees
that the parsed file passed a lot of tests to make sure it fits
into the schema. If any test fails, the factory throws an
exception.
@section boostserialization Boost Serialization
Boost serialization is quite flexible and easy to use, but there
are several pitfalls, and worst, the generated XML cannot easily
be edited by hand. One of the main problems: If you store lists,
you cannot simply add an arbitrary number of list items, but you
must first serialize the list size as a number. If you edit the
file by hand, the number must exactly match the number of items,
or parsing will fail. Error messages (the exceptions) don't help
finding the problem within the parsed XML code. The XML format it
generates is definitely not made to be edited by hand.
In fact, in my project that resulted in this new class (CoMoL, see
http://comol.sourceforge.net), we first used Boost serialization
to read and write XML files. The configuration was then done on
the GUI. But this was not comfortable enough, so the configuration
was mostly edited by hand. It was a pain then to find any typos in
the XML and the storage was too unflexible. So we needed a new
apporach, and well here it is. CoMoL now uses the full flexibility
of @ref freexml including optional tags and attributes.
@section gsoap Using gSOAP for Serialization of C++ Structures
When I was working at Siemens, we often used gSOAP
(http://gsoap.sf.net) when we needed mor flexibility in XML
declaration than what's possible with @ref boostserialization. But
gSOAP has several problems:
- It is a C framework, not native C++, with all the problems
that result from this, i.e. memory management is absolutely
awful.
- It is a quite a complex problem to copy a gSOAP structure
without memory access problems.
- Moreover gSOAP is not real C++ code, but it requires a pre
processor that generates C++ from a pseudo C++ structure.
- It is not designed to Store C++ in XML, but to implement the
gSOAP protocol.
- And last but not least, the license is not free for all usage.
@example address.cxx Example
This is a simple example on how to declare a XML schema and how to
use a xml::Factory to restore it from a file. */
//! @addtogroup freexml
//@{
/*! @defgroup xmlConst XML Constant Declarations
There are macros to help you with declaring constants. Chose a C++
header fiel, where you want to declare constant names for your xml
header file, where you want to declare constant names for your xml
nodes.
Then for every leaf xml::Node name you will use, call
XML_NODE(name) and for every xml::String call XML_STRING(name).
Then for xml::Node you will use, call XML_NODE(name) and for every
xml::String call XML_STRING(othername). After the declaration, you
can use the xml::Node as constant @c xml::node::name, the
xml::String as constant @c xml::string::name and @c std::string
constants for the given node names as @c xml::name::name and @c
xml::name::othername.
For every node with children call XML_PARENT(name, child1, child2 ...).
@note If you want to use the xml::Node, xml::String constants in a
non constant environment, i.e. to add children, attributes or
limits, you must call xml::Node::clone to get a non constant copy.
@note Node names must be unique. You can not even use the same
name for a XML_NODE and a XML_STRING declaration.
This code is from the examples:
@include node_macros.cxx
@see @ref node_macros.cxx
@example node_macros.cxx
@@ -168,12 +322,17 @@ class MethodTrace {
names are more often used than nodes. (@em Never use @c using in a
C++ header file, you would pollute the scope of all the
includers.) */
//@}
//! @addtogroup xmlConst
//@{
//! Define a string for a node name
/*! It is called inside XML_NODE and XML_STRING, so if you work with
these two, you don't have to care about XML_NAME. But you can use
XML_NAME alone if you don't want the other two macros.
Declares a constant of type @c std::string with name @c xml::name::NAME.
@see XML_NODE
@see XML_STRING */
#define XML_NAME(NAME) \
@@ -233,8 +392,14 @@ class MethodTrace {
//@}
//! 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++.
/*! @defgroup freexml Arbitrary XML Schema Definition and Storage
Class xml::Node declares an XML DOM node. Storing XML structures
has never been a problem, but to read them back again,
xml::Factory is needed, which must be given an XML schema
description. The XML schema is fully declared in C++, simply by
shifting the allowed nodes and attributes into the factory and by
setting limits.
A xml::Factory represents a factory that owns a template and can
instanciate XML trees that are valid for the given template from
@@ -286,8 +451,15 @@ class MethodTrace {
<<x.what()<<std::endl;
}
@endcode */
//! Everything is in namespace xml
namespace xml {
//! @addtogroup freexml
//@{
//! @cond INTERNAL
//============================================================================
//! Type of an xml node.
/*! Only start nodes and empty nodes may have attributes. */
@@ -300,6 +472,9 @@ namespace xml {
//! a xml start indication <code>&lt;?xml?&gt;</code>
//! or a document type declaration <code>&lt;!DOCTYPE ...&gt;</code>
};
//! @endcond
//! Declares an attribute to be mandatory.
const bool mandatory(true);
//! Declares an attribute to be optional.
@@ -311,6 +486,10 @@ namespace xml {
class Node;
class Factory;
//@}
//! @defgroup exceptions Exception classes
//@{
//----------------------------------------------------------------------------
class exception: public std::exception {
public:
@@ -330,6 +509,9 @@ namespace xml {
exception("serialized node type is not registered\ntype: "
+type+"\nname: "+name, t) {
}
type_not_registered(std::string type):
exception("serialized node type is not registered\ntype: "+type) {
}
};
//----------------------------------------------------------------------------
class empty_attribute_list: public exception {
@@ -421,6 +603,7 @@ namespace xml {
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, const Tag& tag, char c=0)
@@ -428,6 +611,7 @@ namespace xml {
stream_error("missing end tag, end of file reached", t, is, tag, c) {
}
};
//----------------------------------------------------------------------------
class wrong_start_tag: public stream_error {
public:
wrong_start_tag(const Node& t, std::istream& is, const Tag& tag, char c=0)
@@ -435,6 +619,7 @@ namespace xml {
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, const Tag& tag,
@@ -443,6 +628,7 @@ namespace xml {
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, const Tag& tag,
@@ -452,6 +638,7 @@ namespace xml {
t, is, tag, c) {
}
};
//----------------------------------------------------------------------------
class attributes_in_end_tag: public stream_error {
public:
attributes_in_end_tag(const Node& t, std::istream& is, const Tag& tag)
@@ -459,6 +646,7 @@ namespace xml {
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,
@@ -468,6 +656,7 @@ namespace xml {
t, is, tag, c) {
}
};
//----------------------------------------------------------------------------
class duplicate_attribute: public stream_error {
public:
duplicate_attribute(const Node& t, std::istream& is, const Tag& tag,
@@ -476,6 +665,7 @@ namespace xml {
t, is, tag, 0) {
}
};
//----------------------------------------------------------------------------
class illegal_attribute: public stream_error {
public:
illegal_attribute(const Node& t, std::istream& is, const Tag& tag,
@@ -484,6 +674,7 @@ namespace xml {
t, is, tag, 0) {
}
};
//----------------------------------------------------------------------------
class mandatory_attribute_missing: public stream_error {
public:
mandatory_attribute_missing(const Node& t, std::istream& is,
@@ -492,6 +683,7 @@ namespace xml {
t, is, tag, 0) {
}
};
//----------------------------------------------------------------------------
class wrong_node_number: public stream_error {
public:
wrong_node_number(const Node& t, std::istream& is,
@@ -510,9 +702,13 @@ namespace xml {
return ss.str();
}
};
//! @}
//============================================================================
//! @addtogroup freexml
//@{
//----------------------------------------------------------------------------
//! Map for attribute values.
/*! Attributes can be set using method xml::Node::attr(). Check for
@@ -570,7 +766,7 @@ namespace xml {
};
//----------------------------------------------------------------------------
//! An xml Node.
//! An xml Node that contains child nodes but no text.
/*! XML Nodes may contain either text or other nodes, but not both
at the same time. This node can hold other nodes. For a Node for
text contents, see xml::String. */
@@ -639,6 +835,7 @@ namespace xml {
};
//----------------------------------------------------------------------------
//! A leaf node that contains text but no child nodes.
class String: public Node {
public:
String(std::string name,
@@ -672,6 +869,7 @@ namespace xml {
};
//----------------------------------------------------------------------------
//! A leaf node that contains only numbers and no child nodes.
class UnsignedInteger: public String {
public:
UnsignedInteger(std::string name, unsigned long i=0,
@@ -685,6 +883,52 @@ namespace xml {
};
//----------------------------------------------------------------------------
//! Factory to restore XML structures from a stream.
/*! A xml::Factory must be given a template that declares the
structure, before the factory can be used. This can be done
either at instanciation or later by assignment.
The template is a xml::Node that specifies the schema of the data
that can be loaded from streams through a xml::Factory
instance.
The root element has automatically set the limits 1..1
(<code>xml::Node::limits(1, 1)</code>, see xml::Node::limits),
which means that the root element must exist exactly once. If
you pass another limit, your limit is overwritten and ignored.
E.g. to load an address, that contains a tag &lt;address&gt;
with at least a name and optional an address in it's body, you
may write:
@code
xml::Factory addrTpl(xml::Node("address")
(<<xml::Node("name").limit(1, 1)
(<<xml::String("first").limit(1, 1)
<<xml::String("middle") // 0..n -> .limit(0, 0)
<<xml::String("last").limit(1, 1))
<<(xml::Node("location").max(1)
<<xml::String("line").min(1))
<<xml::String("country").max(1)));
@endcode
According to this example, a valid XML file could be:
@verbatim
<address>
<name>
<first>Marc</first>
<middle>Roman</middle>
<last>Wäckerlin</last>
</name>
<location>
<line>SwissSign AG</line>
<line>Pfingstweidstrasse 60b</line>
<line>8005 Zürich</line>
</location>
<country>Schweiz</country>
</address>
@endverbatim */
class Factory {
public:
Factory(const Node& t) throw();
@@ -695,7 +939,8 @@ namespace xml {
const Node*const operator->() const throw(factory_not_valid);
operator bool() const throw();
friend std::ostream& operator<<(std::ostream& os,
const Factory& factory) throw();
const Factory& factory)
throw(factory_not_valid);
static std::ostream& print(std::ostream& os, const Node& node,
unsigned int level=0) throw();
std::auto_ptr<Node> read(std::istream& is)
@@ -704,12 +949,14 @@ namespace xml {
missing_end_tag, attribute_value_not_quoted, access_error,
duplicate_attribute, attributes_in_end_tag,
illegal_attribute, mandatory_attribute_missing,
wrong_node_number);
wrong_node_number, factory_not_valid);
void reset() throw();
private:
friend class stream_error;
friend class Serialize;
template<class T, class A> friend class List;
template<class T> friend class Container;
template<class T> friend class AssociativeContainer;
template<class T> friend class AssociativeMap;
Node& operator*() throw(factory_not_valid);
Node*const operator->() throw(factory_not_valid);
bool ws(char c) throw();
@@ -734,6 +981,7 @@ namespace xml {
unsigned long _line;
long _open;
};
//@}
/*! @defgroup serialization Class Serialization
@@ -763,74 +1011,87 @@ namespace xml {
@section serActual Actual Status
The following member types are supported
- All built-in C++ types are supported
- std::string is supported
- All built-in C++ types are supported, except enum
- @c std::string is supported
- Contained classes are supported
- Inheritance
- @ref serContainer
The following will be supported soon (ideas):
- lists, maps
- inheritance
@todo The following will be supported soon (ideas):
- choices (one of)
- choices (polymorfism)
- optional members (pointer)
- enum (class xml::Enum)
Pointers cannot be stored.
There are many ways of implemenation. best practice is to
inherit xml::Serialize and to overwrite
xml::Serialize::initXmlMembers:
@code
class A: public xml::Serialize {
protected:
// all persitent members must be registered
virtual void initXmlMembers() {
className("A"); // giving a class name is very important
persist(_anInteger, "anInteger");
persist(_aBool, "aBool");
persist(_aDouble, "aDouble");
persist(_aString, "aString");
persist(_anotherString, "anotherString");
persist(_aLong, "aLong");
}
private:
int _anInteger;
bool _aBool;
double _aDouble;
std::string _aString;
std::string _anotherString;
unsigned long _aLong;
};
@section serBestPract Best Practice and Inheritance
class B: public xml::Serialize {
protected:
virtual void initXmlMembers() {
className("B");
persist(_a); // name must not be given, it's already known
persist(_anInteger, "anInteger");
}
private:
A _a; // contains an A
int _anInteger;
};
There are many ways of implemenation (see example @ref
serialization.cxx). best practice is to inherit xml::Serialize
and to overwrite xml::Serialize::initXmlMembers, as shown in the
example @ref serialization.cxx.
int main(int, char**) {
A a;
B b;
// ... do something with a and b, then write it to stdout:
a.saveXml(std::out)<<std::endl;
b.saveXml(std::out)<<std::endl;
return 0;
}
@endcode
@warning If you do not follow the best practice, you must know
what you are doing to prevent crashing: You must know that
xml::Serialize stores pointers to the variables given in
xml::Serialize::persist. So be careful and don't access
xml::Serialize after the referenced variables have been removed
from memory.
@subsection inheritance Inheritance
If you follow the best practice and inherit from another class,
you must first call method xml::Serialize::initXmlMembers of the
parent class, then call xml::Serialize::className to set the new
name of the child class.
@section examples Examples
@see @ref serialization.cxx for the different approaches
@see @ref contain_serialization.cxx for containment
@see @ref inherit_serialization.cxx for inheritance
@example serialization.cxx
@example contain_serialization.cxx */
In this example you see several apporoaches on how to connect
variables to XML data structures to serialize them in
XML. Please note, that <b>only class @c B</b> shows the
recommended way of doing it.
@warning Please note that xml::Serialize stores pointers to the
variables that are serialized. If you access xml::Serialize
outside of the life-cycle of any of the persistent variables,
then your program may crash (in the best case) or even behave in
an unexpected way.
@example contain_serialization.cxx
Handle containment in the recommended way. It's very simple: If
all classes inherit from xml::Serialize, then containment
behaves as expected.
@example inherit_serialization.cxx
This is an example for inheritance according the recommended way:
- There's nothing special for the parent.
- The child must do the following in xml::Serialize::initXmlMembers
(the order is important!):
-# call xml::Serialize::initXmlMembers of the parent
-# call xml::Serialize::className to set the new class name
-# call xml::Serialize::persist for all child members
- The only difference is, that ...
- ... the child does not inherit xml::Serialize, but a child of it
- ... the child must first call xml::Serialize::initXmlMembers of
the parent in it's own xml::Serialize::initXmlMembers */
//! @addtogroup serialization
//@{
class Serialize {
public:
typedef bool(*FromNodeFunc)(Any, const xml::Node&);
typedef bool(*ToNodeFunc)(const Any, xml::Node&);
typedef bool(*ClearFunc)(Any);
//! You must call Serialize::className() if you use this constructor!
Serialize() throw();
Serialize(const std::string& className) throw();
@@ -866,14 +1127,15 @@ namespace xml {
const std::string& name) throw();
Serialize& persist(std::string& member,
const std::string& name) throw();
std::ostream& saveXml(std::ostream& os,
const std::string& name = std::string())
virtual std::ostream& saveXml(std::ostream& os,
const std::string& name = std::string())
const throw();
std::istream& loadXml(std::istream& is,
const std::string& name = std::string());
virtual std::istream& loadXml(std::istream& is,
const std::string& name = std::string());
std::string schema() const throw();
static void registerFromNode(FromNodeFunc fromNodeFunc);
static void registerToNode(ToNodeFunc toNodeFunc);
static void registerClear(ClearFunc clearFunc);
protected:
virtual void initXmlMembers();
void checkInit(const Serialize* const ser=0) const {
@@ -883,8 +1145,12 @@ namespace xml {
if (!_xmlFactory) const_cast<Serialize*>(this)->initXmlMembers();
}
}
private:
public: //! @todo
virtual void clear() throw();
/*! @todo Why does @c protected: not work here?!? Children can't
access the members if they are protected! */
public:
//! @cond INTERNAL
void clear(Any member) throw();
void reset() throw();
void copy(const Serialize& o) throw();
template<typename TYPE>
@@ -896,85 +1162,210 @@ namespace xml {
_xmlFactory = schema;
return *this;
}
virtual void fromNode(Any member, const xml::Node& node);
virtual void toNode(const Any member, xml::Node& node) const;
/*
template<typename TYPE, class ALLOC>
void fromNode(std::list<TYPE, ALLOC>* member, xml::Node& node) {
member->clear();
for (xml::Node::size_type i(0); i<node.children(); ++i)
...
}*/
void fromNode(Any member, const xml::Node& node);
void toNode(const Any member, xml::Node& node) const;
std::map<std::string, Any> _xmlNames;
xml::Factory _xmlFactory;
static std::set<FromNodeFunc> _fromNode;
static std::set<ToNodeFunc> _toNode;
static std::set<ClearFunc> _clear;
//! @endcond
};
const int MAX_NUM(14);
template<int NUM> struct ToType {typedef Serialize Type;};
template<> struct ToType<1> {typedef Serialize Type;};
template<> struct ToType<2> {typedef std::string Type;};
template<> struct ToType<3> {typedef bool Type;};
template<> struct ToType<4> {typedef unsigned char Type;};
template<> struct ToType<5> {typedef signed char Type;};
template<> struct ToType<6> {typedef char Type;};
template<> struct ToType<7> {typedef unsigned short Type;};
template<> struct ToType<8> {typedef signed short Type;};
template<> struct ToType<9> {typedef unsigned int Type;};
template<> struct ToType<10> {typedef signed int Type;};
template<> struct ToType<11> {typedef unsigned long Type;};
template<> struct ToType<12> {typedef signed long Type;};
template<> struct ToType<13> {typedef float Type;};
template<> struct ToType<14> {typedef double Type;};
template<typename T> struct ToNum {static const int NUM = 1;};
template<> struct ToNum<Serialize > {static const int NUM = 1;};
template<> struct ToNum<std::string > {static const int NUM = 2;};
template<> struct ToNum<bool > {static const int NUM = 3;};
template<> struct ToNum<unsigned char > {static const int NUM = 4;};
template<> struct ToNum<signed char > {static const int NUM = 5;};
template<> struct ToNum< char > {static const int NUM = 6;};
template<> struct ToNum<unsigned short> {static const int NUM = 7;};
template<> struct ToNum<signed short> {static const int NUM = 8;};
template<> struct ToNum<unsigned int > {static const int NUM = 9;};
template<> struct ToNum<signed int > {static const int NUM = 10;};
template<> struct ToNum<unsigned long > {static const int NUM = 11;};
template<> struct ToNum<signed long > {static const int NUM = 12;};
template<> struct ToNum<float > {static const int NUM = 13;};
template<> struct ToNum<double > {static const int NUM = 14;};
template<typename T> bool isSerialize() {
return ToNum<T>::NUM == 1;
}
template <typename T, bool GOOD=(ToNum<T>::NUM==1)> struct Mapper {
static Serialize* toSerialize(T& obj) {
return 0;
}
};
template <typename T> struct Mapper<T, true> {
static Serialize* toSerialize(T& obj) {
return dynamic_cast<Serialize*>(&obj);
}
};
template<class TYPE, class ALLOC=std::allocator<TYPE> > class List:
public std::list<TYPE, ALLOC>, public Serialize {
template <class TYPE> class Optional: public Serialize {
public:
List() {}
List(const List& o): std::list<TYPE, ALLOC>(o), Serialize(o) {}
List(const std::string& className) throw(): Serialize(className) {}
virtual ~List() {}
Optional() throw() {}
Optional(const Optional& o) throw(): _member(new TYPE(*o._member)) {}
Optional(const TYPE& mem) throw(): _member(new TYPE(mem)) {}
virtual ~Optional() throw() {}
Optional& operator=(const Optional& o) throw() {
_member = new TYPE(*o._member);
return *this;
}
Optional& operator=(const TYPE& mem) throw() {
_member = new TYPE(mem);
return *this;
}
operator bool() const throw() {
return _member.get();
}
const TYPE& operator*() const throw() {
return *_member;
}
TYPE& operator*() throw() {
return *_member;
}
const TYPE*const operator->() const throw() {
return _member.get();
}
TYPE*const operator->() throw() {
return _member.get();
}
Optional& reset() throw() {
_member.reset();
return *this;
}
protected:
virtual void clear() throw() {
_member.reset();
}
virtual std::ostream& saveXml(std::ostream& os,
const std::string& name = std::string())
const throw() {
if (!_member.get()) return os;
checkInit();
xml::Node node(*_xmlFactory);
if (name.size()) node.name(name);
toNode(*_member, node);
os<<node;
return os;
}
private:
std::auto_ptr<TYPE> _member;
};
//! @addtogroup serialization
//@{
/*! @defgroup serContainer Serialization of Container
libxml-cpp can serialize container, such as Lists, Vectors or
Maps. Classes that serialize cannot contain standard C++
container directly, but they must contain container defined
here. For every standard container except @c std::bitset there
is a XML representation available.
The following containers are defined:
- xml::DeQue (inherits @c std::deque and xml::Serialize)
- xml::List (inherits @c std::list and xml::Serialize)
- xml::Map (inherits @c std::map and xml::Serialize)
- xml::MultiMap (inherits @c std::multimap and xml::Serialize)
- xml::MultiSet (inherits @c std::multiset and xml::Serialize)
- xml::Set (inherits @c std::set and xml::Serialize)
- xml::Vector (inherits @c std::vector and xml::Serialize)
E.g. use @c xml::List instead of @c std::list.
I don't see any necessity to implement @c std::priority_queue,
@c std::queue and @c std::stack, they are only restricted
interfaces to another container and don't allow random access
(which is needed to store them).
@example list_serialization.cxx */
//@}
//! @cond INTERNAL
template<class CONTAINER_TYPE> class Container:
public CONTAINER_TYPE,
public Serialize {
public:
Container() {}
Container(const Container& o): CONTAINER_TYPE(o), Serialize(o) {}
Container(const std::string& className) throw(): Serialize(className) {}
virtual ~Container() {}
virtual std::istream& loadXml(std::istream& is,
const std::string& name = std::string()) {
checkInit();
xml::Factory factory(_xmlFactory);
if (name.size()) factory->name(name);
std::auto_ptr<xml::Node> node(factory.read(is));
CONTAINER_TYPE::clear();
for (xml::Node::size_type i(0); i<node->children(); ++i) {
typename CONTAINER_TYPE::value_type tmp;
Serialize::fromNode(&tmp, (*node)[i]); // reads into tmp
push_back(tmp);
}
return is;
}
virtual std::ostream& saveXml(std::ostream& os,
const std::string& name = std::string())
const throw() {
checkInit();
xml::Node node(*_xmlFactory);
if (name.size()) node.name(name);
std::auto_ptr<xml::Node> tpl(node[0].clone());
node.clear();
for (typename Container::const_iterator it = this->begin();
it!=this->end(); ++it) {
typename CONTAINER_TYPE::value_type tmp;
tmp = *it;
std::auto_ptr<xml::Node> item(tpl->clone());
Serialize::toNode(&tmp, *item);
node<<*item;
}
os<<node;
return os;
}
protected:
virtual void initXmlMembers() {
std::string itemName("item");
TYPE tmp;
LOG("initXmlMembers List for: "<<typeid(TYPE).name());
if (isSerialize<TYPE>()) {
LOG("Liste von Serialize");
Serialize* ser(Mapper<TYPE>::toSerialize(tmp));
typename CONTAINER_TYPE::value_type tmp;
if (isSerialize<typename CONTAINER_TYPE::value_type>()) {
Serialize* ser(Mapper<typename CONTAINER_TYPE::value_type>
::toSerialize(tmp));
checkInit(ser);
itemName = ser->_xmlFactory->name();
}
_xmlFactory = xml::Node("dummyroot"); // dummy root, (uninitialized exc)
persist(tmp, itemName); // add as child of dummyroot
(*_xmlFactory)[0].limits(0, 0); // any number of children possible
}
virtual void clear() throw() {
CONTAINER_TYPE::clear();
}
};
template<class CONTAINER_TYPE> class AssociativeContainer:
public CONTAINER_TYPE,
public Serialize {
public:
AssociativeContainer() {}
AssociativeContainer(const AssociativeContainer& o):
CONTAINER_TYPE(o), Serialize(o) {
}
AssociativeContainer(const std::string& className) throw():
Serialize(className) {
}
virtual ~AssociativeContainer() {}
virtual std::istream& loadXml(std::istream& is,
const std::string& name = std::string()) {
checkInit();
xml::Factory factory(_xmlFactory);
if (name.size()) factory->name(name);
std::auto_ptr<xml::Node> node(factory.read(is));
CONTAINER_TYPE::clear();
for (xml::Node::size_type i(0); i<node->children(); ++i) {
typename CONTAINER_TYPE::value_type tmp;
Serialize::fromNode(&tmp, (*node)[i]); // reads into tmp
insert(tmp);
}
return is;
}
virtual std::ostream& saveXml(std::ostream& os,
const std::string& name = std::string())
const throw() {
checkInit();
xml::Node node(*_xmlFactory);
if (name.size()) node.name(name);
std::auto_ptr<xml::Node> tpl(node[0].clone());
node.clear();
for (typename CONTAINER_TYPE::const_iterator it = this->begin();
it!=this->end(); ++it) {
typename CONTAINER_TYPE::value_type tmp;
tmp = *it;
std::auto_ptr<xml::Node> item(tpl->clone());
Serialize::toNode(&tmp, *item);
node<<*item;
}
os<<node;
return os;
}
protected:
virtual void initXmlMembers() {
std::string itemName("item");
typename CONTAINER_TYPE::value_type tmp;
if (isSerialize<typename CONTAINER_TYPE::value_type>()) {
Serialize* ser(Mapper<typename CONTAINER_TYPE::value_type>
::toSerialize(tmp));
assert(ser);
assert(ser!=this);
assert((void*)ser==(void*)&tmp);
@@ -982,34 +1373,231 @@ namespace xml {
itemName = ser->_xmlFactory->name();
}
_xmlFactory = xml::Node("dummyroot"); // dummy root, (uninitialized exc)
persist(tmp, itemName); // add _reference as child of dummyroot
persist(tmp, itemName); // add as child of dummyroot
(*_xmlFactory)[0].limits(0, 0); // any number of children possible
}
virtual void fromNode(Any member, const xml::Node& node) {
this->clear();
for (xml::Node::size_type i(0); i<node.parent().children(); ++i) {
TYPE tmp;
Serialize::fromNode(&tmp, node.parent()[i]); // reads into tmp
push_back(tmp);
}
}
virtual void toNode(const Any member, xml::Node& node) const {
std::auto_ptr<xml::Node> tpl(node.clone());
xml::Node& parent(node.parent());
parent.clear(); // "node" is now invalid
for (typename List::const_iterator it = this->begin();
it!=this->end(); ++it) {
TYPE tmp;
tmp = *it;
std::auto_ptr<xml::Node> item(tpl->clone());
Serialize::toNode(&tmp, *item);
parent<<*item;
}
virtual void clear() throw() {
CONTAINER_TYPE::clear();
}
};
//@}
template<class CONTAINER_TYPE> class AssociativeMap:
public CONTAINER_TYPE,
public Serialize {
public:
AssociativeMap() {}
AssociativeMap(const AssociativeMap& o):
CONTAINER_TYPE(o), Serialize(o) {
}
AssociativeMap(const std::string& className) throw():
Serialize(className) {
}
virtual ~AssociativeMap() {}
virtual std::istream& loadXml(std::istream& is,
const std::string& name = std::string()) {
checkInit();
xml::Factory factory(_xmlFactory);
if (name.size()) factory->name(name);
std::auto_ptr<xml::Node> node(factory.read(is));
CONTAINER_TYPE::clear();
LOG("READING: "<<*node);
for (xml::Node::size_type i(0); i<node->children(); ++i) {
typename CONTAINER_TYPE::key_type key;
typename CONTAINER_TYPE::mapped_type data;
LOG("READING Key: "<<(*node)[i]<<"Value:"<<(*node)[1+i]);
Serialize::fromNode(&key, (*node)[i]); // reads into tmp
Serialize::fromNode(&data, (*node)[++i]); // key&value
insert(typename CONTAINER_TYPE::value_type(key, data));
LOG("READ");
}
LOG("DONE");
return is;
}
virtual std::ostream& saveXml(std::ostream& os,
const std::string& name = std::string())
const throw() {
checkInit();
xml::Node node(*_xmlFactory);
if (name.size()) node.name(name);
std::auto_ptr<xml::Node> tpl1(node[0].clone());
std::auto_ptr<xml::Node> tpl2(node[1].clone());
node.clear(); // "node" is now invalid
for (typename AssociativeMap::const_iterator it = this->begin();
it!=this->end(); ++it) {
typename CONTAINER_TYPE::key_type key;
typename CONTAINER_TYPE::mapped_type data;
key = it->first;
data = it->second;
std::auto_ptr<xml::Node> item1(tpl1->clone());
Serialize::toNode(&key, *item1);
std::auto_ptr<xml::Node> item2(tpl2->clone());
Serialize::toNode(&data, *item2);
node<<*item1<<*item2;
}
os<<node;
return os;
}
protected:
virtual void initXmlMembers() {
std::string keyName("key");
std::string valueName("value");
typename CONTAINER_TYPE::key_type key;
typename CONTAINER_TYPE::mapped_type data;
if (isSerialize<typename CONTAINER_TYPE::key_type>()) {
const Serialize* ser(Mapper<typename CONTAINER_TYPE::key_type>
::toSerialize(key));
checkInit(ser);
keyName = ser->_xmlFactory->name();
}
if (isSerialize<typename CONTAINER_TYPE::mapped_type>()) {
Serialize* ser(Mapper<typename CONTAINER_TYPE::mapped_type>
::toSerialize(data));
checkInit(ser);
valueName = ser->_xmlFactory->name();
}
_xmlFactory = xml::Node("dummyroot"); // dummy root, (uninitialized exc)
persist(key, keyName); // add as child of dummyroot
persist(data, valueName); // add as child of dummyroot
(*_xmlFactory)[0].limits(0, 0); // any number of children possible
(*_xmlFactory)[1].limits(0, 0); // any number of children possible
}
virtual void clear() throw() {
CONTAINER_TYPE::clear();
}
};
//! @endcond
}
//! @cond INTERNAL
//! @addtogroup serContainer
//@{
# ifdef __XML_CXX_DECLARE_CONTAINER_CLASS__
# error Macro __XML_CXX_DECLARE_CONTAINER_CLASS__ has been used elsewhere
# endif
# define __XML_CXX_DECLARE_CONTAINER_CLASS__(CONTAINER, STD_CONTAINER) \
namespace xml { \
template<class TYPE, class ALLOC=std::allocator<TYPE> > \
class CONTAINER: \
public Container<STD_CONTAINER<TYPE, ALLOC> > { \
public: \
CONTAINER() {} \
CONTAINER(const CONTAINER& o): \
Container<STD_CONTAINER<TYPE, ALLOC> >(o) { \
} \
CONTAINER(const std::string& className) throw(): \
Container<STD_CONTAINER<TYPE, ALLOC> >(className) { \
} \
virtual ~CONTAINER() {} \
}; \
}
# include <list>
__XML_CXX_DECLARE_CONTAINER_CLASS__(List, std::list);
# include <vector>
__XML_CXX_DECLARE_CONTAINER_CLASS__(Vector, std::vector);
# include <deque>
__XML_CXX_DECLARE_CONTAINER_CLASS__(Deque, std::deque);
# undef __XML_CXX_DECLARE_CONTAINER_CLASS__
# define __XML_CXX_DECLARE_CONTAINER_CLASS__(CONTAINER, STD_CONTAINER) \
namespace xml { \
template<class TYPE, class CONT=std::deque<TYPE> > \
class CONTAINER: \
public AssociativeContainer \
<STD_CONTAINER<TYPE, CONT> > { \
public: \
CONTAINER() {} \
CONTAINER(const CONTAINER& o): \
AssociativeContainer \
<STD_CONTAINER<TYPE, CONT> >(o) { \
} \
CONTAINER(const std::string& className) throw(): \
AssociativeContainer \
<STD_CONTAINER<TYPE, CONT> > \
(className) { \
} \
virtual ~CONTAINER() {} \
}; \
}
# include <stack>
__XML_CXX_DECLARE_CONTAINER_CLASS__(Stack, std::stack);
# include <queue>
__XML_CXX_DECLARE_CONTAINER_CLASS__(Queue, std::queue);
# undef __XML_CXX_DECLARE_CONTAINER_CLASS__
# define __XML_CXX_DECLARE_CONTAINER_CLASS__(CONTAINER, STD_CONTAINER) \
namespace xml { \
template \
<class TYPE, class CONT=std::vector<TYPE>, \
class COMPARE=std::less<TYPE>, \
class ALLOC=std::allocator<TYPE> > \
class CONTAINER: \
public AssociativeContainer \
<STD_CONTAINER<TYPE, COMPARE, ALLOC> > { \
public: \
CONTAINER() {} \
CONTAINER(const CONTAINER& o): \
AssociativeContainer \
<STD_CONTAINER<TYPE, COMPARE, ALLOC> >(o) { \
} \
CONTAINER(const std::string& className) throw(): \
AssociativeContainer \
<STD_CONTAINER<TYPE, COMPARE, ALLOC> > \
(className) { \
} \
virtual ~CONTAINER() {} \
}; \
}
__XML_CXX_DECLARE_CONTAINER_CLASS__(PriorityQueue, std::priority_queue);
# undef __XML_CXX_DECLARE_CONTAINER_CLASS__
# define __XML_CXX_DECLARE_CONTAINER_CLASS__(CONTAINER, STD_CONTAINER) \
namespace xml { \
template<class TYPE, class COMPARE=std::less<TYPE>, \
class ALLOC=std::allocator<TYPE> > \
class CONTAINER: \
public AssociativeContainer \
<STD_CONTAINER<TYPE, COMPARE, ALLOC> > { \
public: \
CONTAINER() {} \
CONTAINER(const CONTAINER& o): \
AssociativeContainer \
<STD_CONTAINER<TYPE, COMPARE, ALLOC> >(o) { \
} \
CONTAINER(const std::string& className) throw(): \
AssociativeContainer \
<STD_CONTAINER<TYPE, COMPARE, ALLOC> > \
(className) { \
} \
virtual ~CONTAINER() {} \
}; \
}
# include <set>
__XML_CXX_DECLARE_CONTAINER_CLASS__(Set, std::set);
__XML_CXX_DECLARE_CONTAINER_CLASS__(MultiSet, std::multiset);
# undef __XML_CXX_DECLARE_CONTAINER_CLASS__
# define __XML_CXX_DECLARE_CONTAINER_CLASS__(CONTAINER, STD_CONTAINER) \
namespace xml { \
template<class KEY, class VALUE, class COMPARE=std::less<KEY>, \
class ALLOC=std::allocator<std::pair<const KEY, VALUE> > > \
class CONTAINER: public AssociativeMap \
<STD_CONTAINER<KEY, VALUE, COMPARE, ALLOC> > { \
public: \
CONTAINER() {} \
CONTAINER(const CONTAINER& o): \
AssociativeMap \
<STD_CONTAINER<KEY, VALUE, COMPARE, ALLOC> >(o) { \
} \
CONTAINER(const std::string& className) throw(): \
AssociativeMap \
<STD_CONTAINER<KEY, VALUE, COMPARE, ALLOC> > \
(className) { \
} \
virtual ~CONTAINER() {} \
}; \
}
# include <map>
__XML_CXX_DECLARE_CONTAINER_CLASS__(Map, std::map);
__XML_CXX_DECLARE_CONTAINER_CLASS__(MultiMap, std::multimap);
# undef __XML_CXX_DECLARE_CONTAINER_CLASS__
//@}
//! @endcond
#endif