|
|
|
@ -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. |
|
|
|
|
|
|
|
|
|
@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): |
|
|
|
|
|
|
|
|
|
From the README file: |
|
|
|
|
@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. <p><p><p></p></p></p>) |
|
|
|
|
- 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><?xml?></code>
|
|
|
|
|
//! or a document type declaration <code><!DOCTYPE ...></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 <address> |
|
|
|
|
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 <class TYPE> class Optional: public Serialize { |
|
|
|
|
public: |
|
|
|
|
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; |
|
|
|
|
}; |
|
|
|
|
template <typename T> struct Mapper<T, true> { |
|
|
|
|
static Serialize* toSerialize(T& obj) { |
|
|
|
|
return dynamic_cast<Serialize*>(&obj); |
|
|
|
|
|
|
|
|
|
//! @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"); |
|
|
|
|
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 TYPE, class ALLOC=std::allocator<TYPE> > class List: |
|
|
|
|
public std::list<TYPE, ALLOC>, public Serialize { |
|
|
|
|
template<class CONTAINER_TYPE> class AssociativeContainer: |
|
|
|
|
public CONTAINER_TYPE, |
|
|
|
|
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() {} |
|
|
|
|
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"); |
|
|
|
|
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)); |
|
|
|
|
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 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 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(); |
|
|
|
|
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) { |
|
|
|
|
TYPE tmp; |
|
|
|
|
tmp = *it; |
|
|
|
|
std::auto_ptr<xml::Node> item(tpl->clone()); |
|
|
|
|
Serialize::toNode(&tmp, *item); |
|
|
|
|
parent<<*item; |
|
|
|
|
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 |
|
|
|
|