/*! @file
@ id $ Id $
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
# ifndef LIB_XML_CXX_HXX
# define LIB_XML_CXX_HXX
# include <istream>
# include <sstream>
# include <string>
# include <vector>
# include <set>
# include <map>
# include <memory>
# include <typeinfo>
# include <stdexcept>
# include <xml-cxx/any.hxx>
//! @cond DEBUG
# include <cassert>
# include <iostream>
# include <iomanip>
class MethodTrace {
public :
MethodTrace ( const void * addr , const std : : string & name ) throw ( ) :
_addr ( addr ) , _name ( name ) {
std : : clog < < std : : hex < < std : : setw ( 15 ) < < _addr < < " : "
< < std : : dec < < std : : setw ( 2 + _level )
< < std : : setfill ( ' ' ) < < " \\ " < < _name < < std : : endl ;
+ + _level ;
}
~ MethodTrace ( ) throw ( ) {
- - _level ;
std : : clog < < std : : hex < < std : : setw ( 15 ) < < _addr < < " : " < < std : : dec
< < std : : setw ( 2 + _level ) < < std : : setfill ( ' ' )
< < " / " < < _name < < std : : endl ;
}
private :
const void * _addr ;
const std : : string _name ;
static unsigned int _level ;
} ;
# define TRACE MethodTrace XXX_METHOD(this, __PRETTY_FUNCTION__)
# define LOG(X) std::clog<<__PRETTY_FUNCTION__<<"\t**** "<<X<<std::endl
# define ASSERT(X, Y) { \
if ( ! ( Y ) ) { \
LOG ( X ) ; \
} \
assert ( Y ) ; \
}
//! @endcond
/*! @mainpage
@ section maintitle C + + XML Class Library
- 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 ) :
@ 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 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
other tags only ( type xml : : Node ) or text only ( type
xml : : String ) . - > This is intended behaviour !
- Unlimited recursion is not possible
( 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 )
- Optional values are supported through xml : : Optional
- @ c std : : bitset , @ c std : : priority_queue , @ c std : : queue and
@ c std : : stack are not 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
( e . g . for configuration files ) that can easily be edited by
hand . This library does not need any kind of C + + code parser or
proprietary pre compiler . You can specify a schema entirly in
native C + + . Access to the XML structures is through typical C + +
operators which rresults in a simple and intuitive syntax . The
schema is verified when XML is read and exceptions are thrown when
the XML to be pares is invalid . Exceptions specify exactly the
location and reason of the problem , so that the editor of the XML
file can easily find and correct the problem . Due to the
verification , it is not necessary to check every access : Only
optional attributes and tags must be tested for their existence ,
before they can be accessed .
There are a lot of different approaches for using XML in C + + , all
of them have their specific limitations . This library should be
better .
The design is based on my experiance with gsoap
( http : //gsoap.sf.net), boost serialization (http://boost.org) and
Qt XML ( http : //qtsoftware.com).
@ 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 .
This looks like :
@ code
QDomDocument doc ;
if ( ! doc . setContent ( _http . readAll ( ) ) ) ; // error
QDomNodeList releases ( doc . elementsByTagName ( " release " ) ) ;
for ( int i ( 0 ) ; i < releases . size ( ) ; + + i )
if ( releases . at ( i ) . attributes ( ) . contains ( " type " ) & &
releases . at ( i ) . attributes ( ) . namedItem ( " type " ) . nodeValue ( ) = = _name ) {
_software = releases . at ( i ) . firstChildElement ( " software " ) ;
_firmware = releases . at ( i ) . firstChildElement ( " firmware " ) ;
if ( _software . isNull ( ) | | _firmware . isNull ( ) | |
releases . at ( i ) . firstChildElement ( " notes " ) . isNull ( ) ) ; // error
. . .
@ endcode
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 file , where you want to declare constant names for your xml
nodes .
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 .
@ 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 .
@ see @ ref node_macros . cxx
@ example node_macros . cxx
The example code is equivalent to :
@ code
int main ( int , char * * ) {
xml : : Factory test ( xml : : Node ( " base " )
< < ( xml : : Node ( " child " )
< < xml : : String ( " element " ) ) ;
std : : stringstream ss ( " <base> \n "
" <child> \n "
" <element>Hello</element> \n "
" </child> \n "
" </base> " ) ;
std : : unique_ptr < xml : : Node > file ( test . read ( ss ) ) ;
std : : cout < < " The element is: "
< < ( * file ) [ " child " ] [ " element " ]
< < std : : endl ;
return 0 ;
}
@ endcode
The advantage is the fact that you have no more quoted strings to
cope with . So your potential runtime errors through typos become
compile time errors , which is more robust and easier to find . If
you want to write less code , you are free to use < code > using
namespace xml : : name < / code > in your C + + implementation files , since
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) \
namespace xml { \
namespace name { \
static const std : : string NAME ( # NAME ) ; \
} \
}
//! Define a constant for a xml::Node and for a string containing its name
/*! Put this macro in the global part of your header files. After
declaration of e . g . < code > XML_NODE ( tagname ) < / code > you can use
< code > xml : : node : : tagname < / code > as constant xml : : Node and
< code > xml : : name : : tagname < / code > as a constant std : : string with
contents @ c " tagname " in your code .
@ see XML_STRING same for xml : : String
@ see XML_NAME called by XML_NODE */
# define XML_NODE(NAME) \
XML_NAME ( NAME ) ; \
namespace xml { \
namespace node { \
static const xml : : Node NAME ( # NAME ) ; \
} \
}
//! Define a constant for a xml::String and for a string containing its name
/*! Put this macro in the global part of your header files. After
declaration of e . g . < code > XML_STRING ( tagname ) < / code > you can use
< code > xml : : string : : tagname < / code > as constant xml : : String and
< code > xml : : name : : tagname < / code > as a constant std : : string with
contents @ c " tagname " in your code .
@ see XML_NODE same for xml : : Node
@ see XML_NAME called by XML_STRING */
# define XML_STRING(NAME) \
XML_NAME ( NAME ) ; \
namespace xml { \
namespace string { \
static const xml : : String NAME ( # NAME ) ; \
} \
}
//! @todo Define a constant for a xml::Node and for a string containing its name
/*! Put this macro in the global part of your header files. After
declaration of e . g . < code > XML_NODE ( tagname ) < / code > you can use
< code > xml : : node : : tagname < / code > as constant xml : : Node and
< code > xml : : name : : tagname < / code > as a constant std : : string with
contents @ c " tagname " in your code .
@ see XML_STRING same for xml : : String
@ see XML_NAME called by XML_NODE */
# define XML_PARENT(NAME, ...) \
XML_NAME ( NAME ) ; \
namespace xml { \
namespace node { \
static const xml : : Node NAME ( # NAME ) ; \
} \
}
//@}
/*! @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
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 & lt ; persons & gt ; 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>
[ . . . ]
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 : : unique_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 ;
}
@ endcode */
//! Everything is in namespace xml
namespace xml {
std : : string version ( ) ;
//! @addtogroup freexml
//@{
//! @cond INTERNAL
//============================================================================
//! Type of an xml node.
/*! Only start nodes and empty nodes may have attributes. */
enum NodeType {
START , //!< start node, such as <code><node></code>
END , //!< end node, such as <code></node></code>
EMPTY , //!< empty node, such as <code><node/></code>
SPECIAL //!< special node, such as
//! a comment <code><!-- ... --></code>,
//! 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.
const bool optional ( false ) ;
//================================================================= EXCEPTIONS
struct Tag ;
class Attributes ;
class Node ;
class Factory ;
//@}
//! @defgroup exceptions Exception classes
//@{
//----------------------------------------------------------------------------
class exception : public std : : exception {
public :
exception ( std : : string reason ) throw ( ) ;
exception ( std : : string reason , const Node & t ) throw ( ) ;
~ exception ( ) throw ( ) ;
void line ( unsigned long line ) throw ( ) ;
const char * what ( ) const throw ( ) ;
private :
std : : string _what ;
Node * _node ;
} ;
//----------------------------------------------------------------------------
class type_not_registered : public exception {
public :
type_not_registered ( std : : string type , std : : string name , const Node & t ) :
exception ( " serialized node type is not registered \n type: "
+ type + " \n name: " + name , t ) {
}
type_not_registered ( std : : string type ) :
exception ( " serialized node type is not registered \n type: " + type ) {
}
} ;
//----------------------------------------------------------------------------
class empty_attribute_list : public exception {
public :
empty_attribute_list ( const std : : string & name ) throw ( ) :
exception ( " list of attribute is empty access to first element failed "
" \n attribute: " + name ) {
}
} ;
//----------------------------------------------------------------------------
class factory_not_valid : public exception {
public :
factory_not_valid ( ) throw ( ) :
exception ( " a factory must be given a template node " ) {
}
} ;
//----------------------------------------------------------------------------
class no_parent : public exception {
public :
no_parent ( const Node & t ) throw ( ) : exception ( " node has no parent " , t ) { }
} ;
//----------------------------------------------------------------------------
class tag_expected : public exception {
public :
tag_expected ( const Node & t , const std : : string & txt ) throw ( ) :
exception ( " tag ('<') expected, text not allowed \n text: " + txt , t ) { }
} ;
//----------------------------------------------------------------------------
class type_mismatch : public exception {
public :
type_mismatch ( const Node & t , const std : : string & txt ,
const std : : string & comment ) throw ( ) :
exception ( " wrong type, text contains mismatching character \n " + comment
+ " \n text: " + txt , t ) { }
} ;
//----------------------------------------------------------------------------
class access_error : public exception {
public :
access_error ( const Node & t , const std : : string & name ) throw ( ) ;
~ access_error ( ) throw ( ) { }
const char * what ( ) const throw ( ) ;
private :
std : : string _name ;
} ;
//----------------------------------------------------------------------------
class out_of_range : public exception {
public :
out_of_range ( const Node & t , size_t pos ) throw ( ) ;
~ out_of_range ( ) throw ( ) { }
const char * what ( ) const throw ( ) ;
private :
size_t _pos ;
} ;
//----------------------------------------------------------------------------
class cannot_have_children : public exception {
public :
cannot_have_children ( const Node & parent , const Node & child ) throw ( ) ;
~ cannot_have_children ( ) throw ( ) ;
const char * what ( ) const throw ( ) ;
private :
Node * _child ;
} ;
//----------------------------------------------------------------------------
class attribute_not_available : public exception {
public :
attribute_not_available ( const Node & t , const std : : string & attr ) throw ( ) :
exception ( " attribute \" " + attr + " \" not set " , t ) {
}
} ;
//----------------------------------------------------------------------------
class stream_error : public exception {
public :
stream_error ( const std : : string & reason , const Node & t ,
std : : istream & is , const Tag & tag , char c = 0 ) throw ( ) ;
stream_error ( const std : : string & reason , const Node & t ,
std : : istream & is ) throw ( ) ;
~ stream_error ( ) throw ( ) ;
const char * what ( ) const throw ( ) ;
private :
std : : istream : : streampos _pos ;
Tag * _tag ;
char _char ;
} ;
//----------------------------------------------------------------------------
class wrong_end_tag : public stream_error {
public :
wrong_end_tag ( const Node & t , std : : istream & is , const Tag & tag , char c = 0 )
throw ( ) :
stream_error ( " mismatching end tag " , t , is , tag , c ) {
}
} ;
//----------------------------------------------------------------------------
class missing_end_tag : public stream_error {
public :
missing_end_tag ( const Node & t , std : : istream & is , const Tag & tag , char c = 0 )
throw ( ) :
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 )
throw ( ) :
stream_error ( " start tag does not match expected tag " , t , is , tag , c ) {
}
} ;
//----------------------------------------------------------------------------
class second_slash_in_tag : public stream_error {
public :
second_slash_in_tag ( const Node & t , std : : istream & is , const Tag & tag ,
char c = 0 )
throw ( ) :
stream_error ( " a tag may have no more than one slash " , t , is , tag , c ) {
}
} ;
//----------------------------------------------------------------------------
class character_after_slash : public stream_error {
public :
character_after_slash ( const Node & t , std : : istream & is , const Tag & tag ,
char c = 0 )
throw ( ) :
stream_error ( " unexpected character after empty-slash " ,
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 )
throw ( ) :
stream_error ( " attributes are not allowed in end tags " , t , is , tag ) {
}
} ;
//----------------------------------------------------------------------------
class attribute_value_not_quoted : public stream_error {
public :
attribute_value_not_quoted ( const Node & t , std : : istream & is ,
const Tag & tag ,
char c , std : : string attr ) throw ( ) :
stream_error ( " attribute values must be quoted ( \" ) \n attribute: " + attr ,
t , is , tag , c ) {
}
} ;
//----------------------------------------------------------------------------
class duplicate_attribute : public stream_error {
public :
duplicate_attribute ( const Node & t , std : : istream & is , const Tag & tag ,
std : : string attr ) throw ( ) :
stream_error ( " attribute duplicated \n attribute: " + attr ,
t , is , tag , 0 ) {
}
} ;
//----------------------------------------------------------------------------
class illegal_attribute : public stream_error {
public :
illegal_attribute ( const Node & t , std : : istream & is , const Tag & tag ,
std : : string attr ) throw ( ) :
stream_error ( " illegal attribute found \n attribute: " + attr ,
t , is , tag , 0 ) {
}
} ;
//----------------------------------------------------------------------------
class mandatory_attribute_missing : public stream_error {
public :
mandatory_attribute_missing ( const Node & t , std : : istream & is ,
const Tag & tag , std : : string attr ) throw ( ) :
stream_error ( " mandatory attribute missing \n attribute: " + attr ,
t , is , tag , 0 ) {
}
} ;
//----------------------------------------------------------------------------
class wrong_node_number : public stream_error {
public :
wrong_node_number ( const Node & t , std : : istream & is ,
const std : : string & name ,
unsigned long num ,
unsigned long min , unsigned long max ) throw ( ) :
stream_error ( " wrong number of child nodes \n name of child: " + name
+ " \n number of nodes: " + conv ( num )
+ " \n minimuml number: " + conv ( min )
+ " \n maximum number: " + conv ( max ) , t , is ) {
}
private :
static std : : string conv ( unsigned long i ) throw ( ) {
std : : stringstream ss ;
ss < < i ;
return ss . str ( ) ;
}
} ;
//! @}
//============================================================================
//! @addtogroup freexml
//@{
//----------------------------------------------------------------------------
//! Map for attribute values.
/*! Attributes can be set using method xml::Node::attr(). Check for
an attribute with xml : : Node : : hasAttr ( ) . Attributes must be
unique , which means that every attribute must be set at maximum
once . This is corect : < code > & lt ; node
attribute = " value " & gt ; < / code > , this is not allowed :
< code > & lt ; node attribute = " value " attribute = " value " & gt ; < / code > */
class Attributes : public std : : map < std : : string , std : : string > {
public :
//! Attributes may contain a list of space separated values.
typedef std : : vector < std : : string > List ;
//! Attribute values ar mainly a std::pair.
/*! In addition to a normal std::pair, attributes offer an
assignment operator to set the value , and can be constructed
as empty attribute , given only a key .
@ note Simply use xml : : Attr instead of xml : : Attributes : : Value . */
class Value : public value_type {
public :
Value ( const value_type & o ) throw ( ) ;
Value ( const std : : string & name ) throw ( ) ;
Value ( const std : : string & name , const std : : string & namevalue ) throw ( ) ;
Value & operator = ( const std : : string & value ) throw ( ) ;
const std : : string & name ( ) const throw ( ) ;
const std : : string & value ( ) const throw ( ) ;
std : : string & value ( ) throw ( ) ;
operator bool ( ) const throw ( ) ;
bool toBool ( ) const throw ( ) ;
operator unsigned long ( ) const throw ( ) ;
unsigned long toNumber ( ) const throw ( ) ;
operator List ( ) const throw ( ) ;
List toList ( const std : : string & separators = " \t \n \r " ) const throw ( ) ;
std : : string front ( const std : : string & separators = " \t \n \r " ) const
throw ( empty_attribute_list ) ;
private :
Value ( ) ; // not implemented, key must always be given
} ;
Attributes ( ) throw ( ) ;
Attributes ( const std : : string & empty ) throw ( ) ;
Attributes ( const std : : string & key , const std : : string & value ) throw ( ) ;
Attributes & operator < < ( const Value & v ) throw ( ) ;
Attributes & operator < < ( const std : : string & key ) throw ( ) ;
} ;
//! Simplification: Use xml::Attr instead of xml::Attributes::Value.
typedef Attributes : : Value Attr ;
//----------------------------------------------------------------------------
//! @internal structure for parsing tags
struct Tag {
std : : string name ;
NodeType type ;
std : : string text ;
Attributes attributes ;
std : : string special ;
bool found ;
} ;
//----------------------------------------------------------------------------
//! 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 . */
class Node {
private :
typedef std : : vector < Node * > Contents ;
public :
typedef Contents : : size_type size_type ;
typedef std : : vector < Node * > List ;
Node ( std : : string name , size_type min = 0 , size_type max = 0 ) throw ( ) ;
Node ( const Node & o ) throw ( ) ;
virtual ~ Node ( ) throw ( ) ;
virtual Node & operator = ( const Node & o ) throw ( ) ;
virtual std : : unique_ptr < Node > clone ( ) const throw ( ) ;
virtual std : : ostream & out ( std : : ostream & o , unsigned int level = 0 ) const
throw ( ) ;
virtual std : : string text ( ) const throw ( ) ;
virtual Node & text ( const std : : string & txt ) throw ( tag_expected ,
type_mismatch ) ;
virtual Node & append ( const Node & o ) throw ( cannot_have_children ) ;
virtual Node & remove ( Node & n ) throw ( access_error ) ;
virtual Node & remove ( const std : : string & n ) throw ( access_error ) ;
virtual Node & remove ( size_type n ) throw ( out_of_range ) ;
virtual Node & set ( const Attributes & o ) throw ( ) ;
Node & clear ( ) throw ( ) ;
std : : string name ( ) const throw ( ) ;
Node & name ( const std : : string & n ) throw ( ) ;
Node & min ( size_type m ) throw ( ) ;
size_type min ( ) const throw ( ) ;
Node & max ( size_type m ) throw ( ) ;
size_type max ( ) const throw ( ) ;
bool isChild ( ) const throw ( ) ;
Node & parent ( ) const throw ( no_parent ) ;
bool hasAttr ( const std : : string & name ) const throw ( ) ;
Node & attr ( const std : : string & name , bool mandatory ) throw ( ) ;
Node & attr ( const std : : string & name , const std : : string & deflt ) throw ( ) ;
std : : string attr ( const std : : string & name ) const throw ( ) ;
std : : string & attr ( const std : : string & name ) throw ( ) ;
const Attributes : : Value attribute ( const std : : string & name )
const throw ( attribute_not_available ) ;
const Attributes & attributes ( ) const throw ( ) ;
Attributes & attributes ( ) throw ( ) ;
const Node & first ( ) const throw ( out_of_range ) ;
Node & first ( ) throw ( out_of_range ) ;
const Node & last ( ) const throw ( out_of_range ) ;
Node & last ( ) throw ( out_of_range ) ;
Node & limits ( size_type min = 0 , size_type max = 0 ) throw ( ) ;
List list ( const std : : string & name ) const throw ( ) ;
bool operator ( ) ( const std : : string & child ) const throw ( ) ;
Node & operator < < ( const Node & o ) throw ( cannot_have_children ) ;
Node & operator < < ( const Attributes & o ) throw ( ) ;
size_type children ( ) const throw ( ) ;
const Node & operator [ ] ( size_type child ) const throw ( out_of_range ) ;
Node & operator [ ] ( size_type child ) throw ( out_of_range ) ;
const Node & operator [ ] ( const std : : string & child ) const
throw ( access_error ) ;
Node & operator [ ] ( const std : : string & child ) throw ( access_error ) ;
std : : string operator * ( ) const throw ( ) ;
Node & operator = ( const std : : string & contents ) throw ( tag_expected ,
type_mismatch ) ;
friend std : : ostream & operator < < ( std : : ostream & o , const Node & t ) throw ( ) ;
protected :
Attributes _attributes ;
private :
Node * find ( const std : : string & child ) const throw ( ) ;
virtual std : : unique_ptr < Node > clone ( Node * p ) const throw ( ) ;
Node ( ) ; // not implemented
Contents _contents ;
std : : string _name ;
Node * _parent ;
size_type _min ;
size_type _max ;
} ;
//----------------------------------------------------------------------------
//! A leaf node that contains text but no child nodes.
class String : public Node {
public :
String ( std : : string name ,
Node : : size_type min = 0 , Node : : size_type max = 0 ) throw ( ) ;
String ( std : : string name , const std : : string & text ,
Node : : size_type min = 0 , Node : : size_type max = 0 ) throw ( ) ;
virtual ~ String ( ) throw ( ) { }
virtual std : : unique_ptr < Node > clone ( ) const throw ( ) ;
virtual std : : string text ( ) const throw ( ) ;
virtual String & text ( const std : : string & txt ) throw ( tag_expected ,
type_mismatch ) ;
virtual std : : ostream & out ( std : : ostream & o , unsigned int level = 0 ) const
throw ( ) ;
virtual String & append ( const Node & o ) throw ( cannot_have_children ) ;
Node & operator = ( const std : : string & contents ) throw ( ) ;
operator std : : string ( ) const throw ( ) ;
operator bool ( ) const throw ( ) ;
operator char ( ) const throw ( ) ;
operator signed char ( ) const throw ( ) ;
operator unsigned char ( ) const throw ( ) ;
operator signed short ( ) const throw ( ) ;
operator unsigned short ( ) const throw ( ) ;
operator signed int ( ) const throw ( ) ;
operator unsigned int ( ) const throw ( ) ;
operator signed long ( ) const throw ( ) ;
operator unsigned long ( ) const throw ( ) ;
operator float ( ) const throw ( ) ;
operator double ( ) const throw ( ) ;
protected :
std : : string _text ;
} ;
//----------------------------------------------------------------------------
//! A leaf node that contains only numbers and no child nodes.
class UnsignedInteger : public String {
public :
UnsignedInteger ( std : : string name , unsigned long i = 0 ,
size_type min = 0 , size_type max = 0 ) throw ( ) ;
virtual std : : unique_ptr < Node > clone ( ) const throw ( ) ;
virtual ~ UnsignedInteger ( ) throw ( ) { }
virtual UnsignedInteger & text ( const std : : string & txt )
throw ( tag_expected , type_mismatch ) ;
unsigned long number ( ) const throw ( ) ;
static unsigned long number ( const Node & node ) throw ( ) ;
} ;
//----------------------------------------------------------------------------
//! 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 60 b < / line >
< line > 8005 Zürich < / line >
< / location >
< country > Schweiz < / country >
< / address >
@ endverbatim */
class Factory {
public :
Factory ( const Node & t ) throw ( ) ;
Factory ( ) throw ( ) ;
Factory & operator = ( const Node & t ) throw ( ) ;
Factory & append ( const Node & node ) throw ( ) ;
const Node & operator * ( ) const throw ( factory_not_valid ) ;
const Node * const operator - > ( ) const throw ( factory_not_valid ) ;
operator bool ( ) const throw ( ) ;
friend std : : ostream & operator < < ( std : : ostream & os ,
const Factory & factory )
throw ( factory_not_valid ) ;
static std : : ostream & print ( std : : ostream & os , const Node & node ,
unsigned int level = 0 ) throw ( ) ;
std : : unique_ptr < Node > read ( std : : istream & is )
throw ( wrong_end_tag , wrong_start_tag , tag_expected , type_mismatch ,
second_slash_in_tag , character_after_slash ,
missing_end_tag , attribute_value_not_quoted , access_error ,
duplicate_attribute , attributes_in_end_tag ,
illegal_attribute , mandatory_attribute_missing ,
wrong_node_number , factory_not_valid ) ;
void reset ( ) throw ( ) ;
private :
friend class stream_error ;
friend class Serialize ;
template < class T > friend class Optional ;
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 ( ) ;
std : : unique_ptr < Node > read ( std : : istream & is , const Node & position )
throw ( wrong_end_tag , wrong_start_tag , tag_expected , type_mismatch ,
second_slash_in_tag , character_after_slash ,
missing_end_tag ,
attribute_value_not_quoted , access_error , duplicate_attribute ,
attributes_in_end_tag ,
illegal_attribute , mandatory_attribute_missing ,
wrong_node_number ) ;
std : : unique_ptr < Node > checkChildren ( const xml : : Node & tpl ,
std : : unique_ptr < Node > node ,
std : : istream & is ) const
throw ( wrong_node_number ) ;
Tag tag ( std : : istream & is , const Node & position )
throw ( second_slash_in_tag , wrong_start_tag , character_after_slash ,
missing_end_tag , attributes_in_end_tag , tag_expected ,
attribute_value_not_quoted , access_error , duplicate_attribute ,
illegal_attribute , mandatory_attribute_missing ) ;
Node _template ;
unsigned long _line ;
long _open ;
} ;
//@}
/*! @defgroup serialization Class Serialization
@ section serIntro Introduction
Boost library ( http : //boost.org) offers a serialization framework,
which is able to serialize even complex class structures , you only
need to overwrite one or two serialization macros . The
disadvantages are that a lot of macros are needed and it becomes
quite complex as soon as you need inheritance . Also the generated
XML is not very enhanced , especially for Lists and
optional . Editing the boost serialization code by hand is a pain .
Classes could also be serialized using gSOAP ( http : //gsoap.sf.net)
which is designed for the SOA - Protocol . This serialization is much
more flexible , but it requires a pseudo C + + declaration and a C + +
parser / generator . Also it has very bad memory management , since it
is plain C internally .
Our requirements are :
- No precompiler , plain C + + .
- Automatic memory management .
- Nice looking XML code that is easy to edit manually .
- Good error messages ( exception ) in case of bad XML files .
- As few ugly overflow as possible .
@ section serActual Actual Status
The following member types are supported
- All built - in C + + types are supported , except enum
- @ c std : : string is supported
- Contained classes are supported
- Inheritance
- @ ref serContainer
@ todo The following will be supported soon ( ideas ) :
- choices ( one of )
- choices ( polymorfism )
- optional members ( pointer )
- enum ( class xml : : Enum )
Pointers cannot be stored .
@ section serBestPract Best Practice and Inheritance
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 .
@ 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
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 ( ) ;
Serialize ( const Serialize & other ) throw ( ) ;
virtual ~ Serialize ( ) ;
Serialize & operator = ( const Serialize & other ) throw ( ) ;
virtual Serialize & className ( const std : : string & name ) throw ( ) ;
Serialize & persist ( Serialize & member ,
const std : : string & name ) throw ( ) ;
Serialize & persist ( bool & member ,
const std : : string & name ) throw ( ) ;
Serialize & persist ( char & member ,
const std : : string & name ) throw ( ) ;
Serialize & persist ( unsigned char & member ,
const std : : string & name ) throw ( ) ;
Serialize & persist ( signed char & member ,
const std : : string & name ) throw ( ) ;
Serialize & persist ( unsigned short & member ,
const std : : string & name ) throw ( ) ;
Serialize & persist ( signed short & member ,
const std : : string & name ) throw ( ) ;
Serialize & persist ( unsigned int & member ,
const std : : string & name ) throw ( ) ;
Serialize & persist ( signed int & member ,
const std : : string & name ) throw ( ) ;
Serialize & persist ( unsigned long & member ,
const std : : string & name ) throw ( ) ;
Serialize & persist ( signed long & member ,
const std : : string & name ) throw ( ) ;
Serialize & persist ( float & member ,
const std : : string & name ) throw ( ) ;
Serialize & persist ( double & member ,
const std : : string & name ) throw ( ) ;
Serialize & persist ( std : : string & member ,
const std : : string & name ) throw ( ) ;
virtual std : : ostream & saveXml ( std : : ostream & os ,
const std : : string & name = std : : string ( ) )
const throw ( ) ;
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 ) ;
virtual void clear ( ) throw ( ) ;
protected :
virtual void initXmlMembers ( ) ;
void checkInit ( const Serialize * const ser = 0 ) const {
if ( ser ) {
if ( ! ser - > _xmlFactory ) const_cast < Serialize * > ( ser ) - > initXmlMembers ( ) ;
} else {
if ( ! _xmlFactory ) const_cast < Serialize * > ( this ) - > initXmlMembers ( ) ;
}
}
/*! @todo Why does @c protected: not work here?!? Children can't
access the members if they are protected ! */
public :
//! @cond INTERNAL
template < typename TYPE > friend bool assignFromNode ( Any member ,
const xml : : Node & node ) ;
template < typename TYPE > friend bool assigntoNode ( Any member ,
const xml : : Node & node ) ;
virtual bool optional ( ) const throw ( ) ;
void clear ( Any member ) throw ( ) ;
void reset ( ) throw ( ) ;
void copy ( const Serialize & o ) throw ( ) ;
template < typename TYPE >
Serialize & persistSimpleType ( TYPE & member ,
const std : : string & name ) throw ( ) {
_xmlNames [ name ] = & member ;
xml : : Node schema ( * _xmlFactory ) ;
schema < < xml : : String ( name ) . limits ( 1 , 1 ) ;
_xmlFactory = schema ;
return * this ;
}
virtual void fromNode ( Any member , const xml : : Node & node ) ;
virtual 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
} ;
template < class TYPE > class Optional : public Serialize {
public :
Optional ( ) throw ( ) : _valid ( false ) { }
Optional ( const Optional & o ) throw ( ) :
_member ( o . _member ) , _valid ( o . valid ) {
}
Optional ( const TYPE & mem ) throw ( ) :
_member ( mem ) , _valid ( true ) {
}
virtual ~ Optional ( ) throw ( ) { }
Optional & operator = ( const Optional & o ) throw ( ) {
_member = o . _member ;
_valid = o . _valid ;
return * this ;
}
Optional & operator = ( const TYPE & mem ) throw ( ) {
_member = mem ;
_valid = true ;
return * this ;
}
operator bool ( ) const throw ( ) {
return _valid ;
}
const TYPE & operator * ( ) const throw ( ) {
return _member ;
}
TYPE & operator * ( ) throw ( ) {
return _member ;
}
const TYPE * const operator - > ( ) const throw ( ) {
return & _member ;
}
TYPE * const operator - > ( ) throw ( ) {
return & _member ;
}
virtual void clear ( ) throw ( ) {
_valid = false ;
}
virtual Optional & className ( const std : : string & name ) throw ( ) {
if ( ! _xmlFactory ) {
Serialize : : className ( name ) ;
persist ( _member , name ) ;
// make the child the root, and it's optional
_xmlFactory = ( * _xmlFactory ) [ 0 ] ;
_xmlFactory - > limits ( 0 , 1 ) ;
}
return * this ;
}
protected :
virtual bool optional ( ) const throw ( ) {
return true ;
}
virtual void fromNode ( Any member , const xml : : Node & node ) {
_valid = true ;
Serialize : : fromNode ( Any ( & _member ) , node ) ;
}
virtual void toNode ( const Any member , xml : : Node & node ) const {
if ( ! _valid ) {
node . parent ( ) . remove ( node ) ;
return ;
}
const Any mem ( & const_cast < Optional * > ( this ) - > _member ) ;
Serialize : : toNode ( mem , node ) ;
}
private :
TYPE _member ;
bool _valid ;
} ;
//! @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
@ example optional_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 : : unique_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
this - > 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 : : unique_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 : : unique_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 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 : : unique_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
this - > 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 : : unique_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 : : unique_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 ) ;
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 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 : : unique_ptr < xml : : Node > node ( factory . read ( is ) ) ;
CONTAINER_TYPE : : clear ( ) ;
for ( xml : : Node : : size_type i ( 0 ) ; i < node - > children ( ) ; + + i ) {
typename CONTAINER_TYPE : : key_type key ;
typename CONTAINER_TYPE : : mapped_type data ;
Serialize : : fromNode ( & key , ( * node ) [ i ] ) ; // reads into tmp
Serialize : : fromNode ( & data , ( * node ) [ + + i ] ) ; // key&value
this - > insert ( typename CONTAINER_TYPE : : value_type ( key , data ) ) ;
}
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 : : unique_ptr < xml : : Node > tpl1 ( node [ 0 ] . clone ( ) ) ;
std : : unique_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 : : unique_ptr < xml : : Node > item1 ( tpl1 - > clone ( ) ) ;
Serialize : : toNode ( & key , * item1 ) ;
std : : unique_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