diff --git a/doc/examples/contain_serialization.cxx b/doc/examples/contain_serialization.cxx index 8bfec40..ad23bee 100644 --- a/doc/examples/contain_serialization.cxx +++ b/doc/examples/contain_serialization.cxx @@ -33,7 +33,7 @@ class B: public xml::Serialize { className("b") .persist(i, "i") .persist(txt, "txt") - .persist(a); + .persist(a, "a"); } }; diff --git a/doc/examples/inherit_serialization.cxx b/doc/examples/inherit_serialization.cxx index c1f971c..48c2596 100644 --- a/doc/examples/inherit_serialization.cxx +++ b/doc/examples/inherit_serialization.cxx @@ -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 #include @@ -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("\n" - "\t1234\n" - "\tDies ist ein Serialisierungs-Test\n" - ""); - A a; - a._ser.loadXml(ss); - if (a.a==1234) a.a=4321; - a._ser.saveXml(std::cout)<\n" - "\t1234\n" - "\tDies ist ein Serialisierungs-Test\n" - ""); - B b; - b.loadXml(ss); - if (b.a==1234) b.a=4321; - b.saveXml(std::cout)<\n" - "\t1234\n" - "\tDies ist ein Serialisierungs-Test\n" - ""); - 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)<\n" - "\t1234\n" - "\tDies ist ein Serialisierungs-Test\n" - ""); - 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)<\n" + "\t1234\n" + "\t5678\n" + "\tDies ist ein Serialisierungs-Test\n" + ""); + B b; + b.loadXml(ss); + if (b.b==1234) b.b=4321; + if (b.a==5678) b.a=8765; + b.saveXml(std::cout)< #include #include +#include #include #include #include @@ -42,7 +43,12 @@ class MethodTrace { }; #define TRACE MethodTrace XXX_METHOD(this, __PRETTY_FUNCTION__) #define LOG(X) std::clog<<__PRETTY_FUNCTION__<<"\t**** "<() throw(factory_not_valid); bool ws(char c) throw(); std::auto_ptr 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 + Serialize& persist(std::list& member, + const std::string& name) throw() { + return persist(member, name, name); + } + template + Serialize& persist(std::list& member, + const std::string& list, + const std::string& item) throw() { + mapName >()[std::make_pair(this, list)] + = &member; + mapMember >()[&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 - Serialize& persist(TYPE& member, const std::string& name) throw() { - assert(mapName().find(std::make_pair(this, name))==mapName().end()); - assert(mapMember().find(&member)==mapMember().end()); - assert(_xmlNames.find(name)==_xmlNames.end()); + Serialize& persistSimpleType(TYPE& member, + const std::string& name) throw() { + ASSERT("type: "<"); + } + 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,32 +1021,118 @@ namespace xml { xml::Node& node) { //! @todo improve this (inefficient) std::stringstream ss; // simple but inefficient: rewrite and reread - ss<_xmlFactory->name()]; - member->loadXml(ss); + ss<loadXml(ss, node.name()); } template<> void Serialize::toNode(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); + xml::Node& node) const { + 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() { - // Remove my entries from the maps + 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()[std::make_pair(this, name)] = &ser; + mapMember()[&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); ichildren(); ++i) + node<<(*ser._xmlFactory)[i]; + _xmlFactory = schema<(this)->initXmlMembers(); + xml::Node node(*_xmlFactory); + if (name.size()) node.name(name); for (std::map::const_iterator it(_xmlNames.begin()); it!=_xmlNames.end(); ++it) { #define QWERTZ_CHECK_TYPE_ZTREWQ___XXX(TYPE) \ - if (*it->second==typeid(TYPE)) { \ - mapMember() \ - .erase(mapName()[std::make_pair(this, it->first)]); \ - mapName().erase(std::make_pair(this, it->first)); \ - } else + if (*it->second==typeid(TYPE)) \ + toNode(mapName()[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) @@ -1038,34 +1150,24 @@ namespace xml { QWERTZ_CHECK_TYPE_ZTREWQ___XXX(long) QWERTZ_CHECK_TYPE_ZTREWQ___XXX(signed long) QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned long) - {} // ignore coding error here, but memory leaks - better abort? + throw std::runtime_error(it->second->name()); #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; - } - Serialize& Serialize::persist(Serialize& ser) throw() { - ser.initXmlMembers(); - std::string name(ser._xmlFactory->name()); - mapName()[std::make_pair(this, name)] = &ser; - mapMember()[&ser] = name; - _xmlNames[name] = &typeid(Serialize); - xml::Node schema(*_xmlFactory); - schema<<*ser._xmlFactory; - _xmlFactory = schema; - return *this; + } + os<(this)->initXmlMembers(); - xml::Node node(*_xmlFactory); + 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 node(factory.read(is)); for (std::map::const_iterator it(_xmlNames.begin()); it!=_xmlNames.end(); ++it) { #define QWERTZ_CHECK_TYPE_ZTREWQ___XXX(TYPE) \ if (*it->second==typeid(TYPE)) \ - toNode(mapName()[std::make_pair(this, it->first)], node); \ + fromNode(mapName()[std::make_pair(this, it->first)], \ + (*node)[it->first]); \ else QWERTZ_CHECK_TYPE_ZTREWQ___XXX(Serialize) QWERTZ_CHECK_TYPE_ZTREWQ___XXX(std::string) @@ -1086,26 +1188,33 @@ namespace xml { QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned long) throw std::runtime_error(it->second->name()); #undef QWERTZ_CHECK_TYPE_ZTREWQ___XXX - } - os< node(_xmlFactory.read(is)); + std::string Serialize::schema() const throw() { + if (!_xmlFactory) const_cast(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::const_iterator it(_xmlNames.begin()); it!=_xmlNames.end(); ++it) { -#define QWERTZ_CHECK_TYPE_ZTREWQ___XXX(TYPE) \ - if (*it->second==typeid(TYPE)) \ - fromNode(mapName()[std::make_pair(this, it->first)], *node); \ - else +#define QWERTZ_CHECK_TYPE_ZTREWQ___XXX(TYPE) \ + if (*it->second==typeid(TYPE)) { \ + mapMember() \ + .erase(mapName()[std::make_pair(this, it->first)]); \ + mapName().erase(std::make_pair(this, 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(char) QWERTZ_CHECK_TYPE_ZTREWQ___XXX(signed char) QWERTZ_CHECK_TYPE_ZTREWQ___XXX(unsigned char) QWERTZ_CHECK_TYPE_ZTREWQ___XXX(short) @@ -1117,11 +1226,14 @@ namespace xml { 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()); + {} // ignore coding error here, but memory leaks - better abort? #undef QWERTZ_CHECK_TYPE_ZTREWQ___XXX } - return is; } - void Serialize::initXmlMembers() {} + void Serialize::copy(const Serialize& o) throw() { + clear(); + _xmlFactory.reset(); + initXmlMembers(); + } } diff --git a/test/serialization_test.cxx b/test/serialization_test.cxx index db5fe5e..3d92e85 100644 --- a/test/serialization_test.cxx +++ b/test/serialization_test.cxx @@ -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("\n" + " -1234\n" + " 1\n" + " 3.141\n" + " This is A inside of A2\n" + " Another A-String\n" + " 1234567890\n" + " Text from A2\n" + " \n" + " 987654321\n" + " Q\n" + " 2.5\n" + " -127\n" + " \n" + ""); + A2 a2; + CPPUNIT_ASSERT_EQUAL(std::string("\n" + "\t\n" + "\t\n" + "\t\n" + "\t\n" + "\t\n" + "\t\n" + "\t\n" + "\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\n" + ""), 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("\n" + "\t-1234\n" + "\t1\n" + "\t3.141\n" + "\tThis is A inside of A2\n" + "\tAnother A-String\n" + "\t1234567890\n" + "\tText from A2\n" + "\t\n" + "\t\t987654321\n" + "\t\tQ\n" + "\t\t2.5\n" + "\t\t-127\n" + "\t\n" + ""), ss.str()); + } + void moreComplexLoad() { + std::stringstream ss + ("\n" + " \n" + " -1234\n" + " 1\n" + " 3.141\n" + " This is A inside of A2\n" + " Another A-String\n" + " 1234567890\n" + " Text from A2\n" + " \n" + " 987654321\n" + " Q\n" + " 2.5\n" + " -127\n" + " \n" + " \n" + " \n" + " 212121\n" + " W\n" + " 2.25\n" + " 124\n" + " \n" + ""); + C c; + CPPUNIT_ASSERT_EQUAL(std::string("\n" + "\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\t\n" + "\t\t\n" + "\t\n" + "\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\n" + ""), 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("\n" + "\t\n" + "\t\t-1234\n" + "\t\t1\n" + "\t\t3.141\n" + "\t\tThis is A inside of A2\n" + "\t\tAnother A-String\n" + "\t\t1234567890\n" + "\t\tText from A2\n" + "\t\t\n" + "\t\t\t987654321\n" + "\t\t\tQ\n" + "\t\t\t2.5\n" + "\t\t\t-127\n" + "\t\t\n" + "\t\n" + "\t\n" + "\t\t212121\n" + "\t\tW\n" + "\t\t2.25\n" + "\t\t124\n" + "\t\n" + ""), 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);