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