serialization works for containment and inheritance
This commit is contained in:
		| @@ -33,7 +33,7 @@ class B: public xml::Serialize { | ||||
|       className("b") | ||||
|         .persist(i, "i") | ||||
|         .persist(txt, "txt") | ||||
|         .persist(a); | ||||
|         .persist(a, "a"); | ||||
|     } | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| //       1         2         3         4         5         6         7         8 | ||||
| // 45678901234567890123456789012345678901234567890123456789012345678901234567890 | ||||
|  | ||||
| // g++ -I../../src ../../src/xml.cxx serialization.cxx | ||||
| // g++ -I../../src ../../src/xml.cxx inherit_serialization.cxx | ||||
|  | ||||
| #include <xml-cxx/xml.hxx> | ||||
| #include <iostream> | ||||
| @@ -17,7 +17,7 @@ class A: public xml::Serialize { | ||||
|     std::string txt; | ||||
|   protected: | ||||
|     void initXmlMembers() { | ||||
|       className("b"); | ||||
|       className("A"); | ||||
|       persist(a, "a"); | ||||
|       persist(txt, "txt"); | ||||
|     } | ||||
| @@ -25,68 +25,25 @@ class A: public xml::Serialize { | ||||
|  | ||||
| class B: public A { | ||||
|   public: | ||||
|     int a; | ||||
|     std::string txt; | ||||
|     int b; | ||||
|   protected: | ||||
|     void initXmlMembers() { | ||||
|       className("b"); | ||||
|       persist(a, "a"); | ||||
|       persist(txt, "txt"); | ||||
|       A::initXmlMembers(); | ||||
|       className("B"); | ||||
|       persist(b, "b"); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| //! Class with external xml::Serialize | ||||
| class C { | ||||
|   public: | ||||
|     int a; | ||||
|     std::string txt; | ||||
| }; | ||||
|  | ||||
| int main(int, char**) { | ||||
|   { // Serialization as a member | ||||
|     std::stringstream ss("<a>\n" | ||||
|                          "\t<a>1234</a>\n" | ||||
|   std::stringstream ss("<B>\n" | ||||
|                        "\t<b>1234</b>\n" | ||||
|                        "\t<a>5678</a>\n" | ||||
|                        "\t<txt>Dies ist ein Serialisierungs-Test</txt>\n" | ||||
|                          "</a>"); | ||||
|     A a; | ||||
|     a._ser.loadXml(ss); | ||||
|     if (a.a==1234) a.a=4321; | ||||
|     a._ser.saveXml(std::cout)<<std::endl; | ||||
|   } { // Inherited Serialization | ||||
|     std::stringstream ss("<b>\n" | ||||
|                          "\t<a>1234</a>\n" | ||||
|                          "\t<txt>Dies ist ein Serialisierungs-Test</txt>\n" | ||||
|                          "</b>"); | ||||
|                        "</B>"); | ||||
|   B b; | ||||
|   b.loadXml(ss); | ||||
|     if (b.a==1234) b.a=4321; | ||||
|   if (b.b==1234) b.b=4321; | ||||
|   if (b.a==5678) b.a=8765; | ||||
|   b.saveXml(std::cout)<<std::endl; | ||||
|   } { // External xml::Serialize: "ser" must live in no longer than "c"! | ||||
|     std::stringstream ss("<c>\n" | ||||
|                          "\t<a>1234</a>\n" | ||||
|                          "\t<txt>Dies ist ein Serialisierungs-Test</txt>\n" | ||||
|                          "</c>"); | ||||
|     C c; | ||||
|     xml::Serialize ser(xml::Serialize("c") | ||||
|                        .persist(c.a, "a") | ||||
|                        .persist(c.txt, "txt")); | ||||
|     ser.loadXml(ss); | ||||
|     if (c.a==1234) c.a=4321; | ||||
|     ser.saveXml(std::cout)<<std::endl; | ||||
|   } { // Use xml::Serialize to store anything, e.g. local variables | ||||
|       // Important: "ser" must live in no longer than the variables! | ||||
|     std::stringstream ss("<d>\n" | ||||
|                          "\t<a>1234</a>\n" | ||||
|                          "\t<txt>Dies ist ein Serialisierungs-Test</txt>\n" | ||||
|                          "</d>"); | ||||
|     int a; | ||||
|     std::string txt; | ||||
|     xml::Serialize ser(xml::Serialize("d") | ||||
|                        .persist(a, "a") | ||||
|                        .persist(txt, "txt")); | ||||
|     ser.loadXml(ss); | ||||
|     if (a==1234) a=4321; | ||||
|     ser.saveXml(std::cout)<<std::endl; | ||||
|   } | ||||
|   return 0; | ||||
| } | ||||
|   | ||||
| @@ -6,11 +6,15 @@ | ||||
| AM_CXXFLAGS += -I ${top_srcdir}/src | ||||
| AM_LDFLAGS = -L${top_builddir}/src -lxml-cxx | ||||
|  | ||||
| noinst_PROGRAMS = address node_macros serialization contain_serialization | ||||
| noinst_PROGRAMS = address node_macros serialization \ | ||||
|                   contain_serialization inherit_serialization \ | ||||
| 		  list_serialization | ||||
|  | ||||
| address_SOURCES = address.cxx | ||||
| node_macros_SOURCES = node_macros.cxx | ||||
| serialization_SOURCES = serialization.cxx | ||||
| contain_serialization_SOURCES = contain_serialization.cxx | ||||
| inherit_serialization_SOURCES = inherit_serialization.cxx | ||||
| list_serialization_SOURCES = list_serialization.cxx | ||||
|  | ||||
| MAINTAINERCLEANFILES = makefile.in | ||||
|   | ||||
| @@ -12,6 +12,7 @@ | ||||
| #include <sstream> | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include <list> | ||||
| #include <map> | ||||
| #include <memory> | ||||
| #include <typeinfo> | ||||
| @@ -42,7 +43,12 @@ class MethodTrace { | ||||
| }; | ||||
| #define TRACE MethodTrace XXX_METHOD(this, __PRETTY_FUNCTION__) | ||||
| #define LOG(X) std::clog<<__PRETTY_FUNCTION__<<"\t**** "<<X<<std::endl | ||||
|  | ||||
| #define ASSERT(X, Y) {                          \ | ||||
|     if (!(Y)) {                                 \ | ||||
|       LOG(X);                                   \ | ||||
|     }                                           \ | ||||
|     assert(Y);                                  \ | ||||
| } | ||||
|  | ||||
|  | ||||
| /*! @mainpage | ||||
| @@ -578,6 +584,7 @@ namespace xml { | ||||
|       virtual Node& set(const Attributes& o) throw(); | ||||
|       Node& clear() throw (); | ||||
|       std::string name() const throw(); | ||||
|       Node& name(const std::string& n) throw(); | ||||
|       Node& min(size_type m) throw(); | ||||
|       size_type min() const throw(); | ||||
|       Node& max(size_type m) throw(); | ||||
| @@ -639,6 +646,7 @@ namespace xml { | ||||
|       Node& operator=(const std::string& contents) throw(); | ||||
|       operator std::string() const throw(); | ||||
|       operator bool() const throw(); | ||||
|       operator char() const throw(); | ||||
|       operator signed char() const throw(); | ||||
|       operator unsigned char() const throw(); | ||||
|       operator signed short() const throw(); | ||||
| @@ -687,8 +695,12 @@ namespace xml { | ||||
|                 duplicate_attribute, attributes_in_end_tag, | ||||
|                 illegal_attribute, mandatory_attribute_missing, | ||||
|                 wrong_node_number); | ||||
|       void reset() throw(); | ||||
|     private: | ||||
|       friend class stream_error; | ||||
|       friend class Serialize; | ||||
|       Node& operator*() throw(factory_not_valid); | ||||
|       Node*const operator->() throw(factory_not_valid); | ||||
|       bool ws(char c) throw(); | ||||
|       std::auto_ptr<Node> read(std::istream& is, const Node& position) | ||||
|           throw(wrong_end_tag, wrong_start_tag, tag_expected, type_mismatch, | ||||
| @@ -809,14 +821,78 @@ namespace xml { | ||||
|       //! You must call Serialize::className() if you use this constructor! | ||||
|       Serialize() throw(); | ||||
|       Serialize(const std::string& className) throw(); | ||||
|       Serialize(const Serialize& other) throw(); | ||||
|       virtual ~Serialize(); | ||||
|       Serialize& operator=(const Serialize& other) throw(); | ||||
|       Serialize& className(const std::string& name) throw(); | ||||
|       Serialize& persist(Serialize& ser) throw(); | ||||
|       Serialize& persist(Serialize& member, | ||||
|                          const std::string& name) throw(); | ||||
|       template<typename TYPE, class ALLOC> | ||||
|           Serialize& persist(std::list<TYPE, ALLOC>& member, | ||||
|                              const std::string& name) throw() { | ||||
|         return persist(member, name, name); | ||||
|       } | ||||
|       template<typename TYPE, class ALLOC> | ||||
|           Serialize& persist(std::list<TYPE, ALLOC>& member, | ||||
|                              const std::string& list, | ||||
|                              const std::string& item) throw() { | ||||
|         mapName<std::list<TYPE, ALLOC> >()[std::make_pair(this, list)] | ||||
|           = &member; | ||||
|         mapMember<std::list<TYPE, ALLOC> >()[&member] = list; | ||||
|         _xmlNames[list] = &typeid(TYPE); | ||||
|         Serialize ser(list); | ||||
|         TYPE dummy; | ||||
|         ser.persist(dummy, item); | ||||
|         *_xmlFactory<<(xml::Node(list).limits(1,1) | ||||
|                        <<(*ser._xmlFactory)[0].limits(0, 0)); | ||||
|         return *this; | ||||
|       } | ||||
|       Serialize& persist(bool& member, | ||||
|                          const std::string& name) throw(); | ||||
|       Serialize& persist(char& member, | ||||
|                          const std::string& name) throw(); | ||||
|       Serialize& persist(unsigned char& member, | ||||
|                          const std::string& name) throw(); | ||||
|       Serialize& persist(signed char& member, | ||||
|                          const std::string& name) throw(); | ||||
|       Serialize& persist(unsigned short& member, | ||||
|                          const std::string& name) throw(); | ||||
|       Serialize& persist(signed short& member, | ||||
|                          const std::string& name) throw(); | ||||
|       Serialize& persist(unsigned int& member, | ||||
|                          const std::string& name) throw(); | ||||
|       Serialize& persist(signed int& member, | ||||
|                          const std::string& name) throw(); | ||||
|       Serialize& persist(unsigned long& member, | ||||
|                          const std::string& name) throw(); | ||||
|       Serialize& persist(signed long& member, | ||||
|                          const std::string& name) throw(); | ||||
|       Serialize& persist(float& member, | ||||
|                          const std::string& name) throw(); | ||||
|       Serialize& persist(double& member, | ||||
|                          const std::string& name) throw(); | ||||
|       Serialize& persist(std::string& member, | ||||
|                          const std::string& name) throw(); | ||||
|       std::ostream& saveXml(std::ostream& os, | ||||
|                             const std::string& name = std::string()) const throw(); | ||||
|       std::istream& loadXml(std::istream& is, | ||||
|                             const std::string& name = std::string()); | ||||
|       std::string schema() const throw(); | ||||
|     protected: | ||||
|       virtual void initXmlMembers(); | ||||
|     private: | ||||
|       void clear() throw(); | ||||
|       void copy(const Serialize& o) throw(); | ||||
|       template<typename TYPE> | ||||
|           Serialize& persist(TYPE& member, const std::string& name) throw() { | ||||
|         assert(mapName<TYPE>().find(std::make_pair(this, name))==mapName<TYPE>().end()); | ||||
|         assert(mapMember<TYPE>().find(&member)==mapMember<TYPE>().end()); | ||||
|         assert(_xmlNames.find(name)==_xmlNames.end()); | ||||
|           Serialize& persistSimpleType(TYPE& member, | ||||
|                                        const std::string& name) throw() { | ||||
|         ASSERT("type: "<<typeid(TYPE).name()<<"; name="<<name, | ||||
|                mapName<TYPE>().find(std::make_pair(this, name)) | ||||
|                ==mapName<TYPE>().end()); | ||||
|         ASSERT("type: "<<typeid(TYPE).name()<<"; name="<<name, | ||||
|                mapMember<TYPE>().find(&member)==mapMember<TYPE>().end()); | ||||
|         ASSERT("type: "<<typeid(TYPE).name()<<"; name="<<name, | ||||
|                _xmlNames.find(name)==_xmlNames.end()); | ||||
|         mapName<TYPE>()[std::make_pair(this, name)] = &member; | ||||
|         mapMember<TYPE>()[&member] = name; | ||||
|         _xmlNames[name] = &typeid(TYPE); | ||||
| @@ -825,11 +901,6 @@ namespace xml { | ||||
|         _xmlFactory = schema; | ||||
|         return *this; | ||||
|       } | ||||
|       std::ostream& saveXml(std::ostream& os) const throw(); | ||||
|       std::istream& loadXml(std::istream& is); | ||||
|     protected: | ||||
|       virtual void initXmlMembers(); | ||||
|     private: | ||||
|       template<typename TYPE> | ||||
|           std::map<std::pair<const Serialize*, std::string>, TYPE*>& | ||||
|           mapName() const { | ||||
| @@ -841,13 +912,19 @@ namespace xml { | ||||
|         return MAP; | ||||
|       } | ||||
|       template<typename TYPE> void fromNode(TYPE* member, xml::Node& node) { | ||||
|         *member = | ||||
|           (TYPE)dynamic_cast<xml::String&>(node[mapMember<TYPE>()[member]]); | ||||
|         *member = (TYPE)dynamic_cast<xml::String&>(node); | ||||
|       } | ||||
|       /* | ||||
|       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) | ||||
|           ... | ||||
|           }*/ | ||||
|       template<typename TYPE> void toNode(TYPE* member, xml::Node& node) const { | ||||
|         std::stringstream ss; | ||||
|         ss<<*member; | ||||
|         node[mapMember<TYPE>()[member]].text(ss.str()); | ||||
|         node.text(ss.str()); | ||||
|       } | ||||
|       std::map<std::string, const std::type_info*> _xmlNames; | ||||
|       xml::Factory _xmlFactory; | ||||
|   | ||||
							
								
								
									
										284
									
								
								src/xml.cxx
									
									
									
									
									
								
							
							
						
						
									
										284
									
								
								src/xml.cxx
									
									
									
									
									
								
							| @@ -330,6 +330,11 @@ namespace xml { | ||||
|   std::string Node::name() const throw() { | ||||
|     return _name; | ||||
|   } | ||||
|   //! Set a new node's tag name. | ||||
|   Node& Node::name(const std::string& n) throw() { | ||||
|     _name = n; | ||||
|     return *this; | ||||
|   } | ||||
|   //! Set minimum number of instances (in a xml::Factory). | ||||
|   /*! @copydoc limits */ | ||||
|   Node& Node::min(Node::size_type m) throw() { | ||||
| @@ -598,6 +603,12 @@ namespace xml { | ||||
|     ss>>res; | ||||
|     return res; | ||||
|   } | ||||
|   String::operator char() const throw() { | ||||
|     char res; | ||||
|     std::stringstream ss(text()); | ||||
|     ss>>res; | ||||
|     return res; | ||||
|   } | ||||
|   String::operator signed char() const throw() { | ||||
|     signed char res; | ||||
|     std::stringstream ss(text()); | ||||
| @@ -849,6 +860,21 @@ namespace xml { | ||||
|     e.line(_line); | ||||
|     throw; | ||||
|   } | ||||
|   void Factory::reset() throw() { | ||||
|     _line = 0; | ||||
|     _open = 0; | ||||
|     _template = xml::Node("<xml::start>"); | ||||
|   } | ||||
|   Node& Factory::operator*() throw(factory_not_valid) try { | ||||
|     return _template[0]; | ||||
|   } catch (...) { | ||||
|     throw factory_not_valid(); | ||||
|   } | ||||
|   Node*const Factory::operator->() throw(factory_not_valid) try { | ||||
|     return &_template[0]; | ||||
|   } catch (...) { | ||||
|     throw factory_not_valid(); | ||||
|   } | ||||
|   bool Factory::ws(char c) throw() { | ||||
|     static char last(0); | ||||
|     if ((c=='\n'||c=='\r')&&last!='\n'&&last!='\r') ++_line; | ||||
| @@ -995,22 +1021,184 @@ namespace xml { | ||||
|                                                  xml::Node& node) { | ||||
|     //! @todo improve this (inefficient) | ||||
|     std::stringstream ss; // simple but inefficient: rewrite and reread | ||||
|     ss<<node[member->_xmlFactory->name()]; | ||||
|     member->loadXml(ss); | ||||
|     ss<<node; | ||||
|     member->loadXml(ss, node.name()); | ||||
|   } | ||||
|   template<> void Serialize::toNode<Serialize>(Serialize* member, | ||||
|                                                xml::Node& node) const { | ||||
|     //! @todo improve this (inefficient) | ||||
|     std::stringstream ss; // simple but inefficient: write and reread | ||||
|     member->saveXml(ss); | ||||
|     node[member->_xmlFactory->name()] = *member->_xmlFactory.read(ss); | ||||
|     std::stringstream ss; | ||||
|     member->saveXml(ss, node.name()); | ||||
|     xml::Factory factory(node); | ||||
|     node = *factory.read(ss); | ||||
|   } | ||||
|   //---------------------------------------------------------------------------- | ||||
|   Serialize::Serialize() throw() {} | ||||
|   Serialize::Serialize(const std::string& className) throw(): | ||||
|       _xmlFactory(xml::Node(xml::String(className).limits(1,1))) { | ||||
|   } | ||||
|   Serialize::Serialize(const Serialize& other) throw() { | ||||
|     copy(other); | ||||
|   } | ||||
|   Serialize::~Serialize() { | ||||
|     clear(); | ||||
|   } | ||||
|   Serialize& Serialize::operator=(const Serialize& other) throw() { | ||||
|     copy(other); | ||||
|     return *this; | ||||
|   } | ||||
|   Serialize& Serialize::className(const std::string& name) throw() { | ||||
|     xml::Node node(xml::Node(xml::String(name).limits(1,1))); | ||||
|     if (_xmlFactory) { | ||||
|       for (xml::Node::size_type i(0); i<_xmlFactory->children(); ++i) | ||||
|         node<<(*_xmlFactory)[i]; | ||||
|     } | ||||
|     _xmlFactory=node; | ||||
|     return *this; | ||||
|   } | ||||
|   Serialize& Serialize::persist(Serialize& ser, | ||||
|                                 const std::string& name) throw() { | ||||
|     if (!ser._xmlFactory) ser.initXmlMembers(); | ||||
|     mapName<Serialize>()[std::make_pair(this, name)] = &ser; | ||||
|     mapMember<Serialize>()[&ser] = name; | ||||
|     _xmlNames[name] = &typeid(Serialize); | ||||
|     xml::Node schema(*_xmlFactory); | ||||
|     xml::Node node(xml::Node(name).limits(1,1)); | ||||
|     for (xml::Node::size_type i(0); i<ser._xmlFactory->children(); ++i) | ||||
|       node<<(*ser._xmlFactory)[i]; | ||||
|     _xmlFactory = schema<<node; | ||||
|     return *this; | ||||
|   } | ||||
|   Serialize& Serialize::persist(bool& member, | ||||
|                                 const std::string& name) throw() { | ||||
|     return persistSimpleType(member, name); | ||||
|   } | ||||
|   Serialize& Serialize::persist(char& member, | ||||
|                                 const std::string& name) throw() { | ||||
|     return persistSimpleType(member, name); | ||||
|   } | ||||
|   Serialize& Serialize::persist(unsigned char& member, | ||||
|                                 const std::string& name) throw() { | ||||
|     return persistSimpleType(member, name); | ||||
|   } | ||||
|   Serialize& Serialize::persist(signed char& member, | ||||
|                                 const std::string& name) throw() { | ||||
|     return persistSimpleType(member, name); | ||||
|   } | ||||
|   Serialize& Serialize::persist(unsigned short& member, | ||||
|                                 const std::string& name) throw() { | ||||
|     return persistSimpleType(member, name); | ||||
|   } | ||||
|   Serialize& Serialize::persist(signed short& member, | ||||
|                                 const std::string& name) throw() { | ||||
|     return persistSimpleType(member, name); | ||||
|   } | ||||
|   Serialize& Serialize::persist(unsigned int& member, | ||||
|                                const std::string& name) throw() { | ||||
|     return persistSimpleType(member, name); | ||||
|   } | ||||
|   Serialize& Serialize::persist(signed int& member, | ||||
|                                 const std::string& name) throw() { | ||||
|     return persistSimpleType(member, name); | ||||
|   } | ||||
|   Serialize& Serialize::persist(unsigned long& member, | ||||
|                                 const std::string& name) throw() { | ||||
|     return persistSimpleType(member, name); | ||||
|   } | ||||
|   Serialize& Serialize::persist(signed long& member, | ||||
|                                 const std::string& name) throw() { | ||||
|     return persistSimpleType(member, name); | ||||
|   } | ||||
|   Serialize& Serialize::persist(float& member, | ||||
|                                 const std::string& name) throw() { | ||||
|     return persistSimpleType(member, name); | ||||
|   } | ||||
|   Serialize& Serialize::persist(double& member, | ||||
|                                 const std::string& name) throw() { | ||||
|     return persistSimpleType(member, name); | ||||
|   } | ||||
|   Serialize& Serialize::persist(std::string& member, | ||||
|                                 const std::string& name) throw() { | ||||
|     return persistSimpleType(member, name); | ||||
|   } | ||||
|   std::ostream& Serialize::saveXml(std::ostream& os, | ||||
|                                    const std::string& name) const throw() { | ||||
|     if (!_xmlFactory) const_cast<Serialize*>(this)->initXmlMembers(); | ||||
|     xml::Node node(*_xmlFactory); | ||||
|     if (name.size()) node.name(name); | ||||
|     for (std::map<std::string, const std::type_info*>::const_iterator | ||||
|            it(_xmlNames.begin()); | ||||
|          it!=_xmlNames.end(); ++it) { | ||||
| #define QWERTZ_CHECK_TYPE_ZTREWQ___XXX(TYPE)                            \ | ||||
|       if (*it->second==typeid(TYPE))                                    \ | ||||
|         toNode(mapName<TYPE>()[std::make_pair(this, it->first)],        \ | ||||
|                node[it->first]);                                        \ | ||||
|       else | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(Serialize) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(std::string) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(float) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(double) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(bool) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(char) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(signed char) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned char) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(short) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(signed short) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned short) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(int) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(signed int) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned int) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(long) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(signed long) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned long) | ||||
|       throw std::runtime_error(it->second->name()); | ||||
| #undef QWERTZ_CHECK_TYPE_ZTREWQ___XXX | ||||
|     }       | ||||
|     os<<node; | ||||
|     return os; | ||||
|   } | ||||
|   std::istream& Serialize::loadXml(std::istream& is, const std::string& name) { | ||||
|     if (!_xmlFactory) initXmlMembers(); | ||||
|     xml::Factory factory(_xmlFactory); | ||||
|     if (name.size()) factory->name(name); | ||||
|     std::auto_ptr<xml::Node> node(factory.read(is)); | ||||
|     for (std::map<std::string, const std::type_info*>::const_iterator | ||||
|            it(_xmlNames.begin()); | ||||
|          it!=_xmlNames.end(); ++it) { | ||||
| #define QWERTZ_CHECK_TYPE_ZTREWQ___XXX(TYPE)                            \ | ||||
|       if (*it->second==typeid(TYPE))                                    \ | ||||
|         fromNode(mapName<TYPE>()[std::make_pair(this, it->first)],      \ | ||||
|                  (*node)[it->first]);                                   \ | ||||
|       else | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(Serialize) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(std::string) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(float) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(double) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(bool) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(char) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(signed char) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned char) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(short) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(signed short) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned short) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(int) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(signed int) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned int) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(long) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(signed long) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned long) | ||||
|       throw std::runtime_error(it->second->name()); | ||||
| #undef QWERTZ_CHECK_TYPE_ZTREWQ___XXX | ||||
|     } | ||||
|     return is; | ||||
|   } | ||||
|   std::string Serialize::schema() const throw() { | ||||
|     if (!_xmlFactory) const_cast<Serialize*>(this)->initXmlMembers(); | ||||
|     std::stringstream ss; | ||||
|     ss<<*_xmlFactory; | ||||
|     return ss.str(); | ||||
|   } | ||||
|   void Serialize::initXmlMembers() {} | ||||
|   void Serialize::clear() throw() { | ||||
|     // Remove my entries from the maps | ||||
|     for (std::map<std::string, const std::type_info*>::const_iterator | ||||
|            it(_xmlNames.begin()); | ||||
| @@ -1042,86 +1230,10 @@ namespace xml { | ||||
| #undef QWERTZ_CHECK_TYPE_ZTREWQ___XXX | ||||
|     } | ||||
|   } | ||||
|   Serialize& Serialize::className(const std::string& name) throw() { | ||||
|     _xmlFactory=xml::Node(xml::String(name).limits(1,1)); | ||||
|     return *this; | ||||
|   void Serialize::copy(const Serialize& o) throw() { | ||||
|     clear(); | ||||
|     _xmlFactory.reset(); | ||||
|     initXmlMembers(); | ||||
|   } | ||||
|   Serialize& Serialize::persist(Serialize& ser) throw() { | ||||
|     ser.initXmlMembers(); | ||||
|     std::string name(ser._xmlFactory->name()); | ||||
|     mapName<Serialize>()[std::make_pair(this, name)] = &ser; | ||||
|     mapMember<Serialize>()[&ser] = name; | ||||
|     _xmlNames[name] = &typeid(Serialize); | ||||
|     xml::Node schema(*_xmlFactory); | ||||
|     schema<<*ser._xmlFactory; | ||||
|     _xmlFactory = schema; | ||||
|     return *this; | ||||
|   } | ||||
|   std::ostream& Serialize::saveXml(std::ostream& os) const throw() { | ||||
|     if (!_xmlFactory) const_cast<Serialize*>(this)->initXmlMembers(); | ||||
|     xml::Node node(*_xmlFactory); | ||||
|     for (std::map<std::string, const std::type_info*>::const_iterator | ||||
|            it(_xmlNames.begin()); | ||||
|          it!=_xmlNames.end(); ++it) { | ||||
| #define QWERTZ_CHECK_TYPE_ZTREWQ___XXX(TYPE)                            \ | ||||
|       if (*it->second==typeid(TYPE))                                    \ | ||||
|         toNode(mapName<TYPE>()[std::make_pair(this, it->first)], node); \ | ||||
|       else | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(Serialize) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(std::string) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(float) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(double) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(bool) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(char) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(signed char) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned char) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(short) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(signed short) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned short) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(int) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(signed int) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned int) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(long) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(signed long) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned long) | ||||
|       throw std::runtime_error(it->second->name()); | ||||
| #undef QWERTZ_CHECK_TYPE_ZTREWQ___XXX | ||||
|     }       | ||||
|     os<<node; | ||||
|     return os; | ||||
|   } | ||||
|   std::istream& Serialize::loadXml(std::istream& is) { | ||||
|     if (!_xmlFactory) initXmlMembers(); | ||||
|     std::auto_ptr<xml::Node> node(_xmlFactory.read(is)); | ||||
|     for (std::map<std::string, const std::type_info*>::const_iterator | ||||
|            it(_xmlNames.begin()); | ||||
|          it!=_xmlNames.end(); ++it) { | ||||
| #define QWERTZ_CHECK_TYPE_ZTREWQ___XXX(TYPE)                               \ | ||||
|       if (*it->second==typeid(TYPE))                                       \ | ||||
|         fromNode(mapName<TYPE>()[std::make_pair(this, it->first)], *node); \ | ||||
|       else | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(Serialize) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(std::string) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(float) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(double) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(bool) | ||||
|       //QWERTZ_CHECK_TYPE_ZTREWQ___XXX(char) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(signed char) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned char) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(short) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(signed short) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned short) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(int) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(signed int) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned int) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(long) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(signed long) | ||||
|       QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned long) | ||||
|       throw std::runtime_error(it->second->name()); | ||||
| #undef QWERTZ_CHECK_TYPE_ZTREWQ___XXX | ||||
|     } | ||||
|     return is; | ||||
|   } | ||||
|   void Serialize::initXmlMembers() {} | ||||
|    | ||||
| } | ||||
|   | ||||
| @@ -31,6 +31,58 @@ class A: public xml::Serialize { | ||||
|     } | ||||
| }; | ||||
|  | ||||
| class B: public xml::Serialize { | ||||
|   public: | ||||
|     long aLong; | ||||
|     char aChar; | ||||
|   protected: | ||||
|     void initXmlMembers() { | ||||
|       className("B"); | ||||
|       persist(aLong, "aLong"); | ||||
|       persist(aChar, "aChar"); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| class B2: public B { | ||||
|   public: | ||||
|     float aFloat; | ||||
|     short aShort; | ||||
|   protected: | ||||
|     void initXmlMembers() { | ||||
|       B::initXmlMembers(); | ||||
|       className("B2"); | ||||
|       persist(aFloat, "float"); | ||||
|       persist(aShort, "short"); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| // Complex Example: Class A2 is a class that inherits another class | ||||
| // (A) and contains a class (B2) that inherits another class (B) | ||||
| class A2: public A { | ||||
|   public: | ||||
|     std::string aText; | ||||
|     B2 childOfB; | ||||
|   protected: | ||||
|     void initXmlMembers() { | ||||
|       A::initXmlMembers(); | ||||
|       className("A2"); | ||||
|       persist(aText, "aText"); | ||||
|       persist(childOfB, "childOfB"); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| class C: public xml::Serialize { | ||||
|   public: | ||||
|     A2 a2; | ||||
|     B2 b2; | ||||
|   protected: | ||||
|     void initXmlMembers() { | ||||
|       className("C") | ||||
|         .persist(a2, "a2") | ||||
|         .persist(b2, "b2"); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| class SerializationTest: public CppUnit::TestFixture { | ||||
|   public: | ||||
|     std::string _file; | ||||
| @@ -91,10 +143,203 @@ class SerializationTest: public CppUnit::TestFixture { | ||||
|                            a._anotherString); | ||||
|       CPPUNIT_ASSERT_EQUAL(4123674622ul, a._aLong); | ||||
|     } | ||||
|     void complexLoad() { | ||||
|       std::stringstream ss("<A2>\n" | ||||
|                            "  <anInteger>-1234</anInteger>\n" | ||||
|                            "  <aBool>1</aBool>\n" | ||||
|                            "  <aDouble>3.141</aDouble>\n" | ||||
|                            "  <aString>This is A inside of A2</aString>\n" | ||||
|                            "  <anotherString>Another A-String</anotherString>\n" | ||||
|                            "  <aLong>1234567890</aLong>\n" | ||||
|                            "  <aText>Text from A2</aText>\n" | ||||
|                            "  <childOfB>\n" | ||||
|                            "      <aLong>987654321</aLong>\n" | ||||
|                            "      <aChar>Q</aChar>\n" | ||||
|                            "      <float>2.5</float>\n" | ||||
|                            "      <short>-127</short>\n" | ||||
|                            "  </childOfB>\n" | ||||
|                            "</A2>"); | ||||
|       A2 a2; | ||||
|       CPPUNIT_ASSERT_EQUAL(std::string("<A2>\n" | ||||
|                                        "\t<anInteger/>\n" | ||||
|                                        "\t<aBool/>\n" | ||||
|                                        "\t<aDouble/>\n" | ||||
|                                        "\t<aString/>\n" | ||||
|                                        "\t<anotherString/>\n" | ||||
|                                        "\t<aLong/>\n" | ||||
|                                        "\t<aText/>\n" | ||||
|                                        "\t<childOfB>\n" | ||||
|                                        "\t\t<aLong/>\n" | ||||
|                                        "\t\t<aChar/>\n" | ||||
|                                        "\t\t<float/>\n" | ||||
|                                        "\t\t<short/>\n" | ||||
|                                        "\t</childOfB>\n" | ||||
|                                        "</A2>"), a2.schema()); | ||||
|       CPPUNIT_ASSERT_NO_THROW(a2.loadXml(ss)); | ||||
|       CPPUNIT_ASSERT_EQUAL(-1234, a2._anInteger); | ||||
|       CPPUNIT_ASSERT_EQUAL(true, a2._aBool); | ||||
|       CPPUNIT_ASSERT_EQUAL(3.141, a2._aDouble); | ||||
|       CPPUNIT_ASSERT_EQUAL(std::string("This is A inside of A2"), a2._aString); | ||||
|       CPPUNIT_ASSERT_EQUAL(std::string("Another A-String"), a2._anotherString); | ||||
|       CPPUNIT_ASSERT_EQUAL(1234567890ul, a2._aLong); | ||||
|       CPPUNIT_ASSERT_EQUAL(std::string("Text from A2"), a2.aText); | ||||
|       CPPUNIT_ASSERT_EQUAL(987654321l, a2.childOfB.aLong); | ||||
|       CPPUNIT_ASSERT_EQUAL('Q', a2.childOfB.aChar); | ||||
|       CPPUNIT_ASSERT_EQUAL(2.5f, a2.childOfB.aFloat); | ||||
|       CPPUNIT_ASSERT_EQUAL((short)-127, a2.childOfB.aShort); | ||||
|     } | ||||
|     void complexSave() { | ||||
|       A2 a2; | ||||
|       a2._anInteger = -1234; | ||||
|       a2._aBool = true; | ||||
|       a2._aDouble = 3.141; | ||||
|       a2._aString = std::string("This is A inside of A2"); | ||||
|       a2._anotherString = std::string("Another A-String"); | ||||
|       a2._aLong = 1234567890ul; | ||||
|       a2.aText = std::string("Text from A2"); | ||||
|       a2.childOfB.aLong = 987654321l; | ||||
|       a2.childOfB.aChar = 'Q'; | ||||
|       a2.childOfB.aFloat = 2.5f; | ||||
|       a2.childOfB.aShort = (short)-127; | ||||
|       std::stringstream ss; | ||||
|       a2.saveXml(ss); | ||||
|       CPPUNIT_ASSERT_EQUAL | ||||
|         (std::string("<A2>\n" | ||||
|                      "\t<anInteger>-1234</anInteger>\n" | ||||
|                      "\t<aBool>1</aBool>\n" | ||||
|                      "\t<aDouble>3.141</aDouble>\n" | ||||
|                      "\t<aString>This is A inside of A2</aString>\n" | ||||
|                      "\t<anotherString>Another A-String</anotherString>\n" | ||||
|                      "\t<aLong>1234567890</aLong>\n" | ||||
|                      "\t<aText>Text from A2</aText>\n" | ||||
|                      "\t<childOfB>\n" | ||||
|                      "\t\t<aLong>987654321</aLong>\n" | ||||
|                      "\t\t<aChar>Q</aChar>\n" | ||||
|                      "\t\t<float>2.5</float>\n" | ||||
|                      "\t\t<short>-127</short>\n" | ||||
|                      "\t</childOfB>\n" | ||||
|                      "</A2>"), ss.str()); | ||||
|     } | ||||
|     void moreComplexLoad() { | ||||
|       std::stringstream ss | ||||
|         ("<C>\n" | ||||
|          "  <a2>\n" | ||||
|          "    <anInteger>-1234</anInteger>\n" | ||||
|          "    <aBool>1</aBool>\n" | ||||
|          "    <aDouble>3.141</aDouble>\n" | ||||
|          "    <aString>This is A inside of A2</aString>\n" | ||||
|          "    <anotherString>Another A-String</anotherString>\n" | ||||
|          "    <aLong>1234567890</aLong>\n" | ||||
|          "    <aText>Text from A2</aText>\n" | ||||
|          "    <childOfB>\n" | ||||
|          "      <aLong>987654321</aLong>\n" | ||||
|          "      <aChar>Q</aChar>\n" | ||||
|          "      <float>2.5</float>\n" | ||||
|          "      <short>-127</short>\n" | ||||
|          "    </childOfB>\n" | ||||
|          "  </a2>\n" | ||||
|          "  <b2>\n" | ||||
|          "    <aLong>212121</aLong>\n" | ||||
|          "    <aChar>W</aChar>\n" | ||||
|          "    <float>2.25</float>\n" | ||||
|          "    <short>124</short>\n" | ||||
|          "  </b2>\n" | ||||
|          "</C>"); | ||||
|       C c; | ||||
|       CPPUNIT_ASSERT_EQUAL(std::string("<C>\n" | ||||
|                                        "\t<a2>\n" | ||||
|                                        "\t\t<anInteger/>\n" | ||||
|                                        "\t\t<aBool/>\n" | ||||
|                                        "\t\t<aDouble/>\n" | ||||
|                                        "\t\t<aString/>\n" | ||||
|                                        "\t\t<anotherString/>\n" | ||||
|                                        "\t\t<aLong/>\n" | ||||
|                                        "\t\t<aText/>\n" | ||||
|                                        "\t\t<childOfB>\n" | ||||
|                                        "\t\t\t<aLong/>\n" | ||||
|                                        "\t\t\t<aChar/>\n" | ||||
|                                        "\t\t\t<float/>\n" | ||||
|                                        "\t\t\t<short/>\n" | ||||
|                                        "\t\t</childOfB>\n" | ||||
|                                        "\t</a2>\n" | ||||
|                                        "\t<b2>\n" | ||||
|                                        "\t\t<aLong/>\n" | ||||
|                                        "\t\t<aChar/>\n" | ||||
|                                        "\t\t<float/>\n" | ||||
|                                        "\t\t<short/>\n" | ||||
|                                        "\t</b2>\n" | ||||
|                                        "</C>"), c.schema()); | ||||
|       CPPUNIT_ASSERT_NO_THROW(c.loadXml(ss)); | ||||
|       CPPUNIT_ASSERT_EQUAL(-1234, c.a2._anInteger); | ||||
|       CPPUNIT_ASSERT_EQUAL(true, c.a2._aBool); | ||||
|       CPPUNIT_ASSERT_EQUAL(3.141, c.a2._aDouble); | ||||
|       CPPUNIT_ASSERT_EQUAL(std::string("This is A inside of A2"), | ||||
|                            c.a2._aString); | ||||
|       CPPUNIT_ASSERT_EQUAL(std::string("Another A-String"), | ||||
|                            c.a2._anotherString); | ||||
|       CPPUNIT_ASSERT_EQUAL(1234567890ul, c.a2._aLong); | ||||
|       CPPUNIT_ASSERT_EQUAL(std::string("Text from A2"), c.a2.aText); | ||||
|       CPPUNIT_ASSERT_EQUAL(987654321l, c.a2.childOfB.aLong); | ||||
|       CPPUNIT_ASSERT_EQUAL('Q', c.a2.childOfB.aChar); | ||||
|       CPPUNIT_ASSERT_EQUAL(2.5f, c.a2.childOfB.aFloat); | ||||
|       CPPUNIT_ASSERT_EQUAL((short)-127, c.a2.childOfB.aShort); | ||||
|       CPPUNIT_ASSERT_EQUAL(212121l, c.b2.aLong); | ||||
|       CPPUNIT_ASSERT_EQUAL('W', c.b2.aChar); | ||||
|       CPPUNIT_ASSERT_EQUAL(2.25f, c.b2.aFloat); | ||||
|       CPPUNIT_ASSERT_EQUAL((short)124, c.b2.aShort); | ||||
|     } | ||||
|     void moreComplexSave() { | ||||
|       C c; | ||||
|       c.a2._anInteger = -1234; | ||||
|       c.a2._aBool = true; | ||||
|       c.a2._aDouble = 3.141; | ||||
|       c.a2._aString = std::string("This is A inside of A2"); | ||||
|       c.a2._anotherString = std::string("Another A-String"); | ||||
|       c.a2._aLong = 1234567890ul; | ||||
|       c.a2.aText = std::string("Text from A2"); | ||||
|       c.a2.childOfB.aLong = 987654321l; | ||||
|       c.a2.childOfB.aChar = 'Q'; | ||||
|       c.a2.childOfB.aFloat = 2.5f; | ||||
|       c.a2.childOfB.aShort = (short)-127; | ||||
|       c.b2.aLong = 212121l; | ||||
|       c.b2.aChar = 'W'; | ||||
|       c.b2.aFloat = 2.25f; | ||||
|       c.b2.aShort = (short)124; | ||||
|       std::stringstream ss; | ||||
|       c.saveXml(ss); | ||||
|       CPPUNIT_ASSERT_EQUAL | ||||
|         (std::string("<C>\n" | ||||
|                      "\t<a2>\n" | ||||
|                      "\t\t<anInteger>-1234</anInteger>\n" | ||||
|                      "\t\t<aBool>1</aBool>\n" | ||||
|                      "\t\t<aDouble>3.141</aDouble>\n" | ||||
|                      "\t\t<aString>This is A inside of A2</aString>\n" | ||||
|                      "\t\t<anotherString>Another A-String</anotherString>\n" | ||||
|                      "\t\t<aLong>1234567890</aLong>\n" | ||||
|                      "\t\t<aText>Text from A2</aText>\n" | ||||
|                      "\t\t<childOfB>\n" | ||||
|                      "\t\t\t<aLong>987654321</aLong>\n" | ||||
|                      "\t\t\t<aChar>Q</aChar>\n" | ||||
|                      "\t\t\t<float>2.5</float>\n" | ||||
|                      "\t\t\t<short>-127</short>\n" | ||||
|                      "\t\t</childOfB>\n" | ||||
|                      "\t</a2>\n" | ||||
|                      "\t<b2>\n" | ||||
|                      "\t\t<aLong>212121</aLong>\n" | ||||
|                      "\t\t<aChar>W</aChar>\n" | ||||
|                      "\t\t<float>2.25</float>\n" | ||||
|                      "\t\t<short>124</short>\n" | ||||
|                      "\t</b2>\n" | ||||
|                      "</C>"), ss.str()); | ||||
|     } | ||||
|     CPPUNIT_TEST_SUITE(SerializationTest); | ||||
|     CPPUNIT_TEST(memberDeclaration); | ||||
|     CPPUNIT_TEST(store); | ||||
|     CPPUNIT_TEST(restore); | ||||
|     CPPUNIT_TEST(complexLoad); | ||||
|     CPPUNIT_TEST(complexSave); | ||||
|     CPPUNIT_TEST(moreComplexLoad); | ||||
|     CPPUNIT_TEST(moreComplexSave); | ||||
|     CPPUNIT_TEST_SUITE_END(); | ||||
| }; | ||||
| CPPUNIT_TEST_SUITE_REGISTRATION(SerializationTest); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user