From 2860cdf5ece94e5c2b2dbbaa8388f4169cc4ecf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=A4ckerlin?= Date: Wed, 6 May 2009 07:13:50 +0000 Subject: [PATCH] closes #1 --- doc/examples/contain_serialization.cxx | 5 - doc/examples/makefile.am | 3 +- doc/examples/optional_serialization.cxx | 46 +++++++++ src/xml-cxx/xml.hxx | 94 +++++++++++-------- src/xml.cxx | 58 ++++++++++-- test/makefile.am | 5 +- test/optional_serialization_test.cxx | 118 ++++++++++++++++++++++++ 7 files changed, 277 insertions(+), 52 deletions(-) create mode 100644 doc/examples/optional_serialization.cxx create mode 100644 test/optional_serialization_test.cxx diff --git a/doc/examples/contain_serialization.cxx b/doc/examples/contain_serialization.cxx index 5c53951..6ba9343 100644 --- a/doc/examples/contain_serialization.cxx +++ b/doc/examples/contain_serialization.cxx @@ -35,10 +35,6 @@ class B: public xml::Serialize { } }; -std::ostream& fn(std::ostream& os) { - os<\n" @@ -56,7 +52,6 @@ int main(int, char**) { std::cout<<"Text B: "< +#include +#include + +class A: public xml::Serialize { + public: + xml::Optional a; + xml::Optional txt; + protected: + void initXmlMembers() { + className("A"); + persist(a, "a"); + persist(txt, "txt"); + } +}; + +class B: public xml::Serialize { + public: + xml::Optional a; + xml::Optional i; + xml::Optional txt; + protected: + void initXmlMembers() { + className("B"); + persist(a, "a"); + persist(i, "i"); + persist(txt, "txt"); + } +}; + +int main(int, char**) { + { // Serialization as a member + std::stringstream ss(""); + B b; + b.loadXml(ss); + b.saveXml(std::cout)< friend class Optional; template friend class Container; template friend class AssociativeContainer; template friend class AssociativeMap; @@ -1098,9 +1102,9 @@ namespace xml { Serialize(const Serialize& other) throw(); virtual ~Serialize(); Serialize& operator=(const Serialize& other) throw(); - Serialize& className(const std::string& name) throw(); - virtual Serialize& persist(Serialize& member, - const std::string& name) throw(); + virtual Serialize& className(const std::string& name) throw(); + Serialize& persist(Serialize& member, + const std::string& name) throw(); Serialize& persist(bool& member, const std::string& name) throw(); Serialize& persist(char& member, @@ -1136,6 +1140,7 @@ namespace xml { static void registerFromNode(FromNodeFunc fromNodeFunc); static void registerToNode(ToNodeFunc toNodeFunc); static void registerClear(ClearFunc clearFunc); + virtual void clear() throw(); protected: virtual void initXmlMembers(); void checkInit(const Serialize* const ser=0) const { @@ -1145,11 +1150,15 @@ namespace xml { if (!_xmlFactory) const_cast(this)->initXmlMembers(); } } - virtual void clear() throw(); /*! @todo Why does @c protected: not work here?!? Children can't access the members if they are protected! */ public: //! @cond INTERNAL + template friend bool assignFromNode(Any member, + const xml::Node& node); + template friend bool assigntoNode(Any member, + const xml::Node& node); + virtual bool optional() const throw(); void clear(Any member) throw(); void reset() throw(); void copy(const Serialize& o) throw(); @@ -1162,8 +1171,8 @@ namespace xml { _xmlFactory = schema; return *this; } - void fromNode(Any member, const xml::Node& node); - void toNode(const Any member, xml::Node& node) const; + virtual void fromNode(Any member, const xml::Node& node); + virtual void toNode(const Any member, xml::Node& node) const; std::map _xmlNames; xml::Factory _xmlFactory; static std::set _fromNode; @@ -1174,54 +1183,71 @@ namespace xml { template class Optional: public Serialize { public: - Optional() throw() {} - Optional(const Optional& o) throw(): _member(new TYPE(*o._member)) {} - Optional(const TYPE& mem) throw(): _member(new TYPE(mem)) {} + Optional() throw(): _valid(false) {} + Optional(const Optional& o) throw(): + _member(o._member), _valid(o.valid) { + } + Optional(const TYPE& mem) throw(): + _member(mem), _valid(true) { + } virtual ~Optional() throw() {} Optional& operator=(const Optional& o) throw() { - _member = new TYPE(*o._member); + _member = o._member; + _valid = o._valid; return *this; } Optional& operator=(const TYPE& mem) throw() { - _member = new TYPE(mem); + _member = mem; + _valid = true; return *this; } operator bool() const throw() { - return _member.get(); + return _valid; } const TYPE& operator*() const throw() { - return *_member; + return _member; } TYPE& operator*() throw() { - return *_member; + return _member; } const TYPE*const operator->() const throw() { - return _member.get(); + return &_member; } TYPE*const operator->() throw() { - return _member.get(); + return &_member; } - Optional& reset() throw() { - _member.reset(); + 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); + } return *this; } protected: - virtual void clear() throw() { - _member.reset(); + virtual bool optional() const throw() { + return true; } - virtual std::ostream& saveXml(std::ostream& os, - const std::string& name = std::string()) - const throw() { - if (!_member.get()) return os; - checkInit(); - xml::Node node(*_xmlFactory); - if (name.size()) node.name(name); - toNode(*_member, node); - os<(this)->_member); + Serialize::toNode(mem, node); } private: - std::auto_ptr _member; + TYPE _member; + bool _valid; }; //! @addtogroup serialization @@ -1400,17 +1426,13 @@ namespace xml { if (name.size()) factory->name(name); std::auto_ptr node(factory.read(is)); CONTAINER_TYPE::clear(); - LOG("READING: "<<*node); for (xml::Node::size_type i(0); ichildren(); ++i) { typename CONTAINER_TYPE::key_type key; typename CONTAINER_TYPE::mapped_type data; - LOG("READING Key: "<<(*node)[i]<<"Value:"<<(*node)[1+i]); Serialize::fromNode(&key, (*node)[i]); // reads into tmp Serialize::fromNode(&data, (*node)[++i]); // key&value insert(typename CONTAINER_TYPE::value_type(key, data)); - LOG("READ"); } - LOG("DONE"); return is; } virtual std::ostream& saveXml(std::ostream& os, diff --git a/src/xml.cxx b/src/xml.cxx index 986c402..c8de363 100644 --- a/src/xml.cxx +++ b/src/xml.cxx @@ -9,6 +9,7 @@ #include #include #include +#include unsigned int MethodTrace::_level(0); @@ -310,6 +311,25 @@ namespace xml { _contents.push_back(o.clone(this).release()); return *this; } + //! Removes a given child node. + Node& Node::remove(Node& n) throw(access_error) { + Contents::iterator it(std::find(_contents.begin(), _contents.end(), &n)); + if (it==_contents.end()) throw access_error(*this, n.name()); + _contents.erase(it); + return *this; + } + //! Removes the first child node of a given name. + Node& Node::remove(const std::string& n) throw(access_error) { + Node* t(find(n)); + if (!t) throw access_error(*this, n); + return remove(*t); + } + //! Removes the child node of a given position. + Node& Node::remove(size_type n) throw(out_of_range) { + if (n>=children()) throw out_of_range(*this, n); + _contents.erase(_contents.begin()+n); + return *this; + } //! Set a list of attributes. /*! Existing attributes with the same name are overwritten. */ Node& Node::set(const Attributes& o) throw() { @@ -1020,13 +1040,21 @@ namespace xml { } Serialize& Serialize::persist(Serialize& ser, const std::string& name) throw() { - ser.checkInit(); - _xmlNames[name] = &ser; - 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<children(); ++i) + node<<(*ser._xmlFactory)[i]; + _xmlFactory = schema<::const_iterator it(_xmlNames.begin()); it!=_xmlNames.end(); ++it) clear(it->second); } - void Serialize::initXmlMembers() {} void Serialize::reset() throw() { _xmlFactory.reset(); _xmlNames.clear(); @@ -1156,6 +1184,10 @@ namespace xml { if ((**it)(member)) return; // found match throw type_not_registered(member.type().name()); } + bool Serialize::optional() const throw() { + return false; + } + std::set Serialize::_fromNode; std::set Serialize::_toNode; std::set Serialize::_clear; @@ -1201,6 +1233,10 @@ namespace xml { template<> bool assignFromNode(Any member, const xml::Node& node) { if (!any_cast(&member)) return false; + if (any_cast(member)->optional()) { + (*any_cast(member)).fromNode(member, node); + return true; + } //! @todo improve this (inefficient) std::stringstream ss; // simple but inefficient: rewrite and reread ss< bool assignToNode(const Any member, xml::Node& node) { if (!any_cast(&member)) return false; + if (any_cast(member)->optional()) { + any_cast(member)->toNode(member, node); + return true; + } std::stringstream ss; any_cast(member)->saveXml(ss, node.name()); xml::Factory factory(node); @@ -1238,7 +1278,7 @@ namespace xml { } template<> bool clearMember(Any member) { if (!any_cast(&member)) return false; - any_cast(member)->clear(); + any_cast(member)->clear(); return true; } // Init To and From Node --------------------------------------------------- diff --git a/test/makefile.am b/test/makefile.am index 2cec032..f5afc7b 100644 --- a/test/makefile.am +++ b/test/makefile.am @@ -7,12 +7,15 @@ AM_CXXFLAGS += -I ${top_srcdir}/src AM_LDFLAGS = -L${top_builddir}/src LDADD = -lcppunit -lxml-cxx -check_PROGRAMS = xml_test serialization_test container_serialization_test +check_PROGRAMS = xml_test serialization_test \ + container_serialization_test optional_serialization_test + TESTS=${check_PROGRAMS} xml_test_SOURCES = xml_test.cxx serialization_test_SOURCES = serialization_test.cxx container_serialization_test_SOURCES = container_serialization_test.cxx +optional_serialization_test_SOURCES = optional_serialization_test.cxx CLEANFILES = MAINTAINERCLEANFILES = makefile.in diff --git a/test/optional_serialization_test.cxx b/test/optional_serialization_test.cxx new file mode 100644 index 0000000..65f24c1 --- /dev/null +++ b/test/optional_serialization_test.cxx @@ -0,0 +1,118 @@ +/*! @file + + @id $Id: serialization_test.cxx 31 2009-04-28 07:36:07Z $ +*/ +// 1 2 3 4 5 6 7 8 +// 45678901234567890123456789012345678901234567890123456789012345678901234567890 + +#include +#include +#include +#include +#include +#include +#include + +class A: public xml::Serialize { + public: + xml::Optional _int; + protected: + void initXmlMembers() { + className("A"); + persist(_int, "int"); + } +}; + +class B: public xml::Serialize { + public: + xml::Optional _long; + xml::Optional _short; + protected: + void initXmlMembers() { + className("B"); + persist(_long, "long"); + persist(_short, "short"); + } +}; + +class B1: public B { + public: + xml::Optional _a1; + xml::Optional _a2; + xml::Optional _a3; + protected: + void initXmlMembers() { + B::initXmlMembers(); + className("B1"); + persist(_a1, "a1"); + persist(_a2, "a2"); + persist(_a3, "a3"); + } +}; + +class OptionalSerializationTest: public CppUnit::TestFixture { + public: + void checkOptional() { + std::string with("\n" + "\t13\n" + ""); + std::string without(""); + A a; + CPPUNIT_ASSERT_EQUAL(std::string("\n" + "\t\n" + ""), a.schema()); + std::stringstream ss1a(with); + CPPUNIT_ASSERT_NO_THROW(a.loadXml(ss1a)); + CPPUNIT_ASSERT_EQUAL(true, (bool)a._int); + CPPUNIT_ASSERT_EQUAL(13, *a._int); + std::stringstream ss1b; + CPPUNIT_ASSERT_NO_THROW(a.saveXml(ss1b)); + CPPUNIT_ASSERT_EQUAL(with, ss1b.str()); + std::stringstream ss2a(without); + CPPUNIT_ASSERT_NO_THROW(a.loadXml(ss2a)); + CPPUNIT_ASSERT_EQUAL(false, (bool)a._int); + std::stringstream ss2b; + CPPUNIT_ASSERT_NO_THROW(a.saveXml(ss2b)); + CPPUNIT_ASSERT_EQUAL(without, ss2b.str()); + } + void checkMoreOptional() { + std::string first("\n" + "\t13\n" + "\t\n" + "\t\n" + "\t\t42\n" + "\t\n" + ""); + B1 b; + std::stringstream ifirst(first); + CPPUNIT_ASSERT_NO_THROW(b.loadXml(ifirst)); + CPPUNIT_ASSERT_EQUAL(false, (bool)b._long); + CPPUNIT_ASSERT_EQUAL(true, (bool)b._short); + CPPUNIT_ASSERT_EQUAL((short)13, *b._short); + CPPUNIT_ASSERT_EQUAL(false, (bool)b._a1); + CPPUNIT_ASSERT_EQUAL(true, (bool)b._a2); + CPPUNIT_ASSERT_EQUAL(false, (bool)b._a2->_int); + CPPUNIT_ASSERT_EQUAL(true, (bool)b._a3); + CPPUNIT_ASSERT_EQUAL(true, (bool)b._a3->_int); + CPPUNIT_ASSERT_EQUAL(42, *b._a3->_int); + std::stringstream ofirst; + CPPUNIT_ASSERT_NO_THROW(b.saveXml(ofirst)); + CPPUNIT_ASSERT_EQUAL(first, ofirst.str()); + } + CPPUNIT_TEST_SUITE(OptionalSerializationTest); + CPPUNIT_TEST(checkOptional); + CPPUNIT_TEST(checkMoreOptional); + CPPUNIT_TEST_SUITE_END(); +}; +CPPUNIT_TEST_SUITE_REGISTRATION(OptionalSerializationTest); + +int main(int argc, char** argv) try { + std::ofstream ofs((*argv+std::string(".xml")).c_str()); + CppUnit::TextUi::TestRunner runner; + //runner.setOutputter(new CppUnit::XmlOutputter(&runner.result(), ofs)); + runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest()); + return runner.run() ? 0 : 1; + } catch (std::exception& e) { + std::cerr<<"***Exception: "<