2009-04-02 13:24:59 +00:00
|
|
|
/*! @file
|
|
|
|
|
|
|
|
@id $Id$
|
|
|
|
*/
|
|
|
|
// 1 2 3 4 5 6 7 8
|
|
|
|
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
|
|
|
|
|
2009-04-03 14:02:22 +00:00
|
|
|
#ifndef LIB_XML_CXX_HXX
|
|
|
|
#define LIB_XML_CXX_HXX
|
2009-04-02 13:24:59 +00:00
|
|
|
|
|
|
|
#include <istream>
|
2009-04-09 07:01:25 +00:00
|
|
|
#include <sstream>
|
2009-04-02 13:24:59 +00:00
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
2009-04-28 07:36:07 +00:00
|
|
|
#include <set>
|
2009-04-02 13:24:59 +00:00
|
|
|
#include <map>
|
|
|
|
#include <memory>
|
2009-04-23 15:10:21 +00:00
|
|
|
#include <typeinfo>
|
|
|
|
#include <stdexcept>
|
2009-04-30 15:10:09 +00:00
|
|
|
#include <xml-cxx/any.hxx>
|
2009-04-02 13:24:59 +00:00
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
//! @cond DEBUG
|
2009-04-24 07:13:10 +00:00
|
|
|
#include <cassert>
|
|
|
|
#include <iostream>
|
|
|
|
#include <iomanip>
|
|
|
|
class MethodTrace {
|
|
|
|
public:
|
|
|
|
MethodTrace(const void* addr, const std::string& name) throw():
|
|
|
|
_addr(addr), _name(name) {
|
|
|
|
std::clog<<std::hex<<std::setw(15)<<_addr<<": "
|
|
|
|
<<std::dec<<std::setw(2+_level)
|
|
|
|
<<std::setfill(' ')<<"\\ "<<_name<<std::endl;
|
|
|
|
++_level;
|
|
|
|
}
|
|
|
|
~MethodTrace() throw() {
|
|
|
|
--_level;
|
|
|
|
std::clog<<std::hex<<std::setw(15)<<_addr<<": "<<std::dec
|
|
|
|
<<std::setw(2+_level)<<std::setfill(' ')
|
|
|
|
<<"/ "<<_name<<std::endl;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
const void* _addr;
|
|
|
|
const std::string _name;
|
|
|
|
static unsigned int _level;
|
|
|
|
};
|
|
|
|
#define TRACE MethodTrace XXX_METHOD(this, __PRETTY_FUNCTION__)
|
|
|
|
#define LOG(X) std::clog<<__PRETTY_FUNCTION__<<"\t**** "<<X<<std::endl
|
2009-04-27 10:48:27 +00:00
|
|
|
#define ASSERT(X, Y) { \
|
|
|
|
if (!(Y)) { \
|
|
|
|
LOG(X); \
|
|
|
|
} \
|
|
|
|
assert(Y); \
|
|
|
|
}
|
2009-05-04 12:47:57 +00:00
|
|
|
//! @endcond
|
2009-04-24 07:13:10 +00:00
|
|
|
|
2009-04-22 08:25:20 +00:00
|
|
|
/*! @mainpage
|
2009-04-02 13:24:59 +00:00
|
|
|
|
2009-04-22 08:25:20 +00:00
|
|
|
@section maintitle C++ XML Class Library
|
|
|
|
|
|
|
|
- Specify your XML schema in C++ using common C++ syntax,
|
|
|
|
such as shift, dereference, etc.
|
|
|
|
- Verify the schema of XML files while they are read from a stream.
|
2009-05-04 12:47:57 +00:00
|
|
|
- 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):
|
2009-04-22 08:25:20 +00:00
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
@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
|
|
|
|
|
2009-04-22 08:25:20 +00:00
|
|
|
@include README
|
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
@page license License is LGPL 3
|
|
|
|
|
|
|
|
File COPYING from http://www.gnu.org/licenses/lgpl-3.0.txt:
|
|
|
|
|
|
|
|
@include COPYING
|
|
|
|
|
|
|
|
@page limits Known Limitations
|
2009-04-22 08:25:20 +00:00
|
|
|
|
|
|
|
- XML-Comments are only ignored, not read, not stored.
|
2009-04-02 13:24:59 +00:00
|
|
|
- Mixed tags and text is not supported. Tags may either contain
|
|
|
|
other tags only (type xml::Node) or text only (type
|
2009-04-22 08:25:20 +00:00
|
|
|
xml::String). -> This is intended behaviour!
|
2009-04-02 13:24:59 +00:00
|
|
|
- Unlimited recursion is not possible
|
|
|
|
(e.g. <p><p><p></p></p></p>)
|
2009-04-22 08:25:20 +00:00
|
|
|
- Exceptions should be optional, best effort otherwise (option "strict")
|
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
@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)
|
2009-05-06 07:22:21 +00:00
|
|
|
- Optional values are supported through xml::Optional
|
2009-05-04 12:47:57 +00:00
|
|
|
- @c std::bitset, @c std::priority_queue, @c std::queue and
|
|
|
|
@c std::stack are not implemented
|
|
|
|
- Polymorfic serialisation is not yet implemented
|
|
|
|
|
2009-04-22 08:25:20 +00:00
|
|
|
@page rationale Rationale - Limitations of other libraries
|
|
|
|
|
|
|
|
The initial idea was to map C++ data structures to XML files
|
|
|
|
(e.g. for configuration files) that can easily be edited by
|
|
|
|
hand. This library does not need any kind of C++ code parser or
|
|
|
|
proprietary pre compiler. You can specify a schema entirly in
|
|
|
|
native C++. Access to the XML structures is through typical C++
|
|
|
|
operators which rresults in a simple and intuitive syntax. The
|
|
|
|
schema is verified when XML is read and exceptions are thrown when
|
|
|
|
the XML to be pares is invalid. Exceptions specify exactly the
|
|
|
|
location and reason of the problem, so that the editor of the XML
|
|
|
|
file can easily find and correct the problem. Due to the
|
|
|
|
verification, it is not necessary to check every access: Only
|
|
|
|
optional attributes and tags must be tested for their existence,
|
|
|
|
before they can be accessed.
|
|
|
|
|
|
|
|
There are a lot of different approaches for using XML in C++, all
|
|
|
|
of them have their specific limitations. This library should be
|
|
|
|
better.
|
|
|
|
|
|
|
|
The design is based on my experiance with gsoap
|
|
|
|
(http://gsoap.sf.net), boost serialization (http://boost.org) and
|
|
|
|
Qt XML (http://qtsoftware.com).
|
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
@section qtxml Qt XML, a typical DOM approach
|
2009-04-22 08:25:20 +00:00
|
|
|
|
|
|
|
One is the XML part of the Qt library. These classes can read XML
|
|
|
|
into a DOM tree, but then the user has to check for every detail.
|
|
|
|
This looks like:
|
|
|
|
|
|
|
|
@code
|
|
|
|
QDomDocument doc;
|
|
|
|
if (!doc.setContent(_http.readAll())); // error
|
|
|
|
QDomNodeList releases(doc.elementsByTagName("release"));
|
|
|
|
for(int i(0); i<releases.size(); ++i)
|
|
|
|
if (releases.at(i).attributes().contains("type") &&
|
|
|
|
releases.at(i).attributes().namedItem("type").nodeValue()==_name) {
|
|
|
|
_software = releases.at(i).firstChildElement("software");
|
|
|
|
_firmware = releases.at(i).firstChildElement("firmware");
|
|
|
|
if (_software.isNull() || _firmware.isNull() ||
|
|
|
|
releases.at(i).firstChildElement("notes").isNull()); // error
|
|
|
|
...
|
|
|
|
@endcode
|
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
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
|
|
|
|
//@{
|
2009-04-23 06:41:50 +00:00
|
|
|
/*! @defgroup xmlConst XML Constant Declarations
|
2009-04-22 16:10:10 +00:00
|
|
|
|
2009-04-23 06:41:50 +00:00
|
|
|
There are macros to help you with declaring constants. Chose a C++
|
2009-05-04 12:47:57 +00:00
|
|
|
header file, where you want to declare constant names for your xml
|
2009-04-23 06:41:50 +00:00
|
|
|
nodes.
|
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
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.
|
2009-04-23 06:41:50 +00:00
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
@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.
|
2009-04-23 06:41:50 +00:00
|
|
|
|
|
|
|
@note Node names must be unique. You can not even use the same
|
|
|
|
name for a XML_NODE and a XML_STRING declaration.
|
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
@see @ref node_macros.cxx
|
2009-04-23 06:41:50 +00:00
|
|
|
|
|
|
|
@example node_macros.cxx
|
|
|
|
|
|
|
|
The example code is equivalent to:
|
2009-04-22 16:10:10 +00:00
|
|
|
|
2009-04-23 06:41:50 +00:00
|
|
|
@code
|
2009-04-22 16:10:10 +00:00
|
|
|
int main(int, char**) {
|
2009-04-23 06:41:50 +00:00
|
|
|
xml::Factory test(xml::Node("base")
|
|
|
|
<<(xml::Node("child")
|
|
|
|
<<xml::String("element"));
|
|
|
|
std::stringstream ss("<base>\n"
|
|
|
|
" <child>\n"
|
|
|
|
" <element>Hello</element>\n"
|
|
|
|
" </child>\n"
|
|
|
|
"</base>");
|
|
|
|
std::auto_ptr<xml::Node> file(test.read(ss));
|
|
|
|
std::cout<<"The element is: "
|
|
|
|
<<(*file)["child"]["element"]
|
|
|
|
<<std::endl;
|
2009-04-22 16:10:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2009-04-23 06:41:50 +00:00
|
|
|
@endcode
|
|
|
|
|
|
|
|
The advantage is the fact that you have no more quoted strings to
|
|
|
|
cope with. So your potential runtime errors through typos become
|
|
|
|
compile time errors, which is more robust and easier to find. If
|
|
|
|
you want to write less code, you are free to use <code>using
|
|
|
|
namespace xml::name</code> in your C++ implementation files, since
|
|
|
|
names are more often used than nodes. (@em Never use @c using in a
|
|
|
|
C++ header file, you would pollute the scope of all the
|
|
|
|
includers.) */
|
2009-05-04 12:47:57 +00:00
|
|
|
//@}
|
|
|
|
//! @addtogroup xmlConst
|
2009-04-23 06:41:50 +00:00
|
|
|
//@{
|
|
|
|
|
|
|
|
//! 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.
|
2009-05-04 12:47:57 +00:00
|
|
|
|
|
|
|
Declares a constant of type @c std::string with name @c xml::name::NAME.
|
|
|
|
|
2009-04-23 06:41:50 +00:00
|
|
|
@see XML_NODE
|
|
|
|
@see XML_STRING */
|
|
|
|
#define XML_NAME(NAME) \
|
|
|
|
namespace xml {\
|
|
|
|
namespace name {\
|
|
|
|
static const std::string NAME(#NAME); \
|
|
|
|
}\
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Define a constant for a xml::Node and for a string containing its name
|
|
|
|
/*! Put this macro in the global part of your header files. After
|
|
|
|
declaration of e.g. <code>XML_NODE(tagname)</code> you can use
|
|
|
|
<code>xml::node::tagname</code> as constant xml::Node and
|
|
|
|
<code>xml::name::tagname</code> as a constant std::string with
|
|
|
|
contents @c "tagname" in your code.
|
|
|
|
@see XML_STRING same for xml::String
|
|
|
|
@see XML_NAME called by XML_NODE */
|
|
|
|
#define XML_NODE(NAME) \
|
|
|
|
XML_NAME(NAME);\
|
2009-04-22 16:10:10 +00:00
|
|
|
namespace xml {\
|
|
|
|
namespace node {\
|
2009-04-23 06:41:50 +00:00
|
|
|
static const xml::Node NAME(#NAME);\
|
2009-04-22 16:10:10 +00:00
|
|
|
}\
|
|
|
|
}
|
|
|
|
|
2009-04-23 06:41:50 +00:00
|
|
|
//! Define a constant for a xml::String and for a string containing its name
|
|
|
|
/*! Put this macro in the global part of your header files. After
|
|
|
|
declaration of e.g. <code>XML_STRING(tagname)</code> you can use
|
|
|
|
<code>xml::string::tagname</code> as constant xml::String and
|
|
|
|
<code>xml::name::tagname</code> as a constant std::string with
|
|
|
|
contents @c "tagname" in your code.
|
|
|
|
@see XML_NODE same for xml::Node
|
|
|
|
@see XML_NAME called by XML_STRING */
|
|
|
|
#define XML_STRING(NAME) \
|
|
|
|
XML_NAME(NAME);\
|
|
|
|
namespace xml {\
|
|
|
|
namespace string {\
|
|
|
|
static const xml::String NAME(#NAME);\
|
|
|
|
}\
|
|
|
|
}
|
|
|
|
|
|
|
|
//! @todo Define a constant for a xml::Node and for a string containing its name
|
|
|
|
/*! Put this macro in the global part of your header files. After
|
|
|
|
declaration of e.g. <code>XML_NODE(tagname)</code> you can use
|
|
|
|
<code>xml::node::tagname</code> as constant xml::Node and
|
|
|
|
<code>xml::name::tagname</code> as a constant std::string with
|
|
|
|
contents @c "tagname" in your code.
|
|
|
|
@see XML_STRING same for xml::String
|
|
|
|
@see XML_NAME called by XML_NODE */
|
|
|
|
#define XML_PARENT(NAME, ...) \
|
|
|
|
XML_NAME(NAME);\
|
|
|
|
namespace xml {\
|
|
|
|
namespace node {\
|
|
|
|
static const xml::Node NAME(#NAME);\
|
|
|
|
}\
|
|
|
|
}
|
|
|
|
|
|
|
|
//@}
|
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
/*! @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.
|
2009-04-06 07:08:52 +00:00
|
|
|
|
|
|
|
A xml::Factory represents a factory that owns a template and can
|
|
|
|
instanciate XML trees that are valid for the given template from
|
|
|
|
streams. If anything is not valid, an exception is thrown. The @c
|
|
|
|
what() method of the exception gives additional information about
|
|
|
|
the problem.
|
|
|
|
|
|
|
|
In the following example, we want to represent XML data that are
|
|
|
|
contained in a <persons> tag, and may contain a list of @c
|
|
|
|
person. Each @c person has a mandatory attribute @c id and
|
|
|
|
optional @c member-of. @c person has a @c name and may contain a
|
|
|
|
list of @c friends, where each @c friend has an attribute @c
|
|
|
|
id. (The @c id attribute of course should reference to the @c id
|
|
|
|
of another @c name, but this relationship cannot be declared.)
|
|
|
|
|
|
|
|
All tags are by default specified as 0..n (optional and any number
|
|
|
|
there of).
|
|
|
|
|
2009-04-07 06:59:17 +00:00
|
|
|
@code
|
|
|
|
#include <xml-cxx/xml.hxx>
|
|
|
|
#include <iostream>
|
|
|
|
[...]
|
2009-04-06 07:08:52 +00:00
|
|
|
xml::Factory test(xml::Node("persons") // root node
|
|
|
|
<<(xml::Node("person") // child of persons
|
|
|
|
.attr("id", xml::mandatory)
|
|
|
|
.attr("member-of", xml::optional))
|
|
|
|
<<xml::String("name") // the person's name
|
|
|
|
<<(xml::Node("friends") // friends of person
|
|
|
|
<<(xml::Node("friend") // a friend
|
|
|
|
.attr("id", xml::mandatory)))));
|
2009-04-07 06:59:17 +00:00
|
|
|
[...]
|
2009-04-06 07:08:52 +00:00
|
|
|
try {
|
|
|
|
std::auto_ptr<xml::Node> persons(test.read(std::ifstream("file.xml)));
|
|
|
|
// Here we can be sure, that our structure is valid,
|
|
|
|
// but we must check optional elements before access, otherwise
|
|
|
|
// we get an exception.
|
2009-04-07 06:59:17 +00:00
|
|
|
[...]
|
2009-04-06 07:08:52 +00:00
|
|
|
for (xml::Node::size_type i(0); i<persons.children(); ++i) {
|
|
|
|
std::cout<<"Person: "<<*persons[i]["name"]; // exception if no "name"
|
|
|
|
if (persons[i]("friends")) // check if "friends" is set
|
|
|
|
std::cout<<" has "<<persons[i]["friends"].children()<<" friends"
|
|
|
|
else
|
|
|
|
std::cout<<" has no friend list";
|
|
|
|
std::cout<<std::endl;
|
|
|
|
}
|
2009-04-07 06:59:17 +00:00
|
|
|
[...]
|
2009-04-06 07:08:52 +00:00
|
|
|
} catch (const std::exception& x) {
|
|
|
|
std::cerr<<"**** Error in file \"file.xml\":"<<std::endl
|
|
|
|
<<x.what()<<std::endl;
|
|
|
|
}
|
2009-04-07 06:59:17 +00:00
|
|
|
@endcode */
|
2009-05-04 12:47:57 +00:00
|
|
|
|
|
|
|
//! Everything is in namespace xml
|
2009-04-06 07:08:52 +00:00
|
|
|
namespace xml {
|
2009-05-04 12:47:57 +00:00
|
|
|
|
2010-08-03 13:50:09 +00:00
|
|
|
std::string version();
|
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
//! @addtogroup freexml
|
|
|
|
//@{
|
|
|
|
|
|
|
|
//! @cond INTERNAL
|
|
|
|
|
2009-04-02 13:24:59 +00:00
|
|
|
//============================================================================
|
2009-04-07 06:59:17 +00:00
|
|
|
//! Type of an xml node.
|
|
|
|
/*! Only start nodes and empty nodes may have attributes. */
|
|
|
|
enum NodeType {
|
|
|
|
START, //!< start node, such as <code><node></code>
|
|
|
|
END, //!< end node, such as <code></node></code>
|
|
|
|
EMPTY, //!< empty node, such as <code><node/></code>
|
|
|
|
SPECIAL //!< special node, such as
|
|
|
|
//! a comment <code><!-- ... --></code>,
|
|
|
|
//! a xml start indication <code><?xml?></code>
|
|
|
|
//! or a document type declaration <code><!DOCTYPE ...></code>
|
|
|
|
};
|
2009-05-04 12:47:57 +00:00
|
|
|
|
|
|
|
//! @endcond
|
|
|
|
|
2009-04-07 06:59:17 +00:00
|
|
|
//! Declares an attribute to be mandatory.
|
|
|
|
const bool mandatory(true);
|
|
|
|
//! Declares an attribute to be optional.
|
|
|
|
const bool optional(false);
|
2009-04-02 13:24:59 +00:00
|
|
|
|
|
|
|
//================================================================= EXCEPTIONS
|
2009-04-06 07:08:52 +00:00
|
|
|
struct Tag;
|
|
|
|
class Attributes;
|
2009-04-02 13:24:59 +00:00
|
|
|
class Node;
|
|
|
|
class Factory;
|
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
//@}
|
|
|
|
//! @defgroup exceptions Exception classes
|
|
|
|
//@{
|
|
|
|
|
2009-04-02 13:24:59 +00:00
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
class exception: public std::exception {
|
|
|
|
public:
|
2009-04-06 07:08:52 +00:00
|
|
|
exception(std::string reason) throw();
|
2009-04-02 13:24:59 +00:00
|
|
|
exception(std::string reason, const Node& t) throw();
|
|
|
|
~exception() throw();
|
2009-04-07 14:31:46 +00:00
|
|
|
void line(unsigned long line) throw();
|
2009-04-02 13:24:59 +00:00
|
|
|
const char* what() const throw();
|
|
|
|
private:
|
|
|
|
std::string _what;
|
|
|
|
Node* _node;
|
|
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
2009-04-30 08:28:52 +00:00
|
|
|
class type_not_registered: public exception {
|
|
|
|
public:
|
|
|
|
type_not_registered(std::string type, std::string name, const Node& t):
|
|
|
|
exception("serialized node type is not registered\ntype: "
|
|
|
|
+type+"\nname: "+name, t) {
|
|
|
|
}
|
2009-05-04 12:47:57 +00:00
|
|
|
type_not_registered(std::string type):
|
|
|
|
exception("serialized node type is not registered\ntype: "+type) {
|
|
|
|
}
|
2009-04-30 08:28:52 +00:00
|
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
2009-04-09 10:53:03 +00:00
|
|
|
class empty_attribute_list: public exception {
|
|
|
|
public:
|
|
|
|
empty_attribute_list(const std::string& name) throw():
|
|
|
|
exception("list of attribute is empty access to first element failed"
|
|
|
|
"\nattribute: "+name) {
|
|
|
|
}
|
|
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
2009-04-22 16:10:10 +00:00
|
|
|
class factory_not_valid: public exception {
|
|
|
|
public:
|
|
|
|
factory_not_valid() throw():
|
|
|
|
exception("a factory must be given a template node") {
|
|
|
|
}
|
|
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
2009-04-02 13:24:59 +00:00
|
|
|
class no_parent: public exception {
|
|
|
|
public:
|
|
|
|
no_parent(const Node& t) throw(): exception("node has no parent", t) {}
|
|
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
class tag_expected: public exception {
|
|
|
|
public:
|
|
|
|
tag_expected(const Node& t, const std::string& txt) throw():
|
|
|
|
exception("tag ('<') expected, text not allowed\ntext: "+txt, t) {}
|
|
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
2009-04-03 07:07:49 +00:00
|
|
|
class type_mismatch: public exception {
|
|
|
|
public:
|
2009-04-03 14:02:22 +00:00
|
|
|
type_mismatch(const Node& t, const std::string& txt,
|
|
|
|
const std::string& comment) throw():
|
|
|
|
exception("wrong type, text contains mismatching character\n"+comment
|
|
|
|
+"\ntext: "+txt, t) {}
|
2009-04-03 07:07:49 +00:00
|
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
2009-04-02 13:24:59 +00:00
|
|
|
class access_error: public exception {
|
|
|
|
public:
|
|
|
|
access_error(const Node& t, const std::string& name) throw();
|
|
|
|
~access_error() throw() {}
|
|
|
|
const char* what() const throw();
|
|
|
|
private:
|
|
|
|
std::string _name;
|
|
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
class out_of_range: public exception {
|
|
|
|
public:
|
|
|
|
out_of_range(const Node& t, size_t pos) throw();
|
|
|
|
~out_of_range() throw() {}
|
|
|
|
const char* what() const throw();
|
|
|
|
private:
|
|
|
|
size_t _pos;
|
|
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
class cannot_have_children: public exception {
|
|
|
|
public:
|
|
|
|
cannot_have_children(const Node& parent, const Node& child) throw();
|
|
|
|
~cannot_have_children() throw();
|
|
|
|
const char* what() const throw();
|
|
|
|
private:
|
|
|
|
Node* _child;
|
|
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
2009-04-08 15:01:48 +00:00
|
|
|
class attribute_not_available: public exception {
|
|
|
|
public:
|
|
|
|
attribute_not_available(const Node& t, const std::string& attr) throw():
|
|
|
|
exception("attribute \""+attr+"\" not set", t) {
|
|
|
|
}
|
|
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
2009-04-02 13:24:59 +00:00
|
|
|
class stream_error: public exception {
|
|
|
|
public:
|
|
|
|
stream_error(const std::string& reason, const Node& t,
|
2009-04-06 07:08:52 +00:00
|
|
|
std::istream& is, const Tag& tag, char c=0) throw();
|
2009-04-09 10:53:03 +00:00
|
|
|
stream_error(const std::string& reason, const Node& t,
|
|
|
|
std::istream& is) throw();
|
2009-04-06 07:08:52 +00:00
|
|
|
~stream_error() throw();
|
2009-04-02 13:24:59 +00:00
|
|
|
const char* what() const throw();
|
|
|
|
private:
|
|
|
|
std::istream::streampos _pos;
|
2009-04-06 07:08:52 +00:00
|
|
|
Tag* _tag;
|
2009-04-02 13:24:59 +00:00
|
|
|
char _char;
|
|
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
class wrong_end_tag: public stream_error {
|
|
|
|
public:
|
2009-04-06 07:08:52 +00:00
|
|
|
wrong_end_tag(const Node& t, std::istream& is, const Tag& tag, char c=0)
|
2009-04-02 13:24:59 +00:00
|
|
|
throw():
|
|
|
|
stream_error("mismatching end tag", t, is, tag, c) {
|
|
|
|
}
|
|
|
|
};
|
2009-05-04 12:47:57 +00:00
|
|
|
//----------------------------------------------------------------------------
|
2009-04-02 13:24:59 +00:00
|
|
|
class missing_end_tag: public stream_error {
|
|
|
|
public:
|
2009-04-06 07:08:52 +00:00
|
|
|
missing_end_tag(const Node& t, std::istream& is, const Tag& tag, char c=0)
|
2009-04-02 13:24:59 +00:00
|
|
|
throw():
|
|
|
|
stream_error("missing end tag, end of file reached", t, is, tag, c) {
|
|
|
|
}
|
|
|
|
};
|
2009-05-04 12:47:57 +00:00
|
|
|
//----------------------------------------------------------------------------
|
2009-04-02 13:24:59 +00:00
|
|
|
class wrong_start_tag: public stream_error {
|
|
|
|
public:
|
2009-04-06 07:08:52 +00:00
|
|
|
wrong_start_tag(const Node& t, std::istream& is, const Tag& tag, char c=0)
|
2009-04-02 13:24:59 +00:00
|
|
|
throw():
|
|
|
|
stream_error("start tag does not match expected tag", t, is, tag, c) {
|
|
|
|
}
|
|
|
|
};
|
2009-05-04 12:47:57 +00:00
|
|
|
//----------------------------------------------------------------------------
|
2009-04-02 13:24:59 +00:00
|
|
|
class second_slash_in_tag: public stream_error {
|
|
|
|
public:
|
2009-04-06 07:08:52 +00:00
|
|
|
second_slash_in_tag(const Node& t, std::istream& is, const Tag& tag,
|
|
|
|
char c=0)
|
2009-04-02 13:24:59 +00:00
|
|
|
throw():
|
|
|
|
stream_error("a tag may have no more than one slash", t, is, tag, c) {
|
|
|
|
}
|
|
|
|
};
|
2009-05-04 12:47:57 +00:00
|
|
|
//----------------------------------------------------------------------------
|
2009-04-02 13:24:59 +00:00
|
|
|
class character_after_slash: public stream_error {
|
|
|
|
public:
|
2009-04-06 07:08:52 +00:00
|
|
|
character_after_slash(const Node& t, std::istream& is, const Tag& tag,
|
|
|
|
char c=0)
|
2009-04-02 13:24:59 +00:00
|
|
|
throw():
|
|
|
|
stream_error("unexpected character after empty-slash",
|
|
|
|
t, is, tag, c) {
|
|
|
|
}
|
|
|
|
};
|
2009-05-04 12:47:57 +00:00
|
|
|
//----------------------------------------------------------------------------
|
2009-04-02 13:24:59 +00:00
|
|
|
class attributes_in_end_tag: public stream_error {
|
|
|
|
public:
|
2009-04-06 07:08:52 +00:00
|
|
|
attributes_in_end_tag(const Node& t, std::istream& is, const Tag& tag)
|
2009-04-02 13:24:59 +00:00
|
|
|
throw():
|
|
|
|
stream_error("attributes are not allowed in end tags",t, is, tag) {
|
|
|
|
}
|
|
|
|
};
|
2009-05-04 12:47:57 +00:00
|
|
|
//----------------------------------------------------------------------------
|
2009-04-02 13:24:59 +00:00
|
|
|
class attribute_value_not_quoted: public stream_error {
|
|
|
|
public:
|
2009-04-06 07:08:52 +00:00
|
|
|
attribute_value_not_quoted(const Node& t, std::istream& is,
|
|
|
|
const Tag& tag,
|
2009-04-02 13:24:59 +00:00
|
|
|
char c, std::string attr) throw():
|
|
|
|
stream_error("attribute values must be quoted (\")\nattribute: "+attr,
|
|
|
|
t, is, tag, c) {
|
|
|
|
}
|
|
|
|
};
|
2009-05-04 12:47:57 +00:00
|
|
|
//----------------------------------------------------------------------------
|
2009-04-02 13:24:59 +00:00
|
|
|
class duplicate_attribute: public stream_error {
|
|
|
|
public:
|
2009-04-06 07:08:52 +00:00
|
|
|
duplicate_attribute(const Node& t, std::istream& is, const Tag& tag,
|
2009-04-02 13:24:59 +00:00
|
|
|
std::string attr) throw():
|
|
|
|
stream_error("attribute duplicated\nattribute: "+attr,
|
|
|
|
t, is, tag, 0) {
|
|
|
|
}
|
|
|
|
};
|
2009-05-04 12:47:57 +00:00
|
|
|
//----------------------------------------------------------------------------
|
2009-04-09 07:01:25 +00:00
|
|
|
class illegal_attribute: public stream_error {
|
|
|
|
public:
|
|
|
|
illegal_attribute(const Node& t, std::istream& is, const Tag& tag,
|
|
|
|
std::string attr) throw():
|
|
|
|
stream_error("illegal attribute found\nattribute: "+attr,
|
|
|
|
t, is, tag, 0) {
|
|
|
|
}
|
|
|
|
};
|
2009-05-04 12:47:57 +00:00
|
|
|
//----------------------------------------------------------------------------
|
2009-04-09 07:01:25 +00:00
|
|
|
class mandatory_attribute_missing: public stream_error {
|
|
|
|
public:
|
|
|
|
mandatory_attribute_missing(const Node& t, std::istream& is,
|
|
|
|
const Tag& tag, std::string attr) throw():
|
|
|
|
stream_error("mandatory attribute missing\nattribute: "+attr,
|
|
|
|
t, is, tag, 0) {
|
|
|
|
}
|
|
|
|
};
|
2009-05-04 12:47:57 +00:00
|
|
|
//----------------------------------------------------------------------------
|
2009-04-09 07:01:25 +00:00
|
|
|
class wrong_node_number: public stream_error {
|
|
|
|
public:
|
|
|
|
wrong_node_number(const Node& t, std::istream& is,
|
2009-04-09 10:53:03 +00:00
|
|
|
const std::string& name,
|
2009-04-09 07:01:25 +00:00
|
|
|
unsigned long num,
|
|
|
|
unsigned long min, unsigned long max) throw():
|
2009-04-09 10:53:03 +00:00
|
|
|
stream_error("wrong number of child nodes\nname of child: "+name
|
|
|
|
+"\nnumber of nodes: "+conv(num)
|
|
|
|
+"\nminimuml number: "+conv(min)
|
|
|
|
+"\nmaximum number: "+conv(max), t, is) {
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
static std::string conv(unsigned long i) throw() {
|
|
|
|
std::stringstream ss;
|
|
|
|
ss<<i;
|
|
|
|
return ss.str();
|
2009-04-09 07:01:25 +00:00
|
|
|
}
|
|
|
|
};
|
2009-05-04 12:47:57 +00:00
|
|
|
//! @}
|
2009-04-02 13:24:59 +00:00
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
//! @addtogroup freexml
|
|
|
|
//@{
|
|
|
|
|
2009-04-06 07:08:52 +00:00
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//! Map for attribute values.
|
|
|
|
/*! Attributes can be set using method xml::Node::attr(). Check for
|
|
|
|
an attribute with xml::Node::hasAttr(). Attributes must be
|
|
|
|
unique, which means that every attribute must be set at maximum
|
|
|
|
once. This is corect: <code><node
|
|
|
|
attribute="value"></code>, this is not allowed:
|
|
|
|
<code><node attribute="value" attribute="value"></code> */
|
2009-04-08 15:01:48 +00:00
|
|
|
class Attributes: public std::map<std::string, std::string> {
|
2009-04-06 07:08:52 +00:00
|
|
|
public:
|
|
|
|
//! Attributes may contain a list of space separated values.
|
|
|
|
typedef std::vector<std::string> List;
|
|
|
|
//! Attribute values ar mainly a std::pair.
|
|
|
|
/*! In addition to a normal std::pair, attributes offer an
|
|
|
|
assignment operator to set the value, and can be constructed
|
2009-04-07 06:59:17 +00:00
|
|
|
as empty attribute, given only a key.
|
|
|
|
@note Simply use xml::Attr instead of xml::Attributes::Value. */
|
2009-04-06 07:08:52 +00:00
|
|
|
class Value: public value_type {
|
|
|
|
public:
|
2009-04-07 06:59:17 +00:00
|
|
|
Value(const value_type& o) throw();
|
2009-04-06 07:08:52 +00:00
|
|
|
Value(const std::string& name) throw();
|
|
|
|
Value(const std::string& name, const std::string& namevalue) throw();
|
2009-04-07 06:59:17 +00:00
|
|
|
Value& operator=(const std::string& value) throw();
|
2009-04-06 07:08:52 +00:00
|
|
|
const std::string& name() const throw();
|
|
|
|
const std::string& value() const throw();
|
|
|
|
std::string& value() throw();
|
|
|
|
operator bool() const throw();
|
2010-03-31 15:13:10 +00:00
|
|
|
bool toBool() const throw();
|
2009-04-06 07:08:52 +00:00
|
|
|
operator unsigned long() const throw();
|
2010-03-31 15:13:10 +00:00
|
|
|
unsigned long toNumber() const throw();
|
2009-04-06 07:08:52 +00:00
|
|
|
operator List() const throw();
|
|
|
|
List toList(const std::string& separators=" \t\n\r") const throw();
|
2009-04-09 10:53:03 +00:00
|
|
|
std::string front(const std::string& separators=" \t\n\r") const
|
|
|
|
throw(empty_attribute_list);
|
2009-04-06 07:08:52 +00:00
|
|
|
private:
|
|
|
|
Value(); // not implemented, key must always be given
|
|
|
|
};
|
|
|
|
Attributes() throw();
|
|
|
|
Attributes(const std::string& empty) throw();
|
2009-04-07 06:59:17 +00:00
|
|
|
Attributes(const std::string& key, const std::string& value) throw();
|
|
|
|
Attributes& operator<<(const Value& v) throw();
|
2009-04-06 07:08:52 +00:00
|
|
|
Attributes& operator<<(const std::string& key) throw();
|
|
|
|
};
|
2009-04-07 06:59:17 +00:00
|
|
|
//! Simplification: Use xml::Attr instead of xml::Attributes::Value.
|
2009-04-06 07:08:52 +00:00
|
|
|
typedef Attributes::Value Attr;
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
2009-04-07 06:59:17 +00:00
|
|
|
//! @internal structure for parsing tags
|
2009-04-06 07:08:52 +00:00
|
|
|
struct Tag {
|
|
|
|
std::string name;
|
|
|
|
NodeType type;
|
|
|
|
std::string text;
|
|
|
|
Attributes attributes;
|
|
|
|
std::string special;
|
2009-04-09 07:01:25 +00:00
|
|
|
bool found;
|
2009-04-06 07:08:52 +00:00
|
|
|
};
|
|
|
|
|
2009-04-02 13:24:59 +00:00
|
|
|
//----------------------------------------------------------------------------
|
2009-05-04 12:47:57 +00:00
|
|
|
//! An xml Node that contains child nodes but no text.
|
2009-04-07 06:59:17 +00:00
|
|
|
/*! XML Nodes may contain either text or other nodes, but not both
|
2009-04-08 06:44:18 +00:00
|
|
|
at the same time. This node can hold other nodes. For a Node for
|
|
|
|
text contents, see xml::String. */
|
2009-04-02 13:24:59 +00:00
|
|
|
class Node {
|
|
|
|
private:
|
|
|
|
typedef std::vector<Node*> Contents;
|
|
|
|
public:
|
|
|
|
typedef Contents::size_type size_type;
|
2009-04-06 14:57:27 +00:00
|
|
|
typedef std::vector<Node*> List;
|
2009-04-09 07:01:25 +00:00
|
|
|
Node(std::string name, size_type min=0, size_type max=0) throw();
|
2009-04-02 13:24:59 +00:00
|
|
|
Node(const Node& o) throw();
|
|
|
|
virtual ~Node() throw();
|
2009-04-23 06:41:50 +00:00
|
|
|
virtual Node& operator=(const Node& o) throw();
|
2009-04-02 13:24:59 +00:00
|
|
|
virtual std::auto_ptr<Node> clone() const throw();
|
|
|
|
virtual std::ostream& out(std::ostream& o, unsigned int level=0) const
|
|
|
|
throw();
|
|
|
|
virtual std::string text() const throw();
|
2009-04-03 07:07:49 +00:00
|
|
|
virtual Node& text(const std::string& txt) throw(tag_expected,
|
|
|
|
type_mismatch);
|
2009-04-02 13:24:59 +00:00
|
|
|
virtual Node& append(const Node& o) throw(cannot_have_children);
|
2009-05-06 07:13:50 +00:00
|
|
|
virtual Node& remove(Node& n) throw(access_error);
|
|
|
|
virtual Node& remove(const std::string& n) throw(access_error);
|
|
|
|
virtual Node& remove(size_type n) throw(out_of_range);
|
2009-04-02 13:24:59 +00:00
|
|
|
virtual Node& set(const Attributes& o) throw();
|
|
|
|
Node& clear() throw ();
|
|
|
|
std::string name() const throw();
|
2009-04-27 10:48:27 +00:00
|
|
|
Node& name(const std::string& n) throw();
|
2009-04-09 10:53:03 +00:00
|
|
|
Node& min(size_type m) throw();
|
|
|
|
size_type min() const throw();
|
|
|
|
Node& max(size_type m) throw();
|
|
|
|
size_type max() const throw();
|
2009-04-02 13:24:59 +00:00
|
|
|
bool isChild() const throw();
|
|
|
|
Node& parent() const throw(no_parent);
|
|
|
|
bool hasAttr(const std::string& name) const throw();
|
|
|
|
Node& attr(const std::string& name, bool mandatory) throw();
|
2009-04-09 07:01:25 +00:00
|
|
|
Node& attr(const std::string& name, const std::string& deflt) throw();
|
2009-04-02 13:24:59 +00:00
|
|
|
std::string attr(const std::string& name) const throw();
|
|
|
|
std::string& attr(const std::string& name) throw();
|
2009-04-08 15:01:48 +00:00
|
|
|
const Attributes::Value attribute(const std::string& name)
|
|
|
|
const throw(attribute_not_available);
|
2009-04-09 07:01:25 +00:00
|
|
|
const Attributes& attributes() const throw();
|
|
|
|
Attributes& attributes() throw();
|
|
|
|
Node& limits(size_type min=0, size_type max=0) throw();
|
2009-04-06 14:57:27 +00:00
|
|
|
List list(const std::string& name) const throw();
|
2009-04-02 13:24:59 +00:00
|
|
|
bool operator()(const std::string& child) const throw();
|
|
|
|
Node& operator<<(const Node& o) throw(cannot_have_children);
|
|
|
|
Node& operator<<(const Attributes& o) throw();
|
|
|
|
size_type children() const throw();
|
2009-04-09 07:01:25 +00:00
|
|
|
const Node& operator[](size_type child) const throw(out_of_range);
|
|
|
|
Node& operator[](size_type child) throw(out_of_range);
|
2009-04-02 13:24:59 +00:00
|
|
|
const Node& operator[](const std::string& child) const
|
|
|
|
throw(access_error);
|
|
|
|
Node& operator[](const std::string& child) throw(access_error);
|
|
|
|
std::string operator*() const throw();
|
2009-04-03 07:07:49 +00:00
|
|
|
Node& operator=(const std::string& contents) throw(tag_expected,
|
|
|
|
type_mismatch);
|
2009-04-02 13:24:59 +00:00
|
|
|
friend std::ostream& operator<<(std::ostream& o, const Node& t) throw();
|
|
|
|
protected:
|
|
|
|
Attributes _attributes;
|
|
|
|
private:
|
|
|
|
Node* find(const std::string& child) const throw();
|
|
|
|
virtual std::auto_ptr<Node> clone(Node* p) const throw();
|
|
|
|
Node(); // not implemented
|
|
|
|
Contents _contents;
|
|
|
|
std::string _name;
|
|
|
|
Node* _parent;
|
2009-04-09 10:53:03 +00:00
|
|
|
size_type _min;
|
|
|
|
size_type _max;
|
2009-04-02 13:24:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
2009-05-04 12:47:57 +00:00
|
|
|
//! A leaf node that contains text but no child nodes.
|
2009-04-02 13:24:59 +00:00
|
|
|
class String: public Node {
|
|
|
|
public:
|
2009-04-20 06:42:09 +00:00
|
|
|
String(std::string name,
|
|
|
|
Node::size_type min=0, Node::size_type max=0) throw();
|
2009-04-09 10:53:03 +00:00
|
|
|
String(std::string name, const std::string& text,
|
2009-04-20 06:42:09 +00:00
|
|
|
Node::size_type min=0, Node::size_type max=0) throw();
|
2009-04-02 13:24:59 +00:00
|
|
|
virtual ~String() throw() {}
|
2009-04-23 06:41:50 +00:00
|
|
|
virtual std::auto_ptr<Node> clone() const throw();
|
2009-04-02 13:24:59 +00:00
|
|
|
virtual std::string text() const throw();
|
2009-04-03 07:07:49 +00:00
|
|
|
virtual String& text(const std::string& txt) throw(tag_expected,
|
|
|
|
type_mismatch);
|
|
|
|
virtual std::ostream& out(std::ostream& o, unsigned int level=0) const
|
|
|
|
throw();
|
|
|
|
virtual String& append(const Node& o) throw(cannot_have_children);
|
|
|
|
Node& operator=(const std::string& contents) throw();
|
2009-04-22 16:10:10 +00:00
|
|
|
operator std::string() const throw();
|
|
|
|
operator bool() const throw();
|
2009-04-27 10:48:27 +00:00
|
|
|
operator char() const throw();
|
2009-04-22 16:10:10 +00:00
|
|
|
operator signed char() const throw();
|
|
|
|
operator unsigned char() const throw();
|
|
|
|
operator signed short() const throw();
|
|
|
|
operator unsigned short() const throw();
|
|
|
|
operator signed int() const throw();
|
|
|
|
operator unsigned int() const throw();
|
|
|
|
operator signed long() const throw();
|
|
|
|
operator unsigned long() const throw();
|
|
|
|
operator float() const throw();
|
|
|
|
operator double() const throw();
|
2009-04-03 14:02:22 +00:00
|
|
|
protected:
|
2009-04-03 07:07:49 +00:00
|
|
|
std::string _text;
|
|
|
|
};
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
2009-05-04 12:47:57 +00:00
|
|
|
//! A leaf node that contains only numbers and no child nodes.
|
2009-04-03 14:02:22 +00:00
|
|
|
class UnsignedInteger: public String {
|
2009-04-03 07:07:49 +00:00
|
|
|
public:
|
2009-04-09 10:53:03 +00:00
|
|
|
UnsignedInteger(std::string name, unsigned long i=0,
|
|
|
|
size_type min=0, size_type max=0) throw();
|
2009-04-03 07:07:49 +00:00
|
|
|
virtual std::auto_ptr<Node> clone() const throw();
|
2009-04-03 14:02:22 +00:00
|
|
|
virtual ~UnsignedInteger() throw() {}
|
|
|
|
virtual UnsignedInteger& text(const std::string& txt)
|
|
|
|
throw(tag_expected, type_mismatch);
|
|
|
|
unsigned long number() const throw();
|
|
|
|
static unsigned long number(const Node& node) throw();
|
2009-04-02 13:24:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
2009-05-04 12:47:57 +00:00
|
|
|
//! 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 */
|
2009-04-02 13:24:59 +00:00
|
|
|
class Factory {
|
|
|
|
public:
|
|
|
|
Factory(const Node& t) throw();
|
2009-04-22 16:10:10 +00:00
|
|
|
Factory() throw();
|
|
|
|
Factory& operator=(const Node& t) throw();
|
|
|
|
Factory& append(const Node& node) throw();
|
|
|
|
const Node& operator*() const throw(factory_not_valid);
|
2009-04-24 07:13:10 +00:00
|
|
|
const Node*const operator->() const throw(factory_not_valid);
|
2009-04-22 16:10:10 +00:00
|
|
|
operator bool() const throw();
|
2009-04-09 13:32:15 +00:00
|
|
|
friend std::ostream& operator<<(std::ostream& os,
|
2009-05-04 12:47:57 +00:00
|
|
|
const Factory& factory)
|
|
|
|
throw(factory_not_valid);
|
2009-04-09 13:32:15 +00:00
|
|
|
static std::ostream& print(std::ostream& os, const Node& node,
|
|
|
|
unsigned int level=0) throw();
|
2009-04-02 13:24:59 +00:00
|
|
|
std::auto_ptr<Node> read(std::istream& is)
|
2009-04-03 07:07:49 +00:00
|
|
|
throw(wrong_end_tag, wrong_start_tag, tag_expected, type_mismatch,
|
2009-04-02 13:24:59 +00:00
|
|
|
second_slash_in_tag, character_after_slash,
|
|
|
|
missing_end_tag, attribute_value_not_quoted, access_error,
|
2009-04-09 07:01:25 +00:00
|
|
|
duplicate_attribute, attributes_in_end_tag,
|
|
|
|
illegal_attribute, mandatory_attribute_missing,
|
2009-05-04 12:47:57 +00:00
|
|
|
wrong_node_number, factory_not_valid);
|
2009-04-27 10:48:27 +00:00
|
|
|
void reset() throw();
|
2009-04-02 13:24:59 +00:00
|
|
|
private:
|
|
|
|
friend class stream_error;
|
2009-04-27 10:48:27 +00:00
|
|
|
friend class Serialize;
|
2009-05-06 07:13:50 +00:00
|
|
|
template<class T> friend class Optional;
|
2009-05-04 12:47:57 +00:00
|
|
|
template<class T> friend class Container;
|
|
|
|
template<class T> friend class AssociativeContainer;
|
|
|
|
template<class T> friend class AssociativeMap;
|
2009-04-27 10:48:27 +00:00
|
|
|
Node& operator*() throw(factory_not_valid);
|
|
|
|
Node*const operator->() throw(factory_not_valid);
|
2009-04-07 14:31:46 +00:00
|
|
|
bool ws(char c) throw();
|
2009-04-02 13:24:59 +00:00
|
|
|
std::auto_ptr<Node> read(std::istream& is, const Node& position)
|
2009-04-03 07:07:49 +00:00
|
|
|
throw(wrong_end_tag, wrong_start_tag, tag_expected, type_mismatch,
|
2009-04-02 13:24:59 +00:00
|
|
|
second_slash_in_tag, character_after_slash,
|
|
|
|
missing_end_tag,
|
|
|
|
attribute_value_not_quoted, access_error, duplicate_attribute,
|
2009-04-09 07:01:25 +00:00
|
|
|
attributes_in_end_tag,
|
|
|
|
illegal_attribute, mandatory_attribute_missing,
|
|
|
|
wrong_node_number);
|
2009-04-09 10:53:03 +00:00
|
|
|
std::auto_ptr<Node> checkChildren(const xml::Node& tpl,
|
|
|
|
std::auto_ptr<Node> node,
|
|
|
|
std::istream& is) const
|
|
|
|
throw(wrong_node_number);
|
2009-04-02 13:24:59 +00:00
|
|
|
Tag tag(std::istream& is, const Node& position)
|
2009-04-09 07:01:25 +00:00
|
|
|
throw(second_slash_in_tag, wrong_start_tag, character_after_slash,
|
|
|
|
missing_end_tag, attributes_in_end_tag, tag_expected,
|
|
|
|
attribute_value_not_quoted, access_error, duplicate_attribute,
|
2009-04-09 10:53:03 +00:00
|
|
|
illegal_attribute, mandatory_attribute_missing);
|
2009-04-09 07:01:25 +00:00
|
|
|
Node _template;
|
2009-04-07 14:31:46 +00:00
|
|
|
unsigned long _line;
|
2009-04-09 07:01:25 +00:00
|
|
|
long _open;
|
2009-04-02 13:24:59 +00:00
|
|
|
};
|
2009-05-04 12:47:57 +00:00
|
|
|
//@}
|
2009-04-02 13:24:59 +00:00
|
|
|
|
2009-04-23 15:10:21 +00:00
|
|
|
/*! @defgroup serialization Class Serialization
|
|
|
|
|
|
|
|
@section serIntro Introduction
|
|
|
|
|
|
|
|
Boost library (http://boost.org) offers a serialization framework,
|
|
|
|
which is able to serialize even complex class structures, you only
|
|
|
|
need to overwrite one or two serialization macros. The
|
|
|
|
disadvantages are that a lot of macros are needed and it becomes
|
|
|
|
quite complex as soon as you need inheritance. Also the generated
|
|
|
|
XML is not very enhanced, especially for Lists and
|
|
|
|
optional. Editing the boost serialization code by hand is a pain.
|
|
|
|
|
|
|
|
Classes could also be serialized using gSOAP (http://gsoap.sf.net)
|
|
|
|
which is designed for the SOA-Protocol. This serialization is much
|
|
|
|
more flexible, but it requires a pseudo C++ declaration and a C++
|
|
|
|
parser/generator. Also it has very bad memory management, since it
|
|
|
|
is plain C internally.
|
|
|
|
|
|
|
|
Our requirements are:
|
|
|
|
- No precompiler, plain C++.
|
|
|
|
- Automatic memory management.
|
|
|
|
- Nice looking XML code that is easy to edit manually.
|
|
|
|
- Good error messages (exception) in case of bad XML files.
|
|
|
|
- As few ugly overflow as possible.
|
|
|
|
|
|
|
|
@section serActual Actual Status
|
|
|
|
|
2009-04-24 15:12:44 +00:00
|
|
|
The following member types are supported
|
2009-05-04 12:47:57 +00:00
|
|
|
- All built-in C++ types are supported, except enum
|
|
|
|
- @c std::string is supported
|
2009-04-24 15:12:44 +00:00
|
|
|
- Contained classes are supported
|
2009-05-04 12:47:57 +00:00
|
|
|
- Inheritance
|
|
|
|
- @ref serContainer
|
2009-04-24 15:12:44 +00:00
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
@todo The following will be supported soon (ideas):
|
2009-04-24 15:12:44 +00:00
|
|
|
- choices (one of)
|
2009-05-04 12:47:57 +00:00
|
|
|
- choices (polymorfism)
|
2009-04-24 15:12:44 +00:00
|
|
|
- optional members (pointer)
|
2009-05-04 12:47:57 +00:00
|
|
|
- enum (class xml::Enum)
|
2009-04-24 15:12:44 +00:00
|
|
|
|
|
|
|
Pointers cannot be stored.
|
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
@section serBestPract Best Practice and Inheritance
|
2009-04-23 15:10:21 +00:00
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
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.
|
2009-04-23 15:10:21 +00:00
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
@warning If you do not follow the best practice, you must know
|
|
|
|
what you are doing to prevent crashing: You must know that
|
|
|
|
xml::Serialize stores pointers to the variables given in
|
|
|
|
xml::Serialize::persist. So be careful and don't access
|
|
|
|
xml::Serialize after the referenced variables have been removed
|
|
|
|
from memory.
|
|
|
|
|
|
|
|
@subsection inheritance Inheritance
|
|
|
|
|
|
|
|
If you follow the best practice and inherit from another class,
|
|
|
|
you must first call method xml::Serialize::initXmlMembers of the
|
|
|
|
parent class, then call xml::Serialize::className to set the new
|
|
|
|
name of the child class.
|
|
|
|
|
|
|
|
@section examples Examples
|
|
|
|
|
|
|
|
@see @ref serialization.cxx for the different approaches
|
|
|
|
@see @ref contain_serialization.cxx for containment
|
|
|
|
@see @ref inherit_serialization.cxx for inheritance
|
2009-04-23 15:10:21 +00:00
|
|
|
|
2009-04-24 15:12:44 +00:00
|
|
|
@example serialization.cxx
|
2009-05-04 12:47:57 +00:00
|
|
|
|
|
|
|
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
|
2009-04-23 15:10:21 +00:00
|
|
|
//@{
|
|
|
|
|
|
|
|
class Serialize {
|
|
|
|
public:
|
2009-04-30 15:10:09 +00:00
|
|
|
typedef bool(*FromNodeFunc)(Any, const xml::Node&);
|
|
|
|
typedef bool(*ToNodeFunc)(const Any, xml::Node&);
|
2009-05-04 12:47:57 +00:00
|
|
|
typedef bool(*ClearFunc)(Any);
|
2009-04-23 15:10:21 +00:00
|
|
|
//! You must call Serialize::className() if you use this constructor!
|
|
|
|
Serialize() throw();
|
|
|
|
Serialize(const std::string& className) throw();
|
2009-04-27 10:48:27 +00:00
|
|
|
Serialize(const Serialize& other) throw();
|
2009-04-23 15:10:21 +00:00
|
|
|
virtual ~Serialize();
|
2009-04-27 10:48:27 +00:00
|
|
|
Serialize& operator=(const Serialize& other) throw();
|
2009-05-06 07:13:50 +00:00
|
|
|
virtual Serialize& className(const std::string& name) throw();
|
|
|
|
Serialize& persist(Serialize& member,
|
|
|
|
const std::string& name) throw();
|
2009-04-27 10:48:27 +00:00
|
|
|
Serialize& persist(bool& member,
|
|
|
|
const std::string& name) throw();
|
|
|
|
Serialize& persist(char& member,
|
|
|
|
const std::string& name) throw();
|
|
|
|
Serialize& persist(unsigned char& member,
|
|
|
|
const std::string& name) throw();
|
|
|
|
Serialize& persist(signed char& member,
|
|
|
|
const std::string& name) throw();
|
|
|
|
Serialize& persist(unsigned short& member,
|
|
|
|
const std::string& name) throw();
|
|
|
|
Serialize& persist(signed short& member,
|
|
|
|
const std::string& name) throw();
|
|
|
|
Serialize& persist(unsigned int& member,
|
|
|
|
const std::string& name) throw();
|
|
|
|
Serialize& persist(signed int& member,
|
|
|
|
const std::string& name) throw();
|
|
|
|
Serialize& persist(unsigned long& member,
|
|
|
|
const std::string& name) throw();
|
|
|
|
Serialize& persist(signed long& member,
|
|
|
|
const std::string& name) throw();
|
|
|
|
Serialize& persist(float& member,
|
|
|
|
const std::string& name) throw();
|
|
|
|
Serialize& persist(double& member,
|
|
|
|
const std::string& name) throw();
|
|
|
|
Serialize& persist(std::string& member,
|
|
|
|
const std::string& name) throw();
|
2009-05-04 12:47:57 +00:00
|
|
|
virtual std::ostream& saveXml(std::ostream& os,
|
|
|
|
const std::string& name = std::string())
|
2009-04-28 07:36:07 +00:00
|
|
|
const throw();
|
2009-05-04 12:47:57 +00:00
|
|
|
virtual std::istream& loadXml(std::istream& is,
|
|
|
|
const std::string& name = std::string());
|
2009-04-27 10:48:27 +00:00
|
|
|
std::string schema() const throw();
|
2009-04-28 07:36:07 +00:00
|
|
|
static void registerFromNode(FromNodeFunc fromNodeFunc);
|
|
|
|
static void registerToNode(ToNodeFunc toNodeFunc);
|
2009-05-04 12:47:57 +00:00
|
|
|
static void registerClear(ClearFunc clearFunc);
|
2009-05-06 07:13:50 +00:00
|
|
|
virtual void clear() throw();
|
2009-04-27 10:48:27 +00:00
|
|
|
protected:
|
|
|
|
virtual void initXmlMembers();
|
2009-04-30 08:28:52 +00:00
|
|
|
void checkInit(const Serialize* const ser=0) const {
|
|
|
|
if (ser) {
|
|
|
|
if (!ser->_xmlFactory) const_cast<Serialize*>(ser)->initXmlMembers();
|
|
|
|
} else {
|
|
|
|
if (!_xmlFactory) const_cast<Serialize*>(this)->initXmlMembers();
|
|
|
|
}
|
|
|
|
}
|
2009-05-04 12:47:57 +00:00
|
|
|
/*! @todo Why does @c protected: not work here?!? Children can't
|
|
|
|
access the members if they are protected! */
|
|
|
|
public:
|
|
|
|
//! @cond INTERNAL
|
2009-05-06 07:13:50 +00:00
|
|
|
template<typename TYPE> friend bool assignFromNode(Any member,
|
|
|
|
const xml::Node& node);
|
|
|
|
template<typename TYPE> friend bool assigntoNode(Any member,
|
|
|
|
const xml::Node& node);
|
|
|
|
virtual bool optional() const throw();
|
2009-05-04 12:47:57 +00:00
|
|
|
void clear(Any member) throw();
|
2009-04-30 08:28:52 +00:00
|
|
|
void reset() throw();
|
2009-04-27 10:48:27 +00:00
|
|
|
void copy(const Serialize& o) throw();
|
2009-04-23 15:10:21 +00:00
|
|
|
template<typename TYPE>
|
2009-04-27 10:48:27 +00:00
|
|
|
Serialize& persistSimpleType(TYPE& member,
|
|
|
|
const std::string& name) throw() {
|
2009-04-27 11:34:39 +00:00
|
|
|
_xmlNames[name] = &member;
|
2009-04-23 15:10:21 +00:00
|
|
|
xml::Node schema(*_xmlFactory);
|
|
|
|
schema<<xml::String(name).limits(1,1);
|
|
|
|
_xmlFactory = schema;
|
|
|
|
return *this;
|
|
|
|
}
|
2009-05-06 07:13:50 +00:00
|
|
|
virtual void fromNode(Any member, const xml::Node& node);
|
|
|
|
virtual void toNode(const Any member, xml::Node& node) const;
|
2009-04-30 15:10:09 +00:00
|
|
|
std::map<std::string, Any> _xmlNames;
|
2009-04-23 15:10:21 +00:00
|
|
|
xml::Factory _xmlFactory;
|
2009-04-28 07:36:07 +00:00
|
|
|
static std::set<FromNodeFunc> _fromNode;
|
|
|
|
static std::set<ToNodeFunc> _toNode;
|
2009-05-04 12:47:57 +00:00
|
|
|
static std::set<ClearFunc> _clear;
|
|
|
|
//! @endcond
|
2009-04-23 15:10:21 +00:00
|
|
|
};
|
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
template <class TYPE> class Optional: public Serialize {
|
|
|
|
public:
|
2009-05-06 07:13:50 +00:00
|
|
|
Optional() throw(): _valid(false) {}
|
|
|
|
Optional(const Optional& o) throw():
|
|
|
|
_member(o._member), _valid(o.valid) {
|
|
|
|
}
|
|
|
|
Optional(const TYPE& mem) throw():
|
|
|
|
_member(mem), _valid(true) {
|
|
|
|
}
|
2009-05-04 12:47:57 +00:00
|
|
|
virtual ~Optional() throw() {}
|
|
|
|
Optional& operator=(const Optional& o) throw() {
|
2009-05-06 07:13:50 +00:00
|
|
|
_member = o._member;
|
|
|
|
_valid = o._valid;
|
2009-05-04 12:47:57 +00:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
Optional& operator=(const TYPE& mem) throw() {
|
2009-05-06 07:13:50 +00:00
|
|
|
_member = mem;
|
|
|
|
_valid = true;
|
2009-05-04 12:47:57 +00:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
operator bool() const throw() {
|
2009-05-06 07:13:50 +00:00
|
|
|
return _valid;
|
2009-05-04 12:47:57 +00:00
|
|
|
}
|
|
|
|
const TYPE& operator*() const throw() {
|
2009-05-06 07:13:50 +00:00
|
|
|
return _member;
|
2009-05-04 12:47:57 +00:00
|
|
|
}
|
|
|
|
TYPE& operator*() throw() {
|
2009-05-06 07:13:50 +00:00
|
|
|
return _member;
|
2009-05-04 12:47:57 +00:00
|
|
|
}
|
|
|
|
const TYPE*const operator->() const throw() {
|
2009-05-06 07:13:50 +00:00
|
|
|
return &_member;
|
2009-05-04 12:47:57 +00:00
|
|
|
}
|
|
|
|
TYPE*const operator->() throw() {
|
2009-05-06 07:13:50 +00:00
|
|
|
return &_member;
|
2009-05-04 12:47:57 +00:00
|
|
|
}
|
2009-05-06 07:13:50 +00:00
|
|
|
virtual void clear() throw() {
|
|
|
|
_valid = false;
|
|
|
|
}
|
|
|
|
virtual Optional& className(const std::string& name) throw() {
|
|
|
|
if (!_xmlFactory) {
|
|
|
|
Serialize::className(name);
|
|
|
|
persist(_member, name);
|
|
|
|
// make the child the root, and it's optional
|
|
|
|
_xmlFactory = (*_xmlFactory)[0];
|
|
|
|
_xmlFactory->limits(0, 1);
|
|
|
|
}
|
2009-05-04 12:47:57 +00:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
protected:
|
2009-05-06 07:13:50 +00:00
|
|
|
virtual bool optional() const throw() {
|
|
|
|
return true;
|
2009-04-30 15:10:09 +00:00
|
|
|
}
|
2009-05-06 07:13:50 +00:00
|
|
|
virtual void fromNode(Any member, const xml::Node& node) {
|
|
|
|
_valid = true;
|
|
|
|
Serialize::fromNode(Any(&_member), node);
|
|
|
|
}
|
|
|
|
virtual void toNode(const Any member, xml::Node& node) const {
|
|
|
|
if (!_valid) {
|
|
|
|
node.parent().remove(node);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const Any mem(&const_cast<Optional*>(this)->_member);
|
|
|
|
Serialize::toNode(mem, node);
|
2009-05-04 12:47:57 +00:00
|
|
|
}
|
|
|
|
private:
|
2009-05-06 07:13:50 +00:00
|
|
|
TYPE _member;
|
|
|
|
bool _valid;
|
2009-04-30 15:10:09 +00:00
|
|
|
};
|
2009-05-04 12:47:57 +00:00
|
|
|
|
|
|
|
//! @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).
|
|
|
|
|
2009-05-06 07:22:21 +00:00
|
|
|
@example list_serialization.cxx
|
|
|
|
@example optional_serialization.cxx */
|
2009-05-04 12:47:57 +00:00
|
|
|
//@}
|
|
|
|
|
|
|
|
//! @cond INTERNAL
|
|
|
|
template<class CONTAINER_TYPE> class Container:
|
|
|
|
public CONTAINER_TYPE,
|
|
|
|
public Serialize {
|
|
|
|
public:
|
|
|
|
Container() {}
|
|
|
|
Container(const Container& o): CONTAINER_TYPE(o), Serialize(o) {}
|
|
|
|
Container(const std::string& className) throw(): Serialize(className) {}
|
|
|
|
virtual ~Container() {}
|
|
|
|
virtual std::istream& loadXml(std::istream& is,
|
|
|
|
const std::string& name = std::string()) {
|
|
|
|
checkInit();
|
|
|
|
xml::Factory factory(_xmlFactory);
|
|
|
|
if (name.size()) factory->name(name);
|
|
|
|
std::auto_ptr<xml::Node> node(factory.read(is));
|
|
|
|
CONTAINER_TYPE::clear();
|
|
|
|
for (xml::Node::size_type i(0); i<node->children(); ++i) {
|
|
|
|
typename CONTAINER_TYPE::value_type tmp;
|
|
|
|
Serialize::fromNode(&tmp, (*node)[i]); // reads into tmp
|
|
|
|
push_back(tmp);
|
|
|
|
}
|
|
|
|
return is;
|
|
|
|
}
|
|
|
|
virtual std::ostream& saveXml(std::ostream& os,
|
|
|
|
const std::string& name = std::string())
|
|
|
|
const throw() {
|
|
|
|
checkInit();
|
|
|
|
xml::Node node(*_xmlFactory);
|
|
|
|
if (name.size()) node.name(name);
|
|
|
|
std::auto_ptr<xml::Node> tpl(node[0].clone());
|
|
|
|
node.clear();
|
|
|
|
for (typename Container::const_iterator it = this->begin();
|
|
|
|
it!=this->end(); ++it) {
|
|
|
|
typename CONTAINER_TYPE::value_type tmp;
|
|
|
|
tmp = *it;
|
|
|
|
std::auto_ptr<xml::Node> item(tpl->clone());
|
|
|
|
Serialize::toNode(&tmp, *item);
|
|
|
|
node<<*item;
|
|
|
|
}
|
|
|
|
os<<node;
|
|
|
|
return os;
|
|
|
|
}
|
|
|
|
protected:
|
|
|
|
virtual void initXmlMembers() {
|
|
|
|
std::string itemName("item");
|
|
|
|
typename CONTAINER_TYPE::value_type tmp;
|
|
|
|
if (isSerialize<typename CONTAINER_TYPE::value_type>()) {
|
|
|
|
Serialize* ser(Mapper<typename CONTAINER_TYPE::value_type>
|
|
|
|
::toSerialize(tmp));
|
|
|
|
checkInit(ser);
|
|
|
|
itemName = ser->_xmlFactory->name();
|
|
|
|
}
|
|
|
|
_xmlFactory = xml::Node("dummyroot"); // dummy root, (uninitialized exc)
|
|
|
|
persist(tmp, itemName); // add as child of dummyroot
|
|
|
|
(*_xmlFactory)[0].limits(0, 0); // any number of children possible
|
|
|
|
}
|
|
|
|
virtual void clear() throw() {
|
|
|
|
CONTAINER_TYPE::clear();
|
2009-04-30 15:10:09 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
template<class CONTAINER_TYPE> class AssociativeContainer:
|
|
|
|
public CONTAINER_TYPE,
|
|
|
|
public Serialize {
|
2009-04-29 11:58:57 +00:00
|
|
|
public:
|
2009-05-04 12:47:57 +00:00
|
|
|
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;
|
|
|
|
}
|
2009-04-29 11:58:57 +00:00
|
|
|
protected:
|
2009-04-30 08:28:52 +00:00
|
|
|
virtual void initXmlMembers() {
|
2009-04-29 11:58:57 +00:00
|
|
|
std::string itemName("item");
|
2009-05-04 12:47:57 +00:00
|
|
|
typename CONTAINER_TYPE::value_type tmp;
|
|
|
|
if (isSerialize<typename CONTAINER_TYPE::value_type>()) {
|
|
|
|
Serialize* ser(Mapper<typename CONTAINER_TYPE::value_type>
|
|
|
|
::toSerialize(tmp));
|
2009-04-30 15:10:09 +00:00
|
|
|
assert(ser);
|
|
|
|
assert(ser!=this);
|
|
|
|
assert((void*)ser==(void*)&tmp);
|
2009-04-30 08:28:52 +00:00
|
|
|
checkInit(ser);
|
2009-04-30 15:10:09 +00:00
|
|
|
itemName = ser->_xmlFactory->name();
|
2009-04-29 11:58:57 +00:00
|
|
|
}
|
2009-04-30 08:28:52 +00:00
|
|
|
_xmlFactory = xml::Node("dummyroot"); // dummy root, (uninitialized exc)
|
2009-05-04 12:47:57 +00:00
|
|
|
persist(tmp, itemName); // add as child of dummyroot
|
2009-04-30 08:28:52 +00:00
|
|
|
(*_xmlFactory)[0].limits(0, 0); // any number of children possible
|
2009-04-29 11:58:57 +00:00
|
|
|
}
|
2009-05-04 12:47:57 +00:00
|
|
|
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();
|
|
|
|
for (xml::Node::size_type i(0); i<node->children(); ++i) {
|
|
|
|
typename CONTAINER_TYPE::key_type key;
|
|
|
|
typename CONTAINER_TYPE::mapped_type data;
|
|
|
|
Serialize::fromNode(&key, (*node)[i]); // reads into tmp
|
|
|
|
Serialize::fromNode(&data, (*node)[++i]); // key&value
|
|
|
|
insert(typename CONTAINER_TYPE::value_type(key, data));
|
2009-04-30 08:28:52 +00:00
|
|
|
}
|
2009-05-04 12:47:57 +00:00
|
|
|
return is;
|
2009-04-29 11:58:57 +00:00
|
|
|
}
|
2009-05-04 12:47:57 +00:00
|
|
|
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();
|
2009-04-30 08:28:52 +00:00
|
|
|
it!=this->end(); ++it) {
|
2009-05-04 12:47:57 +00:00
|
|
|
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();
|
2009-04-30 08:28:52 +00:00
|
|
|
}
|
2009-05-04 12:47:57 +00:00
|
|
|
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();
|
2009-04-30 08:28:52 +00:00
|
|
|
}
|
2009-04-29 11:58:57 +00:00
|
|
|
};
|
2009-05-04 12:47:57 +00:00
|
|
|
//! @endcond
|
2009-04-29 11:58:57 +00:00
|
|
|
|
2009-04-02 13:24:59 +00:00
|
|
|
}
|
2009-04-23 15:10:21 +00:00
|
|
|
|
2009-05-04 12:47:57 +00:00
|
|
|
//! @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
|
|
|
|
|
2009-04-02 13:24:59 +00:00
|
|
|
#endif
|