diff --git a/COPYING b/COPYING deleted file mode 120000 index 0b6cbf8..0000000 --- a/COPYING +++ /dev/null @@ -1 +0,0 @@ -/usr/share/automake-1.10/COPYING \ No newline at end of file diff --git a/README b/README index 744491b..e250fb8 100644 --- a/README +++ b/README @@ -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. Rationale: The initial idea was to map C++ data structures to XML -files for configuration files that can easily be edited by hand. This -library does not need any kind of C++ code parser or special pre +files for configuration files that can easily be edited by hand. + +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 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 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) Structure of the files: diff --git a/doc/doxyfile.in b/doc/doxyfile.in index 2f9fe63..5470aa7 100644 --- a/doc/doxyfile.in +++ b/doc/doxyfile.in @@ -1,4 +1,4 @@ -# Doxyfile 1.5.5 +# Doxyfile 1.5.8 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project @@ -57,8 +57,8 @@ CREATE_SUBDIRS = NO # Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, # Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, -# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, -# and Ukrainian. +# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, +# Spanish, Swedish, and Ukrainian. OUTPUT_LANGUAGE = English @@ -155,13 +155,6 @@ QT_AUTOBRIEF = NO 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 # member inherits the documentation from any documented member that it # re-implements. @@ -219,6 +212,17 @@ OPTIMIZE_FOR_FORTRAN = 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 # 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 @@ -226,9 +230,9 @@ OPTIMIZE_OUTPUT_VHDL = NO # func(std::string) {}). This also make the inheritance and collaboration # 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. CPP_CLI_SUPPORT = NO @@ -239,6 +243,15 @@ CPP_CLI_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 # 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 @@ -264,6 +277,22 @@ SUBGROUPING = YES 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 #--------------------------------------------------------------------------- @@ -340,7 +369,7 @@ HIDE_IN_BODY_DOCS = NO # to NO (the default) then the documentation will be excluded. # 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 # file names in lower-case letters. If set to YES upper-case letters are also @@ -391,7 +420,7 @@ SORT_GROUP_NAMES = NO # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. @@ -448,6 +477,19 @@ SHOW_USED_FILES = YES 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 # 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 @@ -458,6 +500,15 @@ SHOW_DIRECTORIES = NO 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 #--------------------------------------------------------------------------- @@ -605,14 +656,17 @@ IMAGE_PATH = # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # 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. INPUT_FILTER = # 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 -# filter if there is a match. The filters are a list of the form: +# basis. +# 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 # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. @@ -647,22 +701,23 @@ INLINE_SOURCES = 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 # functions referencing it will be listed. 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 # called/used by that function will be listed. REFERENCES_RELATION = YES -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. Otherwise they will link to the documentstion. +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES @@ -751,12 +806,13 @@ HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# 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 # will be generated that can be used as input for Apple's Xcode 3 @@ -765,7 +821,8 @@ GENERATE_HTMLHELP = NO # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/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 @@ -783,13 +840,12 @@ DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. -HTML_DYNAMIC_SECTIONS = NO +GENERATE_HTMLHELP = NO # 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 @@ -811,6 +867,12 @@ HHC_LOCATION = 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 # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. @@ -822,6 +884,55 @@ BINARY_TOC = 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 +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's +# filter section matches. +# Qt Help Project / Filter Attributes. + +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 # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. @@ -833,14 +944,22 @@ DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 -# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be -# generated containing a tree-like index structure (just like the one that +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# 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 # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # 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 # 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 +# 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 #--------------------------------------------------------------------------- @@ -1060,8 +1187,10 @@ GENERATE_PERLMOD = NO PERLMOD_LATEX = NO # 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 -# if you want to understand what is going on. On the other hand, if this +# nicely formatted so it can be parsed by a human reader. +# 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 # and Perl will parse it just the same. @@ -1148,14 +1277,16 @@ SKIP_FUNCTION_MACROS = YES # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: -# TAGFILES = file1 file2 ... +# +# TAGFILES = file1 file2 ... # 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 # URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. @@ -1218,6 +1349,29 @@ HIDE_UNDOC_RELATIONS = NO 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 # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the @@ -1285,13 +1439,13 @@ GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include +# in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif +# generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png @@ -1307,7 +1461,7 @@ DOT_PATH = 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 # 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 @@ -1328,10 +1482,10 @@ DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 # 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. Warning: Depending on the platform used, enabling this option -# may lead to badly anti-aliased labels on the edges of a graph (i.e. they -# become hard to read). +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). DOT_TRANSPARENT = YES @@ -1355,7 +1509,7 @@ GENERATE_LEGEND = 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 diff --git a/doc/examples/address.cxx b/doc/examples/address.cxx index 2e76989..8911e1b 100644 --- a/doc/examples/address.cxx +++ b/doc/examples/address.cxx @@ -5,8 +5,6 @@ // 1 2 3 4 5 6 7 8 // 45678901234567890123456789012345678901234567890123456789012345678901234567890 -// g++ -I../../src ../../src/xml.cxx address.cxx - #include #include #include diff --git a/doc/examples/contain_serialization.cxx b/doc/examples/contain_serialization.cxx index ad23bee..5c53951 100644 --- a/doc/examples/contain_serialization.cxx +++ b/doc/examples/contain_serialization.cxx @@ -5,8 +5,6 @@ // 1 2 3 4 5 6 7 8 // 45678901234567890123456789012345678901234567890123456789012345678901234567890 -// g++ -I../../src ../../src/xml.cxx contain_serialization.cxx - #include #include #include diff --git a/doc/examples/inherit_serialization.cxx b/doc/examples/inherit_serialization.cxx index 48c2596..9d3c134 100644 --- a/doc/examples/inherit_serialization.cxx +++ b/doc/examples/inherit_serialization.cxx @@ -5,8 +5,6 @@ // 1 2 3 4 5 6 7 8 // 45678901234567890123456789012345678901234567890123456789012345678901234567890 -// g++ -I../../src ../../src/xml.cxx inherit_serialization.cxx - #include #include #include @@ -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: int b; protected: void initXmlMembers() { - A::initXmlMembers(); + A::initXmlMembers(); // <- Here is the important difference className("B"); persist(b, "b"); } diff --git a/doc/examples/list_serialization.cxx b/doc/examples/list_serialization.cxx index cc6e409..82dd959 100644 --- a/doc/examples/list_serialization.cxx +++ b/doc/examples/list_serialization.cxx @@ -5,8 +5,6 @@ // 1 2 3 4 5 6 7 8 // 45678901234567890123456789012345678901234567890123456789012345678901234567890 -// g++ -I../../src ../../src/xml.cxx list_serialization.cxx - #include #include #include @@ -37,26 +35,57 @@ class B: public xml::Serialize { }; int main(int, char**) { - std::stringstream ss("\n" - "1234" - "guguseli" - "" - "\n" - "\t\n" - "\t\tHello\n" - "\t\tWorld\n" - "\t\thow\n" - "\t\tare\n" - "\t\tyou\n" - "\t\n" - "" - "" + std::stringstream ss("" + " 1234" + " " + " " + " guguseli" + " " + " " + " " + " " + " " + " Hello" + " World" + " how" + " are" + " you" + " " + " " + " " + " " + " a" + " b" + " c" + " d" + " e" + " " + " " + " " + " " + " f" + " g" + " h" + " i" + " j" + " " + " " + " " + " " + " k" + " l" + " m" + " n" + " o" + " " + " " + " " ""); B b; std::cout<<"SCHEMA:"< #include #include diff --git a/doc/examples/serialization.cxx b/doc/examples/serialization.cxx index 278e208..f867f42 100644 --- a/doc/examples/serialization.cxx +++ b/doc/examples/serialization.cxx @@ -5,8 +5,6 @@ // 1 2 3 4 5 6 7 8 // 45678901234567890123456789012345678901234567890123456789012345678901234567890 -// g++ -I../../src ../../src/xml.cxx serialization.cxx - #include #include #include diff --git a/src/makefile.am b/src/makefile.am index 1462fe8..1f4cb63 100644 --- a/src/makefile.am +++ b/src/makefile.am @@ -7,7 +7,7 @@ AM_CXXFLAGS += -I ${top_srcdir}/src 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 diff --git a/src/xml-cxx/any.hxx b/src/xml-cxx/any.hxx index cb80fc2..c3db984 100644 --- a/src/xml-cxx/any.hxx +++ b/src/xml-cxx/any.hxx @@ -19,107 +19,158 @@ namespace xml { - class Any { + class Serialize; + + //! @cond INTERNAL + + //! @addtogroup serialization + //@{ + + /*! @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 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 struct ToNum {static const int NUM = 1;}; + template<> struct ToNum {static const int NUM = 1;}; + template<> struct ToNum {static const int NUM = 2;}; + template<> struct ToNum {static const int NUM = 3;}; + template<> struct ToNum {static const int NUM = 4;}; + template<> struct ToNum {static const int NUM = 5;}; + template<> struct ToNum< char > {static const int NUM = 6;}; + template<> struct ToNum {static const int NUM = 7;}; + template<> struct ToNum {static const int NUM = 8;}; + template<> struct ToNum {static const int NUM = 9;}; + template<> struct ToNum {static const int NUM = 10;}; + template<> struct ToNum {static const int NUM = 11;}; + template<> struct ToNum {static const int NUM = 12;}; + template<> struct ToNum {static const int NUM = 13;}; + template<> struct ToNum {static const int NUM = 14;}; - public: // structors - - Any(): content(0) {} + template bool isSerialize() { + return ToNum::NUM == 1; + } - template Any(const ValueType& value): - content(new holder(value)) { + template ::NUM==1)> struct Mapper { + static Serialize* toSerialize(T& obj) { + return 0; } + }; + template struct Mapper { + static Serialize* toSerialize(T& obj) { + return dynamic_cast(&obj); + } + }; - Any(const Any& other): content(other.content?other.content->clone():0) {} - + class Any { + public: + Any(): _content(0) {} + template Any(TYPE* value): + _content(new Holder(value)) { + } + Any(const Any& other): + _content(other._content?other._content->clone():0) { + } ~Any() { - delete content; + delete _content; } - - public: // modifiers - Any& swap(Any& rhs) { - std::swap(content, rhs.content); + std::swap(_content, rhs._content); return *this; } - - template Any& operator=(const ValueType& rhs) { + template Any& operator=(TYPE* rhs) { Any(rhs).swap(*this); return *this; } - Any& operator=(const Any& rhs) { Any(rhs).swap(*this); return *this; } - - public: // queries - bool empty() const { - return !content; + return !_content; } - const std::type_info& type() const { - return content?content->type():typeid(void); + return _content?_content->type():typeid(void); } - - private: // types - - class placeholder { - public: // structors - virtual ~placeholder() {} + class Placeholder { + public: + virtual ~Placeholder() {} virtual const std::type_info& type() const = 0; - virtual placeholder* clone() const = 0; + virtual Placeholder* clone() const = 0; }; - - template class holder: public placeholder { - + template class Holder: public Placeholder { public: - - holder(const ValueType& value): held(value) {} - + Holder(typename ToType::NUM>::Type* value): + _held(value) { + } virtual const std::type_info& type() const { - return typeid(ValueType); + return typeid(typename ToType::NUM>::Type); } - - virtual placeholder* clone() const { - return new holder(held); + virtual Placeholder* clone() const { + return new Holder(_held); } - - ValueType held; - + typename ToType::NUM>::Type* _held; }; - - private: // representation - - template friend ValueType* any_cast(Any*); - template friend ValueType& any_cast(Any&); - - placeholder* content; - + private: + template friend TYPE* any_cast(Any*); + template friend TYPE* any_cast(Any&); + Placeholder* _content; }; - - template ValueType* any_cast(Any* operand) { - return operand && operand->type() == typeid(ValueType) - ? &static_cast*>(operand->content)->held + template TYPE* any_cast(Any* operand) { + return operand && operand->type() == typeid(TYPE) + ? static_cast*>(operand->_content)->_held : 0; } - - template const ValueType* any_cast(const Any* operand) { - return any_cast(const_cast(operand)); + template const TYPE* any_cast(const Any* operand) { + return any_cast(const_cast(operand)); } - - template ValueType& any_cast(Any& operand) { - if (operand.type()==typeid(ValueType)) - return static_cast*>(operand.content)->held; + template TYPE* any_cast(Any& operand) { + if (operand.type()==typeid(TYPE)) + return static_cast*>(operand._content)->_held; throw std::bad_cast(); } - - template const ValueType& any_cast(const Any& operand) { - return any_cast(const_cast(operand)); + template const TYPE* any_cast(const Any& operand) { + return any_cast(const_cast(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. // // Distributed under the Boost Software License, Version 1.0. (See diff --git a/src/xml-cxx/xml.hxx b/src/xml-cxx/xml.hxx index 4f7c27c..d8431ab 100644 --- a/src/xml-cxx/xml.hxx +++ b/src/xml-cxx/xml.hxx @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -20,6 +19,7 @@ #include #include +//! @cond DEBUG #include #include #include @@ -51,7 +51,7 @@ class MethodTrace { } \ assert(Y); \ } - +//! @endcond /*! @mainpage @@ -60,11 +60,90 @@ class MethodTrace { - Specify your XML schema in C++ using common C++ syntax, such as shift, dereference, etc. - Verify the schema of XML files while they are read from a stream. + - Map and store your own C++ classes to XML and restore them back. + + @section basics Basics + + Include file: + @code + #include + @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 template.read(is) to read XML from a + stream: + + @code + // start with root element: + xml::Factory template(xml::Node("root").attr("id", xml::optional) + // contains any number of + < + <<(xml::Node("other").limits(1, 1) + // contains min 2 max 4 + <attr("id", xml::optional) + <<*xml::string::child.clone() + [...] + @endcode + + @section introSer Serialize Classes, Join Classes with XML + + When inheriting from xml::Serialize, your class inherits the + methods xml::Serialize::loadXml and + xml::Serialize::saveXml. Simply overwrite + xml::Serialize::initXmlMembers to make your class serializable + (@ref serialization): - From the README file: + @code + class MyClass: public xml::Serialize { + [...] + protected: + void initXmlMembers() { + className("MyClass"); + persist(i, "i"); + persist(s, "s"); + persist(l, "l"); + } + private: + int i; + std::string s; + xml::List l; // same behaviour as std::list + }; + @endcode + + @section readme The README File + @include README - @page Known Limitations + @page license License is LGPL 3 + + File COPYING from http://www.gnu.org/licenses/lgpl-3.0.txt: + + @include COPYING + + @page limits Known Limitations - XML-Comments are only ignored, not read, not stored. - Mixed tags and text is not supported. Tags may either contain @@ -74,6 +153,23 @@ class MethodTrace { (e.g. <p><p><p></p></p></p>) - Exceptions should be optional, best effort otherwise (option "strict") + @see serializationLimits + + @page serializationLimits Limitations of Serialization + + - Only the following types are intended to be serialized:\n + (It is possible to use other techniques, but that's not recommended) + - basic C++ types (except pointer) + - @c std::string + - classes derieved from xml::Serialize + - most standard containers, but in their xml-form, + e.g. xml::List instead of @c std::list + (xml::List inherits @c std::list) + - @c std::bitset, @c std::priority_queue, @c std::queue and + @c std::stack are not implemented + - Optional values are not yet implemented + - Polymorfic serialisation is not yet implemented + @page rationale Rationale - Limitations of other libraries The initial idea was to map C++ data structures to XML files @@ -98,7 +194,7 @@ class MethodTrace { (http://gsoap.sf.net), boost serialization (http://boost.org) and Qt XML (http://qtsoftware.com). - @section Qt XML, a typical DOM approach + @section qtxml Qt XML, a typical DOM approach One is the XML part of the Qt library. These classes can read XML into a DOM tree, but then the user has to check for every detail. @@ -118,25 +214,83 @@ class MethodTrace { ... @endcode - @example address.cxx */ - + This is a typical example of a DOM parser. The main disadvantage + here is that we cannot declare a schema. After parsing an XML + file, we cannot know whether it is valid with respect to our + definition or not. This means that every single access tested. + + xml::Factory lets you specify a schema template and guarantees + that the parsed file passed a lot of tests to make sure it fits + into the schema. If any test fails, the factory throws an + exception. + + @section boostserialization Boost Serialization + + Boost serialization is quite flexible and easy to use, but there + are several pitfalls, and worst, the generated XML cannot easily + be edited by hand. One of the main problems: If you store lists, + you cannot simply add an arbitrary number of list items, but you + must first serialize the list size as a number. If you edit the + file by hand, the number must exactly match the number of items, + or parsing will fail. Error messages (the exceptions) don't help + finding the problem within the parsed XML code. The XML format it + generates is definitely not made to be edited by hand. + + In fact, in my project that resulted in this new class (CoMoL, see + http://comol.sourceforge.net), we first used Boost serialization + to read and write XML files. The configuration was then done on + the GUI. But this was not comfortable enough, so the configuration + was mostly edited by hand. It was a pain then to find any typos in + the XML and the storage was too unflexible. So we needed a new + apporach, and well here it is. CoMoL now uses the full flexibility + of @ref freexml including optional tags and attributes. + + @section gsoap Using gSOAP for Serialization of C++ Structures + + When I was working at Siemens, we often used gSOAP + (http://gsoap.sf.net) when we needed mor flexibility in XML + declaration than what's possible with @ref boostserialization. But + gSOAP has several problems: + + - It is a C framework, not native C++, with all the problems + that result from this, i.e. memory management is absolutely + awful. + - It is a quite a complex problem to copy a gSOAP structure + without memory access problems. + - Moreover gSOAP is not real C++ code, but it requires a pre + processor that generates C++ from a pseudo C++ structure. + - It is not designed to Store C++ in XML, but to implement the + gSOAP protocol. + - And last but not least, the license is not free for all usage. + + @example address.cxx Example + + This is a simple example on how to declare a XML schema and how to + use a xml::Factory to restore it from a file. */ + +//! @addtogroup freexml +//@{ /*! @defgroup xmlConst XML Constant Declarations There are macros to help you with declaring constants. Chose a C++ - header fiel, where you want to declare constant names for your xml + header file, where you want to declare constant names for your xml nodes. - Then for every leaf xml::Node name you will use, call - XML_NODE(name) and for every xml::String call XML_STRING(name). + Then for xml::Node you will use, call XML_NODE(name) and for every + xml::String call XML_STRING(othername). After the declaration, you + can use the xml::Node as constant @c xml::node::name, the + xml::String as constant @c xml::string::name and @c std::string + constants for the given node names as @c xml::name::name and @c + xml::name::othername. - For every node with children call XML_PARENT(name, child1, child2 ...). + @note If you want to use the xml::Node, xml::String constants in a + non constant environment, i.e. to add children, attributes or + limits, you must call xml::Node::clone to get a non constant copy. @note Node names must be unique. You can not even use the same name for a XML_NODE and a XML_STRING declaration. - This code is from the examples: - - @include node_macros.cxx + @see @ref node_macros.cxx @example node_macros.cxx @@ -168,12 +322,17 @@ class MethodTrace { names are more often used than nodes. (@em Never use @c using in a C++ header file, you would pollute the scope of all the includers.) */ +//@} +//! @addtogroup xmlConst //@{ //! Define a string for a node name /*! It is called inside XML_NODE and XML_STRING, so if you work with these two, you don't have to care about XML_NAME. But you can use XML_NAME alone if you don't want the other two macros. + + Declares a constant of type @c std::string with name @c xml::name::NAME. + @see XML_NODE @see XML_STRING */ #define XML_NAME(NAME) \ @@ -233,8 +392,14 @@ class MethodTrace { //@} -//! Represents classes for handling C++ access to XML files with strict schema. -/*! The schema ist not presented through xsd, but it can be declared in C++. +/*! @defgroup freexml Arbitrary XML Schema Definition and Storage + + Class xml::Node declares an XML DOM node. Storing XML structures + has never been a problem, but to read them back again, + xml::Factory is needed, which must be given an XML schema + description. The XML schema is fully declared in C++, simply by + shifting the allowed nodes and attributes into the factory and by + setting limits. A xml::Factory represents a factory that owns a template and can instanciate XML trees that are valid for the given template from @@ -286,8 +451,15 @@ class MethodTrace { <<?xml?> //! or a document type declaration <!DOCTYPE ...> }; + + //! @endcond + //! Declares an attribute to be mandatory. const bool mandatory(true); //! Declares an attribute to be optional. @@ -311,6 +486,10 @@ namespace xml { class Node; class Factory; + //@} + //! @defgroup exceptions Exception classes + //@{ + //---------------------------------------------------------------------------- class exception: public std::exception { public: @@ -330,6 +509,9 @@ namespace xml { exception("serialized node type is not registered\ntype: " +type+"\nname: "+name, t) { } + type_not_registered(std::string type): + exception("serialized node type is not registered\ntype: "+type) { + } }; //---------------------------------------------------------------------------- class empty_attribute_list: public exception { @@ -421,6 +603,7 @@ namespace xml { stream_error("mismatching end tag", t, is, tag, c) { } }; + //---------------------------------------------------------------------------- class missing_end_tag: public stream_error { public: missing_end_tag(const Node& t, std::istream& is, const Tag& tag, char c=0) @@ -428,6 +611,7 @@ namespace xml { stream_error("missing end tag, end of file reached", t, is, tag, c) { } }; + //---------------------------------------------------------------------------- class wrong_start_tag: public stream_error { public: wrong_start_tag(const Node& t, std::istream& is, const Tag& tag, char c=0) @@ -435,6 +619,7 @@ namespace xml { stream_error("start tag does not match expected tag", t, is, tag, c) { } }; + //---------------------------------------------------------------------------- class second_slash_in_tag: public stream_error { public: second_slash_in_tag(const Node& t, std::istream& is, const Tag& tag, @@ -443,6 +628,7 @@ namespace xml { stream_error("a tag may have no more than one slash", t, is, tag, c) { } }; + //---------------------------------------------------------------------------- class character_after_slash: public stream_error { public: character_after_slash(const Node& t, std::istream& is, const Tag& tag, @@ -452,6 +638,7 @@ namespace xml { t, is, tag, c) { } }; + //---------------------------------------------------------------------------- class attributes_in_end_tag: public stream_error { public: attributes_in_end_tag(const Node& t, std::istream& is, const Tag& tag) @@ -459,6 +646,7 @@ namespace xml { stream_error("attributes are not allowed in end tags",t, is, tag) { } }; + //---------------------------------------------------------------------------- class attribute_value_not_quoted: public stream_error { public: attribute_value_not_quoted(const Node& t, std::istream& is, @@ -468,6 +656,7 @@ namespace xml { t, is, tag, c) { } }; + //---------------------------------------------------------------------------- class duplicate_attribute: public stream_error { public: duplicate_attribute(const Node& t, std::istream& is, const Tag& tag, @@ -476,6 +665,7 @@ namespace xml { t, is, tag, 0) { } }; + //---------------------------------------------------------------------------- class illegal_attribute: public stream_error { public: illegal_attribute(const Node& t, std::istream& is, const Tag& tag, @@ -484,6 +674,7 @@ namespace xml { t, is, tag, 0) { } }; + //---------------------------------------------------------------------------- class mandatory_attribute_missing: public stream_error { public: mandatory_attribute_missing(const Node& t, std::istream& is, @@ -492,6 +683,7 @@ namespace xml { t, is, tag, 0) { } }; + //---------------------------------------------------------------------------- class wrong_node_number: public stream_error { public: wrong_node_number(const Node& t, std::istream& is, @@ -510,9 +702,13 @@ namespace xml { return ss.str(); } }; + //! @} //============================================================================ + //! @addtogroup freexml + //@{ + //---------------------------------------------------------------------------- //! Map for attribute values. /*! Attributes can be set using method xml::Node::attr(). Check for @@ -570,7 +766,7 @@ namespace xml { }; //---------------------------------------------------------------------------- - //! An xml Node. + //! An xml Node that contains child nodes but no text. /*! XML Nodes may contain either text or other nodes, but not both at the same time. This node can hold other nodes. For a Node for text contents, see xml::String. */ @@ -639,6 +835,7 @@ namespace xml { }; //---------------------------------------------------------------------------- + //! A leaf node that contains text but no child nodes. class String: public Node { public: String(std::string name, @@ -672,6 +869,7 @@ namespace xml { }; //---------------------------------------------------------------------------- + //! A leaf node that contains only numbers and no child nodes. class UnsignedInteger: public String { public: UnsignedInteger(std::string name, unsigned long i=0, @@ -685,6 +883,52 @@ namespace xml { }; //---------------------------------------------------------------------------- + //! Factory to restore XML structures from a stream. + /*! A xml::Factory must be given a template that declares the + structure, before the factory can be used. This can be done + either at instanciation or later by assignment. + + The template is a xml::Node that specifies the schema of the data + that can be loaded from streams through a xml::Factory + instance. + + The root element has automatically set the limits 1..1 + (xml::Node::limits(1, 1), see xml::Node::limits), + which means that the root element must exist exactly once. If + you pass another limit, your limit is overwritten and ignored. + + E.g. to load an address, that contains a tag <address> + with at least a name and optional an address in it's body, you + may write: + + @code + xml::Factory addrTpl(xml::Node("address") + (< .limit(0, 0) + < + + Marc + Roman + Wäckerlin + + + SwissSign AG + Pfingstweidstrasse 60b + 8005 Zürich + + Schweiz + + @endverbatim */ class Factory { public: Factory(const Node& t) throw(); @@ -695,7 +939,8 @@ namespace xml { const Node*const operator->() const throw(factory_not_valid); operator bool() const throw(); friend std::ostream& operator<<(std::ostream& os, - const Factory& factory) throw(); + const Factory& factory) + throw(factory_not_valid); static std::ostream& print(std::ostream& os, const Node& node, unsigned int level=0) throw(); std::auto_ptr read(std::istream& is) @@ -704,12 +949,14 @@ namespace xml { missing_end_tag, attribute_value_not_quoted, access_error, duplicate_attribute, attributes_in_end_tag, illegal_attribute, mandatory_attribute_missing, - wrong_node_number); + wrong_node_number, factory_not_valid); void reset() throw(); private: friend class stream_error; friend class Serialize; - template friend class List; + template friend class Container; + template friend class AssociativeContainer; + template friend class AssociativeMap; Node& operator*() throw(factory_not_valid); Node*const operator->() throw(factory_not_valid); bool ws(char c) throw(); @@ -734,6 +981,7 @@ namespace xml { unsigned long _line; long _open; }; + //@} /*! @defgroup serialization Class Serialization @@ -763,74 +1011,87 @@ namespace xml { @section serActual Actual Status The following member types are supported - - All built-in C++ types are supported - - std::string is supported + - All built-in C++ types are supported, except enum + - @c std::string is supported - Contained classes are supported + - Inheritance + - @ref serContainer - The following will be supported soon (ideas): - - lists, maps - - inheritance + @todo The following will be supported soon (ideas): - choices (one of) + - choices (polymorfism) - optional members (pointer) + - enum (class xml::Enum) Pointers cannot be stored. - There are many ways of implemenation. best practice is to - inherit xml::Serialize and to overwrite - xml::Serialize::initXmlMembers: - - @code - class A: public xml::Serialize { - protected: - // all persitent members must be registered - virtual void initXmlMembers() { - className("A"); // giving a class name is very important - persist(_anInteger, "anInteger"); - persist(_aBool, "aBool"); - persist(_aDouble, "aDouble"); - persist(_aString, "aString"); - persist(_anotherString, "anotherString"); - persist(_aLong, "aLong"); - } - private: - int _anInteger; - bool _aBool; - double _aDouble; - std::string _aString; - std::string _anotherString; - unsigned long _aLong; - }; + @section serBestPract Best Practice and Inheritance - class B: public xml::Serialize { - protected: - virtual void initXmlMembers() { - className("B"); - persist(_a); // name must not be given, it's already known - persist(_anInteger, "anInteger"); - } - private: - A _a; // contains an A - int _anInteger; - }; + There are many ways of implemenation (see example @ref + serialization.cxx). best practice is to inherit xml::Serialize + and to overwrite xml::Serialize::initXmlMembers, as shown in the + example @ref serialization.cxx. - int main(int, char**) { - A a; - B b; - // ... do something with a and b, then write it to stdout: - a.saveXml(std::out)<only class @c B shows the + recommended way of doing it. + + @warning Please note that xml::Serialize stores pointers to the + variables that are serialized. If you access xml::Serialize + outside of the life-cycle of any of the persistent variables, + then your program may crash (in the best case) or even behave in + an unexpected way. + + @example contain_serialization.cxx + + Handle containment in the recommended way. It's very simple: If + all classes inherit from xml::Serialize, then containment + behaves as expected. + + @example inherit_serialization.cxx + + This is an example for inheritance according the recommended way: + - There's nothing special for the parent. + - The child must do the following in xml::Serialize::initXmlMembers + (the order is important!): + -# call xml::Serialize::initXmlMembers of the parent + -# call xml::Serialize::className to set the new class name + -# call xml::Serialize::persist for all child members + - The only difference is, that ... + - ... the child does not inherit xml::Serialize, but a child of it + - ... the child must first call xml::Serialize::initXmlMembers of + the parent in it's own xml::Serialize::initXmlMembers */ + //! @addtogroup serialization //@{ class Serialize { public: typedef bool(*FromNodeFunc)(Any, const xml::Node&); typedef bool(*ToNodeFunc)(const Any, xml::Node&); + typedef bool(*ClearFunc)(Any); //! You must call Serialize::className() if you use this constructor! Serialize() throw(); Serialize(const std::string& className) throw(); @@ -866,14 +1127,15 @@ namespace xml { const std::string& name) throw(); Serialize& persist(std::string& member, const std::string& name) throw(); - std::ostream& saveXml(std::ostream& os, - const std::string& name = std::string()) + virtual std::ostream& saveXml(std::ostream& os, + const std::string& name = std::string()) const throw(); - std::istream& loadXml(std::istream& is, - const std::string& name = std::string()); + virtual std::istream& loadXml(std::istream& is, + const std::string& name = std::string()); std::string schema() const throw(); static void registerFromNode(FromNodeFunc fromNodeFunc); static void registerToNode(ToNodeFunc toNodeFunc); + static void registerClear(ClearFunc clearFunc); protected: virtual void initXmlMembers(); void checkInit(const Serialize* const ser=0) const { @@ -883,8 +1145,12 @@ namespace xml { if (!_xmlFactory) const_cast(this)->initXmlMembers(); } } - private: - public: //! @todo + virtual void clear() throw(); + /*! @todo Why does @c protected: not work here?!? Children can't + access the members if they are protected! */ + public: + //! @cond INTERNAL + void clear(Any member) throw(); void reset() throw(); void copy(const Serialize& o) throw(); template @@ -896,85 +1162,210 @@ namespace xml { _xmlFactory = schema; return *this; } - virtual void fromNode(Any member, const xml::Node& node); - virtual void toNode(const Any member, xml::Node& node) const; - /* - template - void fromNode(std::list* member, xml::Node& node) { - member->clear(); - for (xml::Node::size_type i(0); i _xmlNames; xml::Factory _xmlFactory; static std::set _fromNode; static std::set _toNode; + static std::set _clear; + //! @endcond }; - - const int MAX_NUM(14); - - template 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 struct ToNum {static const int NUM = 1;}; - template<> struct ToNum {static const int NUM = 1;}; - template<> struct ToNum {static const int NUM = 2;}; - template<> struct ToNum {static const int NUM = 3;}; - template<> struct ToNum {static const int NUM = 4;}; - template<> struct ToNum {static const int NUM = 5;}; - template<> struct ToNum< char > {static const int NUM = 6;}; - template<> struct ToNum {static const int NUM = 7;}; - template<> struct ToNum {static const int NUM = 8;}; - template<> struct ToNum {static const int NUM = 9;}; - template<> struct ToNum {static const int NUM = 10;}; - template<> struct ToNum {static const int NUM = 11;}; - template<> struct ToNum {static const int NUM = 12;}; - template<> struct ToNum {static const int NUM = 13;}; - template<> struct ToNum {static const int NUM = 14;}; - - template bool isSerialize() { - return ToNum::NUM == 1; - } - template ::NUM==1)> struct Mapper { - static Serialize* toSerialize(T& obj) { - return 0; + template class Optional: public Serialize { + public: + Optional() throw() {} + Optional(const Optional& o) throw(): _member(new TYPE(*o._member)) {} + Optional(const TYPE& mem) throw(): _member(new TYPE(mem)) {} + virtual ~Optional() throw() {} + Optional& operator=(const Optional& o) throw() { + _member = new TYPE(*o._member); + return *this; + } + Optional& operator=(const TYPE& mem) throw() { + _member = new TYPE(mem); + return *this; + } + operator bool() const throw() { + return _member.get(); + } + const TYPE& operator*() const throw() { + return *_member; + } + TYPE& operator*() throw() { + return *_member; + } + const TYPE*const operator->() const throw() { + return _member.get(); + } + TYPE*const operator->() throw() { + return _member.get(); + } + Optional& reset() throw() { + _member.reset(); + return *this; + } + protected: + virtual void clear() throw() { + _member.reset(); } + virtual std::ostream& saveXml(std::ostream& os, + const std::string& name = std::string()) + const throw() { + if (!_member.get()) return os; + checkInit(); + xml::Node node(*_xmlFactory); + if (name.size()) node.name(name); + toNode(*_member, node); + os< _member; }; - template struct Mapper { - static Serialize* toSerialize(T& obj) { - return dynamic_cast(&obj); + + //! @addtogroup serialization + //@{ + /*! @defgroup serContainer Serialization of Container + + libxml-cpp can serialize container, such as Lists, Vectors or + Maps. Classes that serialize cannot contain standard C++ + container directly, but they must contain container defined + here. For every standard container except @c std::bitset there + is a XML representation available. + + The following containers are defined: + - xml::DeQue (inherits @c std::deque and xml::Serialize) + - xml::List (inherits @c std::list and xml::Serialize) + - xml::Map (inherits @c std::map and xml::Serialize) + - xml::MultiMap (inherits @c std::multimap and xml::Serialize) + - xml::MultiSet (inherits @c std::multiset and xml::Serialize) + - xml::Set (inherits @c std::set and xml::Serialize) + - xml::Vector (inherits @c std::vector and xml::Serialize) + + E.g. use @c xml::List instead of @c std::list. + + I don't see any necessity to implement @c std::priority_queue, + @c std::queue and @c std::stack, they are only restricted + interfaces to another container and don't allow random access + (which is needed to store them). + + @example list_serialization.cxx */ + //@} + + //! @cond INTERNAL + template class Container: + 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 node(factory.read(is)); + CONTAINER_TYPE::clear(); + for (xml::Node::size_type i(0); ichildren(); ++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 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 item(tpl->clone()); + Serialize::toNode(&tmp, *item); + node<<*item; + } + os<()) { + Serialize* ser(Mapper + ::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 List: - public std::list, public Serialize { + template class AssociativeContainer: + public CONTAINER_TYPE, + public Serialize { public: - List() {} - List(const List& o): std::list(o), Serialize(o) {} - List(const std::string& className) throw(): Serialize(className) {} - virtual ~List() {} + AssociativeContainer() {} + AssociativeContainer(const AssociativeContainer& o): + CONTAINER_TYPE(o), Serialize(o) { + } + AssociativeContainer(const std::string& className) throw(): + Serialize(className) { + } + virtual ~AssociativeContainer() {} + virtual std::istream& loadXml(std::istream& is, + const std::string& name = std::string()) { + checkInit(); + xml::Factory factory(_xmlFactory); + if (name.size()) factory->name(name); + std::auto_ptr node(factory.read(is)); + CONTAINER_TYPE::clear(); + for (xml::Node::size_type i(0); ichildren(); ++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 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 item(tpl->clone()); + Serialize::toNode(&tmp, *item); + node<<*item; + } + os<()) { - LOG("Liste von Serialize"); - Serialize* ser(Mapper::toSerialize(tmp)); + typename CONTAINER_TYPE::value_type tmp; + if (isSerialize()) { + Serialize* ser(Mapper + ::toSerialize(tmp)); assert(ser); assert(ser!=this); assert((void*)ser==(void*)&tmp); @@ -982,34 +1373,231 @@ namespace xml { itemName = ser->_xmlFactory->name(); } _xmlFactory = xml::Node("dummyroot"); // dummy root, (uninitialized exc) - persist(tmp, itemName); // add _reference as child of dummyroot + persist(tmp, itemName); // add as child of dummyroot (*_xmlFactory)[0].limits(0, 0); // any number of children possible } - virtual void fromNode(Any member, const xml::Node& node) { - this->clear(); - for (xml::Node::size_type i(0); i 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 node(factory.read(is)); + CONTAINER_TYPE::clear(); + LOG("READING: "<<*node); + for (xml::Node::size_type i(0); ichildren(); ++i) { + typename CONTAINER_TYPE::key_type key; + typename CONTAINER_TYPE::mapped_type data; + LOG("READING Key: "<<(*node)[i]<<"Value:"<<(*node)[1+i]); + Serialize::fromNode(&key, (*node)[i]); // reads into tmp + Serialize::fromNode(&data, (*node)[++i]); // key&value + insert(typename CONTAINER_TYPE::value_type(key, data)); + LOG("READ"); } + LOG("DONE"); + return is; } - virtual void toNode(const Any member, xml::Node& node) const { - std::auto_ptr tpl(node.clone()); - xml::Node& parent(node.parent()); - parent.clear(); // "node" is now invalid - for (typename List::const_iterator it = this->begin(); + virtual std::ostream& saveXml(std::ostream& os, + const std::string& name = std::string()) + const throw() { + checkInit(); + xml::Node node(*_xmlFactory); + if (name.size()) node.name(name); + std::auto_ptr tpl1(node[0].clone()); + std::auto_ptr tpl2(node[1].clone()); + node.clear(); // "node" is now invalid + for (typename AssociativeMap::const_iterator it = this->begin(); it!=this->end(); ++it) { - TYPE tmp; - tmp = *it; - std::auto_ptr item(tpl->clone()); - Serialize::toNode(&tmp, *item); - parent<<*item; + typename CONTAINER_TYPE::key_type key; + typename CONTAINER_TYPE::mapped_type data; + key = it->first; + data = it->second; + std::auto_ptr item1(tpl1->clone()); + Serialize::toNode(&key, *item1); + std::auto_ptr item2(tpl2->clone()); + Serialize::toNode(&data, *item2); + node<<*item1<<*item2; + } + os<()) { + const Serialize* ser(Mapper + ::toSerialize(key)); + checkInit(ser); + keyName = ser->_xmlFactory->name(); } + if (isSerialize()) { + Serialize* ser(Mapper + ::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 CONTAINER: \ + public Container > { \ + public: \ + CONTAINER() {} \ + CONTAINER(const CONTAINER& o): \ + Container >(o) { \ + } \ + CONTAINER(const std::string& className) throw(): \ + Container >(className) { \ + } \ + virtual ~CONTAINER() {} \ + }; \ + } +# include +__XML_CXX_DECLARE_CONTAINER_CLASS__(List, std::list); +# include +__XML_CXX_DECLARE_CONTAINER_CLASS__(Vector, std::vector); +# include +__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 CONTAINER: \ + public AssociativeContainer \ + > { \ + public: \ + CONTAINER() {} \ + CONTAINER(const CONTAINER& o): \ + AssociativeContainer \ + >(o) { \ + } \ + CONTAINER(const std::string& className) throw(): \ + AssociativeContainer \ + > \ + (className) { \ + } \ + virtual ~CONTAINER() {} \ + }; \ + } +# include +__XML_CXX_DECLARE_CONTAINER_CLASS__(Stack, std::stack); +# include +__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 COMPARE=std::less, \ + class ALLOC=std::allocator > \ + class CONTAINER: \ + public AssociativeContainer \ + > { \ + public: \ + CONTAINER() {} \ + CONTAINER(const CONTAINER& o): \ + AssociativeContainer \ + >(o) { \ + } \ + CONTAINER(const std::string& className) throw(): \ + AssociativeContainer \ + > \ + (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 ALLOC=std::allocator > \ + class CONTAINER: \ + public AssociativeContainer \ + > { \ + public: \ + CONTAINER() {} \ + CONTAINER(const CONTAINER& o): \ + AssociativeContainer \ + >(o) { \ + } \ + CONTAINER(const std::string& className) throw(): \ + AssociativeContainer \ + > \ + (className) { \ + } \ + virtual ~CONTAINER() {} \ + }; \ + } +# include +__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 ALLOC=std::allocator > > \ + class CONTAINER: public AssociativeMap \ + > { \ + public: \ + CONTAINER() {} \ + CONTAINER(const CONTAINER& o): \ + AssociativeMap \ + >(o) { \ + } \ + CONTAINER(const std::string& className) throw(): \ + AssociativeMap \ + > \ + (className) { \ + } \ + virtual ~CONTAINER() {} \ + }; \ + } +# include +__XML_CXX_DECLARE_CONTAINER_CLASS__(Map, std::map); +__XML_CXX_DECLARE_CONTAINER_CLASS__(MultiMap, std::multimap); +# undef __XML_CXX_DECLARE_CONTAINER_CLASS__ +//@} +//! @endcond + #endif diff --git a/src/xml.cxx b/src/xml.cxx index 98e3e72..986c402 100644 --- a/src/xml.cxx +++ b/src/xml.cxx @@ -718,76 +718,48 @@ namespace xml { //---------------------------------------------------------------------------- //! 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 - (xml::Node::limits(1, 1), 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 <address> - with at least a name and optional an address in it's body, you - may write: - - @code - xml::Factory addrTpl(xml::Node("address") - (< .limit(0, 0) - < - - Marc - Roman - Wäckerlin - - - SwissSign AG - Pfingstweidstrasse 60b - 8005 Zürich - - Schweiz - - @endverbatim */ Factory::Factory(const Node& t) throw(): _template(xml::Node("")<")), _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() { _template = xml::Node("")<() const throw(factory_not_valid) try { return &_template[0]; } catch (...) { throw factory_not_valid(); } + //! Check whether the factory has been given a valid template. Factory::operator bool() const throw() { 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); } + //! 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, unsigned int level) throw() { if (node.children()) { @@ -842,6 +814,7 @@ namespace xml { } return os; } + //! Restore a xml::Node tree from a stream, according to the given schema. std::auto_ptr Factory::read(std::istream& is) throw(wrong_end_tag, wrong_start_tag, tag_expected, type_mismatch, second_slash_in_tag, @@ -849,16 +822,20 @@ namespace xml { access_error, duplicate_attribute, attributes_in_end_tag, illegal_attribute, mandatory_attribute_missing, - wrong_node_number) try { - _line=1; - _open=0; - std::auto_ptr node(read(is, _template)); - if (node->children()==0) - throw tag_expected(_template[0], "nothing found, possibly empty stream"); - return (*node)[0].clone(); - } catch (exception& e) { - e.line(_line); - throw; + wrong_node_number, factory_not_valid) { + if (_template.children()==0) throw factory_not_valid(); + try { + _line=1; + _open=0; + std::auto_ptr node(read(is, _template)); + if (node->children()==0) + throw tag_expected(_template[0], + "nothing found, possibly empty stream"); + return (*node)[0].clone(); + } catch (exception& e) { + e.line(_line); + throw; + } } void Factory::reset() throw() { _line = 0; @@ -1124,7 +1101,10 @@ namespace xml { for (std::map::const_iterator it(_xmlNames.begin()); it!=_xmlNames.end(); ++it) - fromNode(it->second, (*node)[it->first]); + if ((*node)(it->first)) + fromNode(it->second, (*node)[it->first]); + else + clear(it->second); return is; } std::string Serialize::schema() const throw() { @@ -1137,10 +1117,18 @@ namespace xml { void Serialize::registerFromNode(Serialize::FromNodeFunc fromNodeFunc) { _fromNode.insert(fromNodeFunc); } - void Serialize::registerToNode(Serialize::ToNodeFunc toNodeFunc) { _toNode.insert(toNodeFunc); } + void Serialize::registerClear(Serialize::ClearFunc clearFunc) { + _clear.insert(clearFunc); + } + void Serialize::clear() throw() { + for (std::map::const_iterator + it(_xmlNames.begin()); + it!=_xmlNames.end(); ++it) + clear(it->second); + } void Serialize::initXmlMembers() {} void Serialize::reset() throw() { _xmlFactory.reset(); @@ -1154,127 +1142,116 @@ namespace xml { for (std::set::const_iterator it(_fromNode.begin()); it!=_fromNode.end(); ++it) if ((**it)(member, node)) return; // found match - /* - if (!RealType::isSerialize()) - */ - throw type_not_registered(member.type().name(), node.name(), node); - /* - Serialize* ser(RealType::serialize(member, 0)); - // from assignFromNode - //! @todo improve this (inefficient) - std::stringstream ss; // simple but inefficient: rewrite and reread - ss<loadXml(ss, node.name()); - */ + throw type_not_registered(member.type().name(), node.name(), node); } void Serialize::toNode(const Any member, xml::Node& node) const { for (std::set::const_iterator it(_toNode.begin()); it!=_toNode.end(); ++it) if ((**it)(member, node)) return; // found match - /* - if (!RealType::isSerialize()) - */ - throw type_not_registered(member.type().name(), node.name(), node); - /* - // from assignToNode - std::stringstream ss; - ((Serialize*)member)->saveXml(ss, node.name()); - xml::Factory factory(node); - node = *factory.read(ss); - */ + throw type_not_registered(member.type().name(), node.name(), node); + } + void Serialize::clear(Any member) throw() { + for (std::set::const_iterator it(_clear.begin()); + it!=_clear.end(); ++it) + if ((**it)(member)) return; // found match + throw type_not_registered(member.type().name()); } std::set Serialize::_fromNode; std::set Serialize::_toNode; + std::set Serialize::_clear; //=============================================================== Assign Types template bool assignFromNode(Any member, const xml::Node& node) { - if (!any_cast(&member)) return false; - *any_cast(member) = + if (!any_cast(&member)) return false; + *any_cast(member) = (TYPE)dynamic_cast(node); return true; } template bool assignToNode(const Any member, xml::Node& node) { - if (!any_cast(&member)) return false; + if (!any_cast(&member)) return false; std::stringstream ss; - ss<<*any_cast(member); + ss<<*any_cast(member); node.text(ss.str()); return true; } template<> bool assignFromNode(Any member, const xml::Node& node) { - if (member.type()!=typeid(bool*)) return false; + if (member.type()!=typeid(bool)) return false; std::string s(*dynamic_cast(node)); std::string::size_type start(s.find_first_not_of(" \t\n\r")), end(s.find_last_not_of(" \t\n\r")); if (start==std::string::npos) { - *any_cast(member) = false; + *any_cast(member) = false; } else { s = s.substr(start, end-start+1); - *any_cast(member) = + *any_cast(member) = s!="0" && s!="false" && s!="no" && s!="off"; } return true; } template<> bool assignToNode(const Any member, xml::Node& node) { - if (member.type()!=typeid(bool*)) return false; - node.text(*any_cast(member)?"true":"false"); + if (member.type()!=typeid(bool)) return false; + node.text(*any_cast(member)?"true":"false"); return true; } template<> bool assignFromNode(Any member, const xml::Node& node) { - if (!any_cast(&member)) return false; + if (!any_cast(&member)) return false; //! @todo improve this (inefficient) std::stringstream ss; // simple but inefficient: rewrite and reread ss<(member)->loadXml(ss, node.name()); + any_cast(member)->loadXml(ss, node.name()); /* - Serialize* ser(any_cast(member)); + Serialize* ser(any_cast(member)); for (xml::Node::size_type i(0); ifromNode(ser->_xmlNames[*node[i]], node[i]);*/ return true; } template<> bool assignToNode(const Any member, xml::Node& node) { - if (!any_cast(&member)) return false; + if (!any_cast(&member)) return false; std::stringstream ss; - any_cast(member)->saveXml(ss, node.name()); + any_cast(member)->saveXml(ss, node.name()); xml::Factory factory(node); node = *factory.read(ss); return true; } - // Register for all simple Types ============================================= - template struct SimpleTypes {}; - template<> struct SimpleTypes<1> {typedef Serialize Type;}; - template<> struct SimpleTypes<2> {typedef std::string Type;}; - template<> struct SimpleTypes<3> {typedef bool Type;}; - template<> struct SimpleTypes<4> {typedef unsigned char Type;}; - template<> struct SimpleTypes<5> {typedef signed char Type;}; - template<> struct SimpleTypes<6> {typedef char Type;}; - template<> struct SimpleTypes<7> {typedef unsigned short Type;}; - template<> struct SimpleTypes<8> {typedef signed short Type;}; - template<> struct SimpleTypes<9> {typedef short Type;}; - template<> struct SimpleTypes<10> {typedef unsigned int Type;}; - template<> struct SimpleTypes<11> {typedef signed int Type;}; - template<> struct SimpleTypes<12> {typedef int Type;}; - template<> struct SimpleTypes<13> {typedef unsigned long Type;}; - template<> struct SimpleTypes<14> {typedef signed long Type;}; - template<> struct SimpleTypes<15> {typedef long Type;}; - template<> struct SimpleTypes<16> {typedef float Type;}; - template<> struct SimpleTypes<17> {typedef double Type;}; - const int START_NUM(17); + //================================================================ Clear Types + template bool clearMember(Any member) { + if (!any_cast(&member)) return false; + *any_cast(member) = 0; + return true; + } + template<> bool clearMember(Any member) { + if (member.type()!=typeid(bool)) return false; + *any_cast(member) = false; + return true; + } + template<> bool clearMember(Any member) { + if (member.type()!=typeid(bool)) return false; + any_cast(member)->clear(); + return true; + } + template<> bool clearMember(Any member) { + if (!any_cast(&member)) return false; + any_cast(member)->clear(); + return true; + } // Init To and From Node --------------------------------------------------- namespace { - template struct RegisterTemplateForStdTypes { + template struct RegisterTemplateForStdTypes { RegisterTemplateForStdTypes next; // recurse to next until 0 RegisterTemplateForStdTypes() { Serialize::registerToNode - (&assignToNode::Type>); + (&assignToNode::Type>); Serialize::registerFromNode - (&assignFromNode::Type>); + (&assignFromNode::Type>); + Serialize::registerClear + (&clearMember::Type>); } }; template<> struct RegisterTemplateForStdTypes<0> {}; // stop recursion diff --git a/test/container_serialization_test.cxx b/test/container_serialization_test.cxx new file mode 100644 index 0000000..32a7225 --- /dev/null +++ b/test/container_serialization_test.cxx @@ -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 +#include +#include +#include +#include + +class A: public xml::Serialize { + public: + int _int; + unsigned _unsigned; + bool operator<(const A& o) const { + return _int _map; + protected: + void initXmlMembers() { + className("C"); + persist(_map, "map"); + } +}; + +class C2: public C { + public: + xml::Set _set; + protected: + void initXmlMembers() { + C::initXmlMembers(); + className("C2"); + persist(_set, "set"); + } +}; + +class D: public xml::Serialize { + public: + xml::Vector _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 c1; + xml::List c2; + xml::Map c3; + xml::MultiMap c4; + xml::MultiSet c5; + //xml::PriorityQueue c6; + //xml::Queue c7; + xml::Set c8; + //xml::Stack c9; + xml::Vector c10; + // no test + std::string file("\n" + "\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\t-13\n" + "\t\t\t\t\t13\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\t1234567890\n" + "\t\t\t\t\t123\n" + "\t\t\t\t\t1.234\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\t-1\n" + "\t\t\t\t\t2\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\t451236789\n" + "\t\t\t\t\t43\n" + "\t\t\t\t\t15.34\n" + "\t\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\tHello\n" + "\t\t\t\tWorld\n" + "\t\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\n" + "\t\n" + ""); + std::stringstream ss(file); + D d; + CPPUNIT_ASSERT_EQUAL(std::string("\n" + "\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\t\n" + "\t\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\t\t\n" + "\t\t\t\t\t\n" + "\t\t\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\t\n" + "\t\t\t\n" + "\t\t\n" + "\t\n" + ""), + 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: "<