start of xml::Optional

master
Marc Wäckerlin 16 years ago
parent 13c7266c9f
commit 6d7836fefd
  1. 1
      COPYING
  2. 11
      README
  3. 236
      doc/doxyfile.in
  4. 2
      doc/examples/address.cxx
  5. 2
      doc/examples/contain_serialization.cxx
  6. 6
      doc/examples/inherit_serialization.cxx
  7. 57
      doc/examples/list_serialization.cxx
  8. 2
      doc/examples/node_macros.cxx
  9. 2
      doc/examples/serialization.cxx
  10. 2
      src/makefile.am
  11. 181
      src/xml-cxx/any.hxx
  12. 900
      src/xml-cxx/xml.hxx
  13. 183
      src/xml.cxx
  14. 173
      test/container_serialization_test.cxx
  15. 3
      test/makefile.am

@ -1 +0,0 @@
/usr/share/automake-1.10/COPYING

@ -3,14 +3,19 @@ This is a C++ class for reading and writing XML structures.
All informaton can be found in the generated doxygen project documentation. All informaton can be found in the generated doxygen project documentation.
Rationale: The initial idea was to map C++ data structures to XML Rationale: The initial idea was to map C++ data structures to XML
files for configuration files that can easily be edited by hand. This files for configuration files that can easily be edited by hand.
library does not need any kind of C++ code parser or special pre
This library does not need any kind of C++ code parser or special pre
compiler. You can specify a schema entirly in native C++. The schema compiler. You can specify a schema entirly in native C++. The schema
is verified when XML is read and exceptions are thrown when the XML to is verified when XML is read and exceptions are thrown when the XML to
be pares is invalid. Exceptions specify exactly the location and be parse is invalid. Exceptions specify exactly the location and
reason of the problem, so that the editor of the XML file can easily reason of the problem, so that the editor of the XML file can easily
find and correct the problem. find and correct the problem.
C++ classes can inherit xml::Serialize and become serializable this
way. All you need to do is to overwrite one single method, where you
declare XML tag names for the class name and for all members.
(More rationale: See also "related Pages" in the doxygen project documentation) (More rationale: See also "related Pages" in the doxygen project documentation)
Structure of the files: Structure of the files:

@ -1,4 +1,4 @@
# Doxyfile 1.5.5 # Doxyfile 1.5.8
# This file describes the settings to be used by the documentation system # This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project # doxygen (www.doxygen.org) for a project
@ -57,8 +57,8 @@ CREATE_SUBDIRS = NO
# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, # Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek,
# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), # Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages),
# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, # Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish,
# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, # Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene,
# and Ukrainian. # Spanish, Swedish, and Ukrainian.
OUTPUT_LANGUAGE = English OUTPUT_LANGUAGE = English
@ -155,13 +155,6 @@ QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = YES MULTILINE_CPP_IS_BRIEF = YES
# If the DETAILS_AT_TOP tag is set to YES then Doxygen
# will output the detailed description near the top, like JavaDoc.
# If set to NO, the detailed description appears after the member
# documentation.
DETAILS_AT_TOP = YES
# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
# member inherits the documentation from any documented member that it # member inherits the documentation from any documented member that it
# re-implements. # re-implements.
@ -219,6 +212,17 @@ OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO OPTIMIZE_OUTPUT_VHDL = NO
# Doxygen selects the parser to use depending on the extension of the files it parses.
# With this tag you can assign which parser to use for a given extension.
# Doxygen has a built-in mapping, but you can override or extend it using this tag.
# The format is ext=language, where ext is a file extension, and language is one of
# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
# use: inc=Fortran f=C
EXTENSION_MAPPING =
# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
# to include (a tag file for) the STL sources as input, then you should # to include (a tag file for) the STL sources as input, then you should
# set this tag to YES in order to let doxygen match functions declarations and # set this tag to YES in order to let doxygen match functions declarations and
@ -226,7 +230,7 @@ OPTIMIZE_OUTPUT_VHDL = NO
# func(std::string) {}). This also make the inheritance and collaboration # func(std::string) {}). This also make the inheritance and collaboration
# diagrams that involve STL classes more complete and accurate. # diagrams that involve STL classes more complete and accurate.
BUILTIN_STL_SUPPORT = NO BUILTIN_STL_SUPPORT = YES
# If you use Microsoft's C++/CLI language, you should set this option to YES to # If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support. # enable parsing support.
@ -239,6 +243,15 @@ CPP_CLI_SUPPORT = NO
SIP_SUPPORT = NO SIP_SUPPORT = NO
# For Microsoft's IDL there are propget and propput attributes to indicate getter
# and setter methods for a property. Setting this option to YES (the default)
# will make doxygen to replace the get and set methods by a property in the
# documentation. This will only work if the methods are indeed getting or
# setting a simple type. If this is not the case, or you want to show the
# methods anyway, you should set this option to NO.
IDL_PROPERTY_SUPPORT = YES
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
# tag is set to YES, then doxygen will reuse the documentation of the first # tag is set to YES, then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default # member in the group (if any) for the other members of the group. By default
@ -264,6 +277,22 @@ SUBGROUPING = YES
TYPEDEF_HIDES_STRUCT = NO TYPEDEF_HIDES_STRUCT = NO
# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
# determine which symbols to keep in memory and which to flush to disk.
# When the cache is full, less often used symbols will be written to disk.
# For small to medium size projects (<1000 input files) the default value is
# probably good enough. For larger projects a too small cache size can cause
# doxygen to be busy swapping symbols to and from disk most of the time
# causing a significant performance penality.
# If the system has enough physical memory increasing the cache will improve the
# performance by keeping more symbols in memory. Note that the value works on
# a logarithmic scale so increasing the size by one will rougly double the
# memory usage. The cache size is given by this formula:
# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
# corresponding to a cache size of 2^16 = 65536 symbols
SYMBOL_CACHE_SIZE = 0
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Build related configuration options # Build related configuration options
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -340,7 +369,7 @@ HIDE_IN_BODY_DOCS = NO
# to NO (the default) then the documentation will be excluded. # to NO (the default) then the documentation will be excluded.
# Set it to YES to include the internal documentation. # Set it to YES to include the internal documentation.
INTERNAL_DOCS = NO INTERNAL_DOCS = YES
# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
# file names in lower-case letters. If set to YES upper-case letters are also # file names in lower-case letters. If set to YES upper-case letters are also
@ -448,6 +477,19 @@ SHOW_USED_FILES = YES
SHOW_DIRECTORIES = NO SHOW_DIRECTORIES = NO
# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
# This will remove the Files entry from the Quick Index and from the
# Folder Tree View (if specified). The default is YES.
SHOW_FILES = YES
# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
# Namespaces page.
# This will remove the Namespaces entry from the Quick Index
# and from the Folder Tree View (if specified). The default is YES.
SHOW_NAMESPACES = YES
# The FILE_VERSION_FILTER tag can be used to specify a program or script that # The FILE_VERSION_FILTER tag can be used to specify a program or script that
# doxygen should invoke to get the current version for each file (typically from # doxygen should invoke to get the current version for each file (typically from
# the version control system). Doxygen will invoke the program by executing (via # the version control system). Doxygen will invoke the program by executing (via
@ -458,6 +500,15 @@ SHOW_DIRECTORIES = NO
FILE_VERSION_FILTER = FILE_VERSION_FILTER =
# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
# doxygen. The layout file controls the global structure of the generated output files
# in an output format independent way. The create the layout file that represents
# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
# file name after the option, if omitted DoxygenLayout.xml will be used as the name
# of the layout file.
LAYOUT_FILE =
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# configuration options related to warning and progress messages # configuration options related to warning and progress messages
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -605,14 +656,17 @@ IMAGE_PATH =
# by executing (via popen()) the command <filter> <input-file>, where <filter> # by executing (via popen()) the command <filter> <input-file>, where <filter>
# is the value of the INPUT_FILTER tag, and <input-file> is the name of an # is the value of the INPUT_FILTER tag, and <input-file> is the name of an
# input file. Doxygen will then use the output that the filter program writes # input file. Doxygen will then use the output that the filter program writes
# to standard output. If FILTER_PATTERNS is specified, this tag will be # to standard output.
# If FILTER_PATTERNS is specified, this tag will be
# ignored. # ignored.
INPUT_FILTER = INPUT_FILTER =
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
# basis. Doxygen will compare the file name with each pattern and apply the # basis.
# filter if there is a match. The filters are a list of the form: # Doxygen will compare the file name with each pattern and apply the
# filter if there is a match.
# The filters are a list of the form:
# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
# is applied to all files. # is applied to all files.
@ -647,13 +701,13 @@ INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = NO STRIP_CODE_COMMENTS = NO
# If the REFERENCED_BY_RELATION tag is set to YES (the default) # If the REFERENCED_BY_RELATION tag is set to YES
# then for each documented function all documented # then for each documented function all documented
# functions referencing it will be listed. # functions referencing it will be listed.
REFERENCED_BY_RELATION = YES REFERENCED_BY_RELATION = YES
# If the REFERENCES_RELATION tag is set to YES (the default) # If the REFERENCES_RELATION tag is set to YES
# then for each documented function all documented entities # then for each documented function all documented entities
# called/used by that function will be listed. # called/used by that function will be listed.
@ -662,7 +716,8 @@ REFERENCES_RELATION = YES
# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
# link to the source code. Otherwise they will link to the documentstion. # link to the source code.
# Otherwise they will link to the documentation.
REFERENCES_LINK_SOURCE = YES REFERENCES_LINK_SOURCE = YES
@ -751,12 +806,13 @@ HTML_STYLESHEET =
HTML_ALIGN_MEMBERS = YES HTML_ALIGN_MEMBERS = YES
# If the GENERATE_HTMLHELP tag is set to YES, additional index files # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# will be generated that can be used as input for tools like the # documentation will contain sections that can be hidden and shown after the
# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # page has loaded. For this to work a browser that supports
# of the generated HTML documentation. # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
GENERATE_HTMLHELP = NO HTML_DYNAMIC_SECTIONS = YES
# If the GENERATE_DOCSET tag is set to YES, additional index files # If the GENERATE_DOCSET tag is set to YES, additional index files
# will be generated that can be used as input for Apple's Xcode 3 # will be generated that can be used as input for Apple's Xcode 3
@ -766,6 +822,7 @@ GENERATE_HTMLHELP = NO
# directory and running "make install" will install the docset in # directory and running "make install" will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
# it at startup. # it at startup.
# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
GENERATE_DOCSET = NO GENERATE_DOCSET = NO
@ -783,13 +840,12 @@ DOCSET_FEEDNAME = "Doxygen generated docs"
DOCSET_BUNDLE_ID = org.doxygen.Project DOCSET_BUNDLE_ID = org.doxygen.Project
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # If the GENERATE_HTMLHELP tag is set to YES, additional index files
# documentation will contain sections that can be hidden and shown after the # will be generated that can be used as input for tools like the
# page has loaded. For this to work a browser that supports # Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # of the generated HTML documentation.
# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
HTML_DYNAMIC_SECTIONS = NO GENERATE_HTMLHELP = NO
# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
# be used to specify the file name of the resulting .chm file. You # be used to specify the file name of the resulting .chm file. You
@ -811,6 +867,12 @@ HHC_LOCATION =
GENERATE_CHI = NO GENERATE_CHI = NO
# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
# is used to encode HtmlHelp index (hhk), content (hhc) and project file
# content.
CHM_INDEX_ENCODING =
# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
# controls whether a binary table of contents is generated (YES) or a # controls whether a binary table of contents is generated (YES) or a
# normal table of contents (NO) in the .chm file. # normal table of contents (NO) in the .chm file.
@ -822,6 +884,55 @@ BINARY_TOC = NO
TOC_EXPAND = NO TOC_EXPAND = NO
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
# are set, an additional index file will be generated that can be used as input for
# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
# HTML documentation.
GENERATE_QHP = NO
# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
# be used to specify the file name of the resulting .qch file.
# The path specified is relative to the HTML output folder.
QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating
# Qt Help Project output. For more information please see
# http://doc.trolltech.com/qthelpproject.html#namespace
QHP_NAMESPACE =
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
# Qt Help Project output. For more information please see
# http://doc.trolltech.com/qthelpproject.html#virtual-folders
QHP_VIRTUAL_FOLDER = doc
# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
# For more information please see
# http://doc.trolltech.com/qthelpproject.html#custom-filters
QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
# filter section matches.
# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
QHP_SECT_FILTER_ATTRS =
# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
# be used to specify the location of Qt's qhelpgenerator.
# If non-empty doxygen will try to run qhelpgenerator on the generated
# .qhp file.
QHG_LOCATION =
# The DISABLE_INDEX tag can be used to turn on/off the condensed index at # The DISABLE_INDEX tag can be used to turn on/off the condensed index at
# top of each HTML page. The value NO (the default) enables the index and # top of each HTML page. The value NO (the default) enables the index and
# the value YES disables it. # the value YES disables it.
@ -833,14 +944,22 @@ DISABLE_INDEX = NO
ENUM_VALUES_PER_LINE = 4 ENUM_VALUES_PER_LINE = 4
# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
# generated containing a tree-like index structure (just like the one that # structure should be generated to display hierarchical information.
# If the tag value is set to FRAME, a side panel will be generated
# containing a tree-like index structure (just like the one that
# is generated for HTML Help). For this to work a browser that supports # is generated for HTML Help). For this to work a browser that supports
# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
# probably better off using the HTML help feature. # probably better off using the HTML help feature. Other possible values
# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
# and Class Hierarchy pages using a tree view instead of an ordered list;
# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
# disables this behavior completely. For backwards compatibility with previous
# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
# respectively.
GENERATE_TREEVIEW = NO GENERATE_TREEVIEW = YES
# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
# used to set the initial width (in pixels) of the frame in which the tree # used to set the initial width (in pixels) of the frame in which the tree
@ -848,6 +967,14 @@ GENERATE_TREEVIEW = NO
TREEVIEW_WIDTH = 250 TREEVIEW_WIDTH = 250
# Use this tag to change the font size of Latex formulas included
# as images in the HTML documentation. The default is 10. Note that
# when you change the font size after a successful doxygen run you need
# to manually remove any form_*.png images from the HTML output directory
# to force them to be regenerated.
FORMULA_FONTSIZE = 10
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# configuration options related to the LaTeX output # configuration options related to the LaTeX output
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -1060,8 +1187,10 @@ GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO PERLMOD_LATEX = NO
# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
# nicely formatted so it can be parsed by a human reader. This is useful # nicely formatted so it can be parsed by a human reader.
# if you want to understand what is going on. On the other hand, if this # This is useful
# if you want to understand what is going on.
# On the other hand, if this
# tag is set to NO the size of the Perl module output will be much smaller # tag is set to NO the size of the Perl module output will be much smaller
# and Perl will parse it just the same. # and Perl will parse it just the same.
@ -1148,8 +1277,10 @@ SKIP_FUNCTION_MACROS = YES
# Optionally an initial location of the external documentation # Optionally an initial location of the external documentation
# can be added for each tagfile. The format of a tag file without # can be added for each tagfile. The format of a tag file without
# this location is as follows: # this location is as follows:
#
# TAGFILES = file1 file2 ... # TAGFILES = file1 file2 ...
# Adding location for the tag files is done as follows: # Adding location for the tag files is done as follows:
#
# TAGFILES = file1=loc1 "file2 = loc2" ... # TAGFILES = file1=loc1 "file2 = loc2" ...
# where "loc1" and "loc2" can be relative or absolute paths or # where "loc1" and "loc2" can be relative or absolute paths or
# URLs. If a location is present for each tag, the installdox tool # URLs. If a location is present for each tag, the installdox tool
@ -1218,6 +1349,29 @@ HIDE_UNDOC_RELATIONS = NO
HAVE_DOT = @HAVE_DOT@ HAVE_DOT = @HAVE_DOT@
# By default doxygen will write a font called FreeSans.ttf to the output
# directory and reference it in all dot files that doxygen generates. This
# font does not include all possible unicode characters however, so when you need
# these (or just want a differently looking font) you can specify the font name
# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
# which can be done by putting it in a standard location or by setting the
# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
# containing the font.
DOT_FONTNAME = FreeSans
# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
# The default size is 10pt.
DOT_FONTSIZE = 10
# By default doxygen will tell dot to use the output directory to look for the
# FreeSans.ttf font (which doxygen will put there itself). If you specify a
# different font using DOT_FONTNAME you can set the path where dot
# can find it using this tag.
DOT_FONTPATH =
# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and # will generate a graph for each documented class showing the direct and
# indirect inheritance relations. Setting this tag to YES will force the # indirect inheritance relations. Setting this tag to YES will force the
@ -1307,7 +1461,7 @@ DOT_PATH =
DOTFILE_DIRS = DOTFILE_DIRS =
# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
# nodes that will be shown in the graph. If the number of nodes in a graph # nodes that will be shown in the graph. If the number of nodes in a graph
# becomes larger than this value, doxygen will truncate the graph, which is # becomes larger than this value, doxygen will truncate the graph, which is
# visualized by representing a node as a red box. Note that doxygen if the # visualized by representing a node as a red box. Note that doxygen if the
@ -1328,10 +1482,10 @@ DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0 MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
# background. This is enabled by default, which results in a transparent # background. This is disabled by default, because dot on Windows does not
# background. Warning: Depending on the platform used, enabling this option # seem to support this out of the box. Warning: Depending on the platform used,
# may lead to badly anti-aliased labels on the edges of a graph (i.e. they # enabling this option may lead to badly anti-aliased labels on the edges of
# become hard to read). # a graph (i.e. they become hard to read).
DOT_TRANSPARENT = YES DOT_TRANSPARENT = YES
@ -1355,7 +1509,7 @@ GENERATE_LEGEND = YES
DOT_CLEANUP = YES DOT_CLEANUP = YES
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration::additions related to the search engine # Options related to the search engine
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# The SEARCHENGINE tag specifies whether or not a search engine should be # The SEARCHENGINE tag specifies whether or not a search engine should be

@ -5,8 +5,6 @@
// 1 2 3 4 5 6 7 8 // 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890 // 45678901234567890123456789012345678901234567890123456789012345678901234567890
// g++ -I../../src ../../src/xml.cxx address.cxx
#include <xml-cxx/xml.hxx> #include <xml-cxx/xml.hxx>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>

@ -5,8 +5,6 @@
// 1 2 3 4 5 6 7 8 // 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890 // 45678901234567890123456789012345678901234567890123456789012345678901234567890
// g++ -I../../src ../../src/xml.cxx contain_serialization.cxx
#include <xml-cxx/xml.hxx> #include <xml-cxx/xml.hxx>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>

@ -5,8 +5,6 @@
// 1 2 3 4 5 6 7 8 // 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890 // 45678901234567890123456789012345678901234567890123456789012345678901234567890
// g++ -I../../src ../../src/xml.cxx inherit_serialization.cxx
#include <xml-cxx/xml.hxx> #include <xml-cxx/xml.hxx>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
@ -23,12 +21,12 @@ class A: public xml::Serialize {
} }
}; };
class B: public A { class B: public A { // A inherits xml::Serialize, B inherits A
public: public:
int b; int b;
protected: protected:
void initXmlMembers() { void initXmlMembers() {
A::initXmlMembers(); A::initXmlMembers(); // <- Here is the important difference
className("B"); className("B");
persist(b, "b"); persist(b, "b");
} }

@ -5,8 +5,6 @@
// 1 2 3 4 5 6 7 8 // 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890 // 45678901234567890123456789012345678901234567890123456789012345678901234567890
// g++ -I../../src ../../src/xml.cxx list_serialization.cxx
#include <xml-cxx/xml.hxx> #include <xml-cxx/xml.hxx>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
@ -37,26 +35,57 @@ class B: public xml::Serialize {
}; };
int main(int, char**) { int main(int, char**) {
std::stringstream ss("<B>\n" std::stringstream ss("<B>"
" <b>1234</b>" " <b>1234</b>"
"<a><list><item>guguseli</item></list></a>" " <a>"
" <list>"
" <item>guguseli</item>"
" </list>"
" </a>"
" <As>" " <As>"
"<A>\n" " <A>"
"\t<list>\n" " <list>"
"\t\t<item>Hello</item>\n" " <item>Hello</item>"
"\t\t<item>World</item>\n" " <item>World</item>"
"\t\t<item>how</item>\n" " <item>how</item>"
"\t\t<item>are</item>\n" " <item>are</item>"
"\t\t<item>you</item>\n" " <item>you</item>"
"\t</list>\n" " </list>"
" </A>"
" <A>"
" <list>"
" <item>a</item>"
" <item>b</item>"
" <item>c</item>"
" <item>d</item>"
" <item>e</item>"
" </list>"
" </A>"
" <A>"
" <list>"
" <item>f</item>"
" <item>g</item>"
" <item>h</item>"
" <item>i</item>"
" <item>j</item>"
" </list>"
" </A>"
" <A>"
" <list>"
" <item>k</item>"
" <item>l</item>"
" <item>m</item>"
" <item>n</item>"
" <item>o</item>"
" </list>"
" </A>" " </A>"
" </As>" " </As>"
"</B>"); "</B>");
B b; B b;
std::cout<<"SCHEMA:"<<std::endl<<b.schema()<<std::endl; std::cout<<"SCHEMA:"<<std::endl<<b.schema()<<std::endl;
b.loadXml(ss); b.loadXml(ss);
// if (a.list.front()=="Hello") a.list.front()="Good Bye"; if (b.as.front().list.front()=="Hello") b.as.front().list.front()="Good Bye";
// if (a.list.back()=="you") a.list.back()="we"; if (b.as.front().list.back()=="you") b.as.front().list.back()="we";
b.saveXml(std::cout)<<std::endl; b.saveXml(std::cout)<<std::endl;
return 0; return 0;
} }

@ -5,8 +5,6 @@
// 1 2 3 4 5 6 7 8 // 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890 // 45678901234567890123456789012345678901234567890123456789012345678901234567890
// g++ -I../../src ../../src/xml.cxx node_macros.cxx
#include <xml-cxx/xml.hxx> #include <xml-cxx/xml.hxx>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>

@ -5,8 +5,6 @@
// 1 2 3 4 5 6 7 8 // 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890 // 45678901234567890123456789012345678901234567890123456789012345678901234567890
// g++ -I../../src ../../src/xml.cxx serialization.cxx
#include <xml-cxx/xml.hxx> #include <xml-cxx/xml.hxx>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>

@ -7,7 +7,7 @@
AM_CXXFLAGS += -I ${top_srcdir}/src AM_CXXFLAGS += -I ${top_srcdir}/src
lib_LTLIBRARIES = libxml-cxx.la lib_LTLIBRARIES = libxml-cxx.la
nobase_include_HEADERS = xml-cxx/xml.hxx nobase_include_HEADERS = xml-cxx/xml.hxx xml-cxx/any.hxx
libxml_cxx_la_SOURCES = xml.cxx libxml_cxx_la_SOURCES = xml.cxx

@ -19,107 +19,158 @@
namespace xml { namespace xml {
class Any { class Serialize;
public: // structors //! @cond INTERNAL
Any(): content(0) {} //! @addtogroup serialization
//@{
template<typename ValueType> Any(const ValueType& value):
content(new holder<ValueType>(value)) { /*! @defgroup xmlAny (internal) Any Object with limited Polymorfism
@internal The implementation of xml::Any is an adaption of @c
boost::any (see http://www.boost.org), but instead of
holding any element, but without polymorfism, the
implementation of xml::Any only holds basic C++ types
(@c int, @c long, @c float, ...), plus @c std::string
and xml::Serialize. All other non-standard-C++ types
are stored as xml::Serialize*. This way, we can get a
polymorfic any type, but only for children of
xml::Serialize, but in fact, that's all we
need. Another limitation is, that xml::Any only stores
pointer types. Simply because we don't need more. */
//@{
//! @internal Number of supported types that can be stored as XML.
const int MAX_NUM(14);
template<int NUM> struct ToType {typedef Serialize Type;};
template<> struct ToType<1> {typedef Serialize Type;};
template<> struct ToType<2> {typedef std::string Type;};
template<> struct ToType<3> {typedef bool Type;};
template<> struct ToType<4> {typedef unsigned char Type;};
template<> struct ToType<5> {typedef signed char Type;};
template<> struct ToType<6> {typedef char Type;};
template<> struct ToType<7> {typedef unsigned short Type;};
template<> struct ToType<8> {typedef signed short Type;};
template<> struct ToType<9> {typedef unsigned int Type;};
template<> struct ToType<10> {typedef signed int Type;};
template<> struct ToType<11> {typedef unsigned long Type;};
template<> struct ToType<12> {typedef signed long Type;};
template<> struct ToType<13> {typedef float Type;};
template<> struct ToType<14> {typedef double Type;};
template<typename T> struct ToNum {static const int NUM = 1;};
template<> struct ToNum<Serialize > {static const int NUM = 1;};
template<> struct ToNum<std::string > {static const int NUM = 2;};
template<> struct ToNum<bool > {static const int NUM = 3;};
template<> struct ToNum<unsigned char > {static const int NUM = 4;};
template<> struct ToNum<signed char > {static const int NUM = 5;};
template<> struct ToNum< char > {static const int NUM = 6;};
template<> struct ToNum<unsigned short> {static const int NUM = 7;};
template<> struct ToNum<signed short> {static const int NUM = 8;};
template<> struct ToNum<unsigned int > {static const int NUM = 9;};
template<> struct ToNum<signed int > {static const int NUM = 10;};
template<> struct ToNum<unsigned long > {static const int NUM = 11;};
template<> struct ToNum<signed long > {static const int NUM = 12;};
template<> struct ToNum<float > {static const int NUM = 13;};
template<> struct ToNum<double > {static const int NUM = 14;};
template<typename T> bool isSerialize() {
return ToNum<T>::NUM == 1;
} }
Any(const Any& other): content(other.content?other.content->clone():0) {} template <typename T, bool GOOD=(ToNum<T>::NUM==1)> struct Mapper {
static Serialize* toSerialize(T& obj) {
return 0;
}
};
template <typename T> struct Mapper<T, true> {
static Serialize* toSerialize(T& obj) {
return dynamic_cast<Serialize*>(&obj);
}
};
class Any {
public:
Any(): _content(0) {}
template<typename TYPE> Any(TYPE* value):
_content(new Holder<TYPE>(value)) {
}
Any(const Any& other):
_content(other._content?other._content->clone():0) {
}
~Any() { ~Any() {
delete content; delete _content;
} }
public: // modifiers
Any& swap(Any& rhs) { Any& swap(Any& rhs) {
std::swap(content, rhs.content); std::swap(_content, rhs._content);
return *this; return *this;
} }
template<typename TYPE> Any& operator=(TYPE* rhs) {
template<typename ValueType> Any& operator=(const ValueType& rhs) {
Any(rhs).swap(*this); Any(rhs).swap(*this);
return *this; return *this;
} }
Any& operator=(const Any& rhs) { Any& operator=(const Any& rhs) {
Any(rhs).swap(*this); Any(rhs).swap(*this);
return *this; return *this;
} }
public: // queries
bool empty() const { bool empty() const {
return !content; return !_content;
} }
const std::type_info& type() const { const std::type_info& type() const {
return content?content->type():typeid(void); return _content?_content->type():typeid(void);
} }
class Placeholder {
private: // types public:
virtual ~Placeholder() {}
class placeholder {
public: // structors
virtual ~placeholder() {}
virtual const std::type_info& type() const = 0; virtual const std::type_info& type() const = 0;
virtual placeholder* clone() const = 0; virtual Placeholder* clone() const = 0;
}; };
template<typename TYPE> class Holder: public Placeholder {
template<typename ValueType> class holder: public placeholder {
public: public:
Holder(typename ToType<ToNum<TYPE>::NUM>::Type* value):
holder(const ValueType& value): held(value) {} _held(value) {
}
virtual const std::type_info& type() const { virtual const std::type_info& type() const {
return typeid(ValueType); return typeid(typename ToType<ToNum<TYPE>::NUM>::Type);
} }
virtual Placeholder* clone() const {
virtual placeholder* clone() const { return new Holder(_held);
return new holder(held);
} }
typename ToType<ToNum<TYPE>::NUM>::Type* _held;
ValueType held;
}; };
private:
private: // representation template<typename TYPE> friend TYPE* any_cast(Any*);
template<typename TYPE> friend TYPE* any_cast(Any&);
template<typename ValueType> friend ValueType* any_cast(Any*); Placeholder* _content;
template<typename ValueType> friend ValueType& any_cast(Any&);
placeholder* content;
}; };
template<typename TYPE> TYPE* any_cast(Any* operand) {
template<typename ValueType> ValueType* any_cast(Any* operand) { return operand && operand->type() == typeid(TYPE)
return operand && operand->type() == typeid(ValueType) ? static_cast<Any::Holder<TYPE>*>(operand->_content)->_held
? &static_cast<Any::holder<ValueType>*>(operand->content)->held
: 0; : 0;
} }
template<typename TYPE> const TYPE* any_cast(const Any* operand) {
template<typename ValueType> const ValueType* any_cast(const Any* operand) { return any_cast<TYPE>(const_cast<Any*>(operand));
return any_cast<ValueType>(const_cast<Any*>(operand));
} }
template<typename TYPE> TYPE* any_cast(Any& operand) {
template<typename ValueType> ValueType& any_cast(Any& operand) { if (operand.type()==typeid(TYPE))
if (operand.type()==typeid(ValueType)) return static_cast<Any::Holder<TYPE>*>(operand._content)->_held;
return static_cast<Any::holder<ValueType>*>(operand.content)->held;
throw std::bad_cast(); throw std::bad_cast();
} }
template<typename TYPE> const TYPE* any_cast(const Any& operand) {
template<typename ValueType> const ValueType& any_cast(const Any& operand) { return any_cast<TYPE>(const_cast<Any&>(operand));
return any_cast<ValueType>(const_cast<Any&>(operand));
} }
//@}
//@}
//! @endcond INTERNAL
} }
// Note for this file only (this file is in large parts taken from boost):
// Copyright Kevlin Henney, 2000, 2001, 2002. All rights reserved. // Copyright Kevlin Henney, 2000, 2001, 2002. All rights reserved.
// //
// Distributed under the Boost Software License, Version 1.0. (See // Distributed under the Boost Software License, Version 1.0. (See

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

@ -718,76 +718,48 @@ namespace xml {
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
//! To instanciate a factory, a template must be given. //! To instanciate a factory, a template must be given.
/*! The template is a 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.
E.g. to load an address, that contains a tag &lt;address&gt;
with at least a name and optional an address in it's body, you
may write:
@code
xml::Factory addrTpl(xml::Node("address")
(<<xml::Node("name").limit(1, 1)
(<<xml::String("first").limit(1, 1)
<<xml::String("middle") // 0..n -> .limit(0, 0)
<<xml::String("last").limit(1, 1))
<<(xml::Node("location").max(1)
<<xml::String("line").min(1))
<<xml::String("country").max(1)));
@endcode
According to this example, a valid XML file could be:
@verbatim
<address>
<name>
<first>Marc</first>
<middle>Roman</middle>
<last>Wäckerlin</last>
</name>
<location>
<line>SwissSign AG</line>
<line>Pfingstweidstrasse 60b</line>
<line>8005 Zürich</line>
</location>
<country>Schweiz</country>
</address>
@endverbatim */
Factory::Factory(const Node& t) throw(): Factory::Factory(const Node& t) throw():
_template(xml::Node("<xml::start>")<<t), _line(0) { _template(xml::Node("<xml::start>")<<t), _line(0) {
_template[0].min(1); _template[0].min(1);
_template[0].max(1); _template[0].max(1);
} }
//! Instanciates an invalid factory. A template must be assigned later.
Factory::Factory() throw(): Factory::Factory() throw():
_template(xml::Node("<xml::start>")), _line(0) { _template(xml::Node("<xml::start>")), _line(0) {
} }
//! Assign a template.
/*! If you don't pass a template at instanciation, you must call
this method later, or you will get xml::factory_not_valid
exceptions when you try to use the factory. */
Factory& Factory::operator=(const Node& t) throw() { Factory& Factory::operator=(const Node& t) throw() {
_template = xml::Node("<xml::start>")<<t; _template = xml::Node("<xml::start>")<<t;
_template[0].min(1); _template[0].min(1);
_template[0].max(1); _template[0].max(1);
} }
//! Get the template.
const Node& Factory::operator*() const throw(factory_not_valid) try { const Node& Factory::operator*() const throw(factory_not_valid) try {
return _template[0]; return _template[0];
} catch (...) { } catch (...) {
throw factory_not_valid(); throw factory_not_valid();
} }
//! Access the (root node of the) template.
const Node*const Factory::operator->() const throw(factory_not_valid) try { const Node*const Factory::operator->() const throw(factory_not_valid) try {
return &_template[0]; return &_template[0];
} catch (...) { } catch (...) {
throw factory_not_valid(); throw factory_not_valid();
} }
//! Check whether the factory has been given a valid template.
Factory::operator bool() const throw() { Factory::operator bool() const throw() {
return _template.children()>0; return _template.children()>0;
} }
std::ostream& operator<<(std::ostream& os, const Factory& factory) throw() { //! Print the factory template's schema in human readable format.
/*! Calls xml::Factory::print */
std::ostream& operator<<(std::ostream& os, const Factory& factory)
throw(factory_not_valid) {
return factory.print(os, *factory); return factory.print(os, *factory);
} }
//! Print a node's schema description in human readable format.
/*! @todo May be changed to a XML Schema output later. */
std::ostream& Factory::print(std::ostream& os, const Node& node, std::ostream& Factory::print(std::ostream& os, const Node& node,
unsigned int level) throw() { unsigned int level) throw() {
if (node.children()) { if (node.children()) {
@ -842,6 +814,7 @@ namespace xml {
} }
return os; return os;
} }
//! Restore a xml::Node tree from a stream, according to the given schema.
std::auto_ptr<Node> Factory::read(std::istream& is) std::auto_ptr<Node> Factory::read(std::istream& is)
throw(wrong_end_tag, wrong_start_tag, tag_expected, type_mismatch, throw(wrong_end_tag, wrong_start_tag, tag_expected, type_mismatch,
second_slash_in_tag, second_slash_in_tag,
@ -849,17 +822,21 @@ namespace xml {
access_error, duplicate_attribute, access_error, duplicate_attribute,
attributes_in_end_tag, attributes_in_end_tag,
illegal_attribute, mandatory_attribute_missing, illegal_attribute, mandatory_attribute_missing,
wrong_node_number) try { wrong_node_number, factory_not_valid) {
if (_template.children()==0) throw factory_not_valid();
try {
_line=1; _line=1;
_open=0; _open=0;
std::auto_ptr<Node> node(read(is, _template)); std::auto_ptr<Node> node(read(is, _template));
if (node->children()==0) if (node->children()==0)
throw tag_expected(_template[0], "nothing found, possibly empty stream"); throw tag_expected(_template[0],
"nothing found, possibly empty stream");
return (*node)[0].clone(); return (*node)[0].clone();
} catch (exception& e) { } catch (exception& e) {
e.line(_line); e.line(_line);
throw; throw;
} }
}
void Factory::reset() throw() { void Factory::reset() throw() {
_line = 0; _line = 0;
_open = 0; _open = 0;
@ -1124,7 +1101,10 @@ namespace xml {
for (std::map<std::string, Any>::const_iterator for (std::map<std::string, Any>::const_iterator
it(_xmlNames.begin()); it(_xmlNames.begin());
it!=_xmlNames.end(); ++it) it!=_xmlNames.end(); ++it)
if ((*node)(it->first))
fromNode(it->second, (*node)[it->first]); fromNode(it->second, (*node)[it->first]);
else
clear(it->second);
return is; return is;
} }
std::string Serialize::schema() const throw() { std::string Serialize::schema() const throw() {
@ -1137,10 +1117,18 @@ namespace xml {
void Serialize::registerFromNode(Serialize::FromNodeFunc fromNodeFunc) { void Serialize::registerFromNode(Serialize::FromNodeFunc fromNodeFunc) {
_fromNode.insert(fromNodeFunc); _fromNode.insert(fromNodeFunc);
} }
void Serialize::registerToNode(Serialize::ToNodeFunc toNodeFunc) { void Serialize::registerToNode(Serialize::ToNodeFunc toNodeFunc) {
_toNode.insert(toNodeFunc); _toNode.insert(toNodeFunc);
} }
void Serialize::registerClear(Serialize::ClearFunc clearFunc) {
_clear.insert(clearFunc);
}
void Serialize::clear() throw() {
for (std::map<std::string, Any>::const_iterator
it(_xmlNames.begin());
it!=_xmlNames.end(); ++it)
clear(it->second);
}
void Serialize::initXmlMembers() {} void Serialize::initXmlMembers() {}
void Serialize::reset() throw() { void Serialize::reset() throw() {
_xmlFactory.reset(); _xmlFactory.reset();
@ -1154,127 +1142,116 @@ namespace xml {
for (std::set<FromNodeFunc>::const_iterator it(_fromNode.begin()); for (std::set<FromNodeFunc>::const_iterator it(_fromNode.begin());
it!=_fromNode.end(); ++it) it!=_fromNode.end(); ++it)
if ((**it)(member, node)) return; // found match if ((**it)(member, node)) return; // found match
/*
if (!RealType<member>::isSerialize())
*/
throw type_not_registered(member.type().name(), node.name(), node); throw type_not_registered(member.type().name(), node.name(), node);
/*
Serialize* ser(RealType<TYPE>::serialize(member, 0));
// from assignFromNode<Serialize>
//! @todo improve this (inefficient)
std::stringstream ss; // simple but inefficient: rewrite and reread
ss<<node;
ser->loadXml(ss, node.name());
*/
} }
void Serialize::toNode(const Any member, xml::Node& node) const { void Serialize::toNode(const Any member, xml::Node& node) const {
for (std::set<ToNodeFunc>::const_iterator it(_toNode.begin()); for (std::set<ToNodeFunc>::const_iterator it(_toNode.begin());
it!=_toNode.end(); ++it) it!=_toNode.end(); ++it)
if ((**it)(member, node)) return; // found match if ((**it)(member, node)) return; // found match
/*
if (!RealType<TYPE>::isSerialize())
*/
throw type_not_registered(member.type().name(), node.name(), node); throw type_not_registered(member.type().name(), node.name(), node);
/* }
// from assignToNode<Serialize> void Serialize::clear(Any member) throw() {
std::stringstream ss; for (std::set<ClearFunc>::const_iterator it(_clear.begin());
((Serialize*)member)->saveXml(ss, node.name()); it!=_clear.end(); ++it)
xml::Factory factory(node); if ((**it)(member)) return; // found match
node = *factory.read(ss); throw type_not_registered(member.type().name());
*/
} }
std::set<Serialize::FromNodeFunc> Serialize::_fromNode; std::set<Serialize::FromNodeFunc> Serialize::_fromNode;
std::set<Serialize::ToNodeFunc> Serialize::_toNode; std::set<Serialize::ToNodeFunc> Serialize::_toNode;
std::set<Serialize::ClearFunc> Serialize::_clear;
//=============================================================== Assign Types //=============================================================== Assign Types
template<typename TYPE> bool assignFromNode(Any member, template<typename TYPE> bool assignFromNode(Any member,
const xml::Node& node) { const xml::Node& node) {
if (!any_cast<TYPE*>(&member)) return false; if (!any_cast<TYPE>(&member)) return false;
*any_cast<TYPE*>(member) = *any_cast<TYPE>(member) =
(TYPE)dynamic_cast<const xml::String&>(node); (TYPE)dynamic_cast<const xml::String&>(node);
return true; return true;
} }
template<typename TYPE> bool assignToNode(const Any member, template<typename TYPE> bool assignToNode(const Any member,
xml::Node& node) { xml::Node& node) {
if (!any_cast<TYPE*>(&member)) return false; if (!any_cast<TYPE>(&member)) return false;
std::stringstream ss; std::stringstream ss;
ss<<*any_cast<TYPE*>(member); ss<<*any_cast<TYPE>(member);
node.text(ss.str()); node.text(ss.str());
return true; return true;
} }
template<> bool assignFromNode<bool>(Any member, template<> bool assignFromNode<bool>(Any member,
const xml::Node& node) { const xml::Node& node) {
if (member.type()!=typeid(bool*)) return false; if (member.type()!=typeid(bool)) return false;
std::string s(*dynamic_cast<const xml::String&>(node)); std::string s(*dynamic_cast<const xml::String&>(node));
std::string::size_type std::string::size_type
start(s.find_first_not_of(" \t\n\r")), start(s.find_first_not_of(" \t\n\r")),
end(s.find_last_not_of(" \t\n\r")); end(s.find_last_not_of(" \t\n\r"));
if (start==std::string::npos) { if (start==std::string::npos) {
*any_cast<bool*>(member) = false; *any_cast<bool>(member) = false;
} else { } else {
s = s.substr(start, end-start+1); s = s.substr(start, end-start+1);
*any_cast<bool*>(member) = *any_cast<bool>(member) =
s!="0" && s!="false" && s!="no" && s!="off"; s!="0" && s!="false" && s!="no" && s!="off";
} }
return true; return true;
} }
template<> bool assignToNode<bool>(const Any member, template<> bool assignToNode<bool>(const Any member,
xml::Node& node) { xml::Node& node) {
if (member.type()!=typeid(bool*)) return false; if (member.type()!=typeid(bool)) return false;
node.text(*any_cast<bool*>(member)?"true":"false"); node.text(*any_cast<bool>(member)?"true":"false");
return true; return true;
} }
template<> bool assignFromNode<Serialize>(Any member, template<> bool assignFromNode<Serialize>(Any member,
const xml::Node& node) { const xml::Node& node) {
if (!any_cast<Serialize*>(&member)) return false; if (!any_cast<Serialize>(&member)) return false;
//! @todo improve this (inefficient) //! @todo improve this (inefficient)
std::stringstream ss; // simple but inefficient: rewrite and reread std::stringstream ss; // simple but inefficient: rewrite and reread
ss<<node; ss<<node;
any_cast<Serialize*>(member)->loadXml(ss, node.name()); any_cast<Serialize>(member)->loadXml(ss, node.name());
/* /*
Serialize* ser(any_cast<Serialize*>(member)); Serialize* ser(any_cast<Serialize>(member));
for (xml::Node::size_type i(0); i<node.children(); ++i) for (xml::Node::size_type i(0); i<node.children(); ++i)
ser->fromNode(ser->_xmlNames[*node[i]], node[i]);*/ ser->fromNode(ser->_xmlNames[*node[i]], node[i]);*/
return true; return true;
} }
template<> bool assignToNode<Serialize>(const Any member, template<> bool assignToNode<Serialize>(const Any member,
xml::Node& node) { xml::Node& node) {
if (!any_cast<Serialize*>(&member)) return false; if (!any_cast<Serialize>(&member)) return false;
std::stringstream ss; std::stringstream ss;
any_cast<Serialize*>(member)->saveXml(ss, node.name()); any_cast<Serialize>(member)->saveXml(ss, node.name());
xml::Factory factory(node); xml::Factory factory(node);
node = *factory.read(ss); node = *factory.read(ss);
return true; return true;
} }
// Register for all simple Types ============================================= //================================================================ Clear Types
template<int NUM> struct SimpleTypes {}; template<typename TYPE> bool clearMember(Any member) {
template<> struct SimpleTypes<1> {typedef Serialize Type;}; if (!any_cast<TYPE>(&member)) return false;
template<> struct SimpleTypes<2> {typedef std::string Type;}; *any_cast<TYPE>(member) = 0;
template<> struct SimpleTypes<3> {typedef bool Type;}; return true;
template<> struct SimpleTypes<4> {typedef unsigned char Type;}; }
template<> struct SimpleTypes<5> {typedef signed char Type;}; template<> bool clearMember<bool>(Any member) {
template<> struct SimpleTypes<6> {typedef char Type;}; if (member.type()!=typeid(bool)) return false;
template<> struct SimpleTypes<7> {typedef unsigned short Type;}; *any_cast<bool>(member) = false;
template<> struct SimpleTypes<8> {typedef signed short Type;}; return true;
template<> struct SimpleTypes<9> {typedef short Type;}; }
template<> struct SimpleTypes<10> {typedef unsigned int Type;}; template<> bool clearMember<std::string>(Any member) {
template<> struct SimpleTypes<11> {typedef signed int Type;}; if (member.type()!=typeid(bool)) return false;
template<> struct SimpleTypes<12> {typedef int Type;}; any_cast<std::string>(member)->clear();
template<> struct SimpleTypes<13> {typedef unsigned long Type;}; return true;
template<> struct SimpleTypes<14> {typedef signed long Type;}; }
template<> struct SimpleTypes<15> {typedef long Type;}; template<> bool clearMember<Serialize>(Any member) {
template<> struct SimpleTypes<16> {typedef float Type;}; if (!any_cast<Serialize>(&member)) return false;
template<> struct SimpleTypes<17> {typedef double Type;}; any_cast<std::string>(member)->clear();
const int START_NUM(17); return true;
}
// Init To and From Node --------------------------------------------------- // Init To and From Node ---------------------------------------------------
namespace { namespace {
template<int NUM=START_NUM> struct RegisterTemplateForStdTypes { template<int NUM=MAX_NUM> struct RegisterTemplateForStdTypes {
RegisterTemplateForStdTypes<NUM-1> next; // recurse to next until 0 RegisterTemplateForStdTypes<NUM-1> next; // recurse to next until 0
RegisterTemplateForStdTypes() { RegisterTemplateForStdTypes() {
Serialize::registerToNode Serialize::registerToNode
(&assignToNode<typename SimpleTypes<NUM>::Type>); (&assignToNode<typename ToType<NUM>::Type>);
Serialize::registerFromNode Serialize::registerFromNode
(&assignFromNode<typename SimpleTypes<NUM>::Type>); (&assignFromNode<typename ToType<NUM>::Type>);
Serialize::registerClear
(&clearMember<typename ToType<NUM>::Type>);
} }
}; };
template<> struct RegisterTemplateForStdTypes<0> {}; // stop recursion template<> struct RegisterTemplateForStdTypes<0> {}; // stop recursion

@ -0,0 +1,173 @@
/*! @file
@id $Id: serialization_test.cxx 31 2009-04-28 07:36:07Z $
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
#include <xml-cxx/xml.hxx>
#include <cppunit/TestFixture.h>
#include <cppunit/ui/text/TestRunner.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
class A: public xml::Serialize {
public:
int _int;
unsigned _unsigned;
bool operator<(const A& o) const {
return _int<o._int || _int==o._int && _unsigned<o._unsigned;
}
protected:
void initXmlMembers() {
className("A");
persist(_int, "int");
persist(_unsigned, "unsigned");
}
};
class B: public xml::Serialize {
public:
long _long;
short _short;
protected:
void initXmlMembers() {
className("B");
persist(_long, "long");
persist(_short, "short");
}
};
class B2: public B {
public:
double _double;
protected:
void initXmlMembers() {
B::initXmlMembers();
className("B2");
persist(_double, "double");
}
};
class C: public xml::Serialize {
public:
xml::Map<A, B2> _map;
protected:
void initXmlMembers() {
className("C");
persist(_map, "map");
}
};
class C2: public C {
public:
xml::Set<std::string> _set;
protected:
void initXmlMembers() {
C::initXmlMembers();
className("C2");
persist(_set, "set");
}
};
class D: public xml::Serialize {
public:
xml::Vector<C2> _vector;
protected:
void initXmlMembers() {
className("D");
persist(_vector, "vector");
}
};
class ContainerSerializationTest: public CppUnit::TestFixture {
public:
void checkContainers() {
// instanciate all container to find template compilation errors
xml::Deque<int> c1;
xml::List<int> c2;
xml::Map<int, int> c3;
xml::MultiMap<int, int> c4;
xml::MultiSet<int> c5;
//xml::PriorityQueue<int> c6;
//xml::Queue<int> c7;
xml::Set<int> c8;
//xml::Stack<int> c9;
xml::Vector<int> c10;
// no test
std::string file("<D>\n"
"\t<vector>\n"
"\t\t<C2>\n"
"\t\t\t<map>\n"
"\t\t\t\t<A>\n"
"\t\t\t\t\t<int>-13</int>\n"
"\t\t\t\t\t<unsigned>13</unsigned>\n"
"\t\t\t\t</A>\n"
"\t\t\t\t<B2>\n"
"\t\t\t\t\t<long>1234567890</long>\n"
"\t\t\t\t\t<short>123</short>\n"
"\t\t\t\t\t<double>1.234</double>\n"
"\t\t\t\t</B2>\n"
"\t\t\t\t<A>\n"
"\t\t\t\t\t<int>-1</int>\n"
"\t\t\t\t\t<unsigned>2</unsigned>\n"
"\t\t\t\t</A>\n"
"\t\t\t\t<B2>\n"
"\t\t\t\t\t<long>451236789</long>\n"
"\t\t\t\t\t<short>43</short>\n"
"\t\t\t\t\t<double>15.34</double>\n"
"\t\t\t\t</B2>\n"
"\t\t\t</map>\n"
"\t\t\t<set>\n"
"\t\t\t\t<item>Hello</item>\n"
"\t\t\t\t<item>World</item>\n"
"\t\t\t</set>\n"
"\t\t</C2>\n"
"\t\t<C2>\n"
"\t\t\t<map/>\n"
"\t\t\t<set/>\n"
"\t\t</C2>\n"
"\t</vector>\n"
"</D>");
std::stringstream ss(file);
D d;
CPPUNIT_ASSERT_EQUAL(std::string("<D>\n"
"\t<vector>\n"
"\t\t<C2>\n"
"\t\t\t<map>\n"
"\t\t\t\t<A>\n"
"\t\t\t\t\t<int/>\n"
"\t\t\t\t\t<unsigned/>\n"
"\t\t\t\t</A>\n"
"\t\t\t\t<B2>\n"
"\t\t\t\t\t<long/>\n"
"\t\t\t\t\t<short/>\n"
"\t\t\t\t\t<double/>\n"
"\t\t\t\t</B2>\n"
"\t\t\t</map>\n"
"\t\t\t<set>\n"
"\t\t\t\t<item/>\n"
"\t\t\t</set>\n"
"\t\t</C2>\n"
"\t</vector>\n"
"</D>"),
d.schema());
CPPUNIT_ASSERT_NO_THROW(d.loadXml(ss));
std::stringstream ss2;
CPPUNIT_ASSERT_NO_THROW(d.saveXml(ss2));
CPPUNIT_ASSERT_EQUAL(file, ss2.str());
}
CPPUNIT_TEST_SUITE(ContainerSerializationTest);
CPPUNIT_TEST(checkContainers);
CPPUNIT_TEST_SUITE_END();
};
CPPUNIT_TEST_SUITE_REGISTRATION(ContainerSerializationTest);
int main() try {
CppUnit::TextUi::TestRunner runner;
runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
return runner.run() ? 0 : 1;
} catch (std::exception& e) {
std::cerr<<"***Exception: "<<e.what()<<std::endl;
return 1;
}

@ -7,11 +7,12 @@ AM_CXXFLAGS += -I ${top_srcdir}/src
AM_LDFLAGS = -L${top_builddir}/src AM_LDFLAGS = -L${top_builddir}/src
LDADD = -lcppunit -lxml-cxx LDADD = -lcppunit -lxml-cxx
check_PROGRAMS = xml_test serialization_test check_PROGRAMS = xml_test serialization_test container_serialization_test
TESTS=${check_PROGRAMS} TESTS=${check_PROGRAMS}
xml_test_SOURCES = xml_test.cxx xml_test_SOURCES = xml_test.cxx
serialization_test_SOURCES = serialization_test.cxx serialization_test_SOURCES = serialization_test.cxx
container_serialization_test_SOURCES = container_serialization_test.cxx
CLEANFILES = CLEANFILES =
MAINTAINERCLEANFILES = makefile.in MAINTAINERCLEANFILES = makefile.in

Loading…
Cancel
Save