/*! @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 <list>
# include <set>
# include <map>
# include <memory>
# include <typeinfo>
# include <stdexcept>
# include <boost/any.hpp>
# 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 ) ; \
}
/*! @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 .
From the README file :
@ include README
@ page 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 " )
@ 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 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
@ example address . cxx */
/*! @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
nodes .
Then for every leaf xml : : Node name you will use , call
XML_NODE ( name ) and for every xml : : String call XML_STRING ( name ) .
For every node with children call XML_PARENT ( name , child1 , child2 . . . ) .
@ 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
@ 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 : : auto_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 . ) */
//@{
//! 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 .
@ 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 ) ; \
} \
}
//@}
//! Represents classes for handling C++ access to XML files with strict schema.
/*! The schema ist not presented through xsd, but it can be declared in C++.
A xml : : Factory represents a factory that owns a template and can
instanciate XML trees that are valid for the given template from
streams . If anything is not valid , an exception is thrown . The @ c
what ( ) method of the exception gives additional information about
the problem .
In the following example , we want to represent XML data that are
contained in a & 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 : : auto_ptr < xml : : Node > persons ( test . read ( std : : ifstream ( " file.xml)));
// Here we can be sure, that our structure is valid,
// but we must check optional elements before access, otherwise
// we get an exception.
[ . . . ]
for ( xml : : Node : : size_type i ( 0 ) ; i < persons . children ( ) ; + + i ) {
std : : cout < < " Person: " < < * persons [ i ] [ " name " ] ; // exception if no "name"
if ( persons [ i ] ( " friends " ) ) // check if "friends" is set
std : : cout < < " has " < < persons [ i ] [ " friends " ] . children ( ) < < " friends "
else
std : : cout < < " has no friend list " ;
std : : cout < < std : : endl ;
}
[ . . . ]
} catch ( const std : : exception & x ) {
std : : cerr < < " **** Error in file \" file.xml \" : " < < std : : endl
< < x . what ( ) < < std : : endl ;
}
@ endcode */
namespace xml {
//============================================================================
//! 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>
} ;
//! 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 ;
//----------------------------------------------------------------------------
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 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 ( ) ;
}
} ;
//============================================================================
//----------------------------------------------------------------------------
//! 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 ( ) ;
operator unsigned long ( ) 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.
/*! 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 : : auto_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 & 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 ( ) ;
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 : : auto_ptr < Node > clone ( Node * p ) const throw ( ) ;
Node ( ) ; // not implemented
Contents _contents ;
std : : string _name ;
Node * _parent ;
size_type _min ;
size_type _max ;
} ;
//----------------------------------------------------------------------------
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 : : auto_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 ;
} ;
//----------------------------------------------------------------------------
class UnsignedInteger : public String {
public :
UnsignedInteger ( std : : string name , unsigned long i = 0 ,
size_type min = 0 , size_type max = 0 ) throw ( ) ;
virtual std : : auto_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 ( ) ;
} ;
//----------------------------------------------------------------------------
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 ( ) ;
static std : : ostream & print ( std : : ostream & os , const Node & node ,
unsigned int level = 0 ) throw ( ) ;
std : : auto_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 ) ;
void reset ( ) throw ( ) ;
private :
friend class stream_error ;
friend class Serialize ;
Node & operator * ( ) throw ( factory_not_valid ) ;
Node * const operator - > ( ) throw ( factory_not_valid ) ;
bool ws ( char c ) throw ( ) ;
std : : auto_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 : : auto_ptr < Node > checkChildren ( const xml : : Node & tpl ,
std : : auto_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
- std : : string is supported
- Contained classes are supported
The following will be supported soon ( ideas ) :
- lists , maps
- inheritance
- choices ( one of )
- optional members ( pointer )
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 ;
} ;
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 ;
} ;
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
@ example serialization . cxx
@ example contain_serialization . cxx */
//@{
class Serialize {
public :
typedef bool ( * FromNodeFunc ) ( boost : : any , const xml : : Node & ) ;
typedef bool ( * ToNodeFunc ) ( const boost : : any , xml : : Node & ) ;
//! 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 ( ) ;
Serialize & className ( const std : : string & name ) throw ( ) ;
Serialize & persist ( Serialize & member ,
const std : : string & name ) throw ( ) ;
template < typename TYPE , class ALLOC >
Serialize & persist ( std : : list < TYPE , ALLOC > & member ,
const std : : string & name ) throw ( ) {
return persist ( member , name , name ) ;
}
template < typename TYPE , class ALLOC >
Serialize & persist ( std : : list < TYPE , ALLOC > & member ,
const std : : string & list ,
const std : : string & item ) throw ( ) {
_xmlNames [ list ] = & member ;
Serialize ser ( list ) ;
TYPE dummy ;
ser . persist ( dummy , item ) ;
* _xmlFactory < < ( xml : : Node ( list ) . limits ( 1 , 1 )
< < ( * ser . _xmlFactory ) [ 0 ] . limits ( 0 , 0 ) ) ;
return * this ;
}
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 ( ) ;
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 ( ) ) ;
std : : string schema ( ) const throw ( ) ;
static void registerFromNode ( FromNodeFunc fromNodeFunc ) ;
static void registerToNode ( ToNodeFunc toNodeFunc ) ;
protected :
virtual void initXmlMembers ( ) ;
private :
void clear ( ) 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 ;
}
void fromNode ( boost : : any member , const xml : : Node & node ) ;
void toNode ( const boost : : 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 )
. . .
} */
std : : map < std : : string , boost : : any > _xmlNames ;
xml : : Factory _xmlFactory ;
static std : : set < FromNodeFunc > _fromNode ;
static std : : set < ToNodeFunc > _toNode ;
} ;
//@}
}
# endif