From 231ef2762d0879ccd6e297dd50d69d47faf385aa Mon Sep 17 00:00:00 2001 From: Aleksandar Fabijanic Date: Wed, 1 Aug 2018 08:06:59 -0700 Subject: [PATCH] Preserve entries order in DynamicStruct #2410 (#2413) * Preserve entries order in DynamicStruct #2410 * disable C++11 default * ifdef C++11 code --- Foundation/include/Poco/Dynamic/Struct.h | 485 ++++++++++++++++++-- Foundation/include/Poco/Dynamic/Var.h | 23 +- Foundation/include/Poco/Dynamic/VarHolder.h | 11 + Foundation/include/Poco/SharedPtr.h | 2 + Foundation/src/Var.cpp | 29 +- Foundation/src/VarIterator.cpp | 2 +- Foundation/testsuite/src/VarTest.cpp | 129 +++++- Foundation/testsuite/src/VarTest.h | 4 + JSON/include/Poco/JSON/Object.h | 83 +++- JSON/src/Object.cpp | 113 +++-- JSON/testsuite/src/JSONTest.cpp | 17 +- 11 files changed, 825 insertions(+), 73 deletions(-) diff --git a/Foundation/include/Poco/Dynamic/Struct.h b/Foundation/include/Poco/Dynamic/Struct.h index 051d82c90..b843d22c5 100644 --- a/Foundation/include/Poco/Dynamic/Struct.h +++ b/Foundation/include/Poco/Dynamic/Struct.h @@ -22,6 +22,8 @@ #include "Poco/Dynamic/Var.h" #include "Poco/Dynamic/VarHolder.h" #include "Poco/SharedPtr.h" +#include "Poco/OrderedMap.h" +#include "Poco/OrderedSet.h" #include #include @@ -30,19 +32,19 @@ namespace Poco { namespace Dynamic { -template +template , typename S = std::set > class Struct /// Struct allows to define a named collection of Var objects. { public: - typedef typename std::map Data; - typedef typename std::set NameSet; + typedef M Data; + typedef S NameSet; typedef typename Data::iterator Iterator; typedef typename Data::const_iterator ConstIterator; typedef typename Struct::Data::value_type ValueType; typedef typename Struct::Data::size_type SizeType; - typedef typename std::pair::Iterator, bool> InsRetVal; - typedef typename Poco::SharedPtr > Ptr; + typedef typename std::pair::Iterator, bool> InsRetVal; + typedef typename Poco::SharedPtr > Ptr; Struct(): _data() /// Creates an empty Struct @@ -57,13 +59,19 @@ public: template Struct(const std::map& val) { - typedef typename std::map::const_iterator MapConstIterator; - - MapConstIterator it = val.begin(); - MapConstIterator end = val.end(); - for (; it != end; ++it) _data.insert(ValueType(it->first, Var(it->second))); + assignMap(val); } +#ifdef POCO_ENABLE_CPP11 + + template + Struct(const OrderedMap& val) + { + assignMap(val); + } + +#endif // POCO_ENABLE_CPP11 + virtual ~Struct() /// Destroys the Struct. { @@ -199,15 +207,30 @@ public: } private: + template + void assignMap(const T& map) + { + typedef typename T::const_iterator MapConstIterator; + + MapConstIterator it = map.begin(); + MapConstIterator end = map.end(); + for (; it != end; ++it) _data.insert(ValueType(it->first, Var(it->second))); + } + Data _data; }; template <> -class VarHolderImpl >: public VarHolder +class VarHolderImpl, std::set > >: public VarHolder { public: - VarHolderImpl(const Struct& val): _val(val) + typedef std::string KeyType; + typedef std::map MapType; + typedef std::set SetType; + typedef Struct ValueType; + + VarHolderImpl(const ValueType& val): _val(val) { } @@ -217,7 +240,7 @@ public: const std::type_info& type() const { - return typeid(Struct); + return typeid(ValueType); } void convert(Int8&) const @@ -283,8 +306,8 @@ public: void convert(std::string& val) const { val.append("{ "); - Struct::ConstIterator it = _val.begin(); - Struct::ConstIterator itEnd = _val.end(); + ValueType::ConstIterator it = _val.begin(); + ValueType::ConstIterator itEnd = _val.end(); if (!_val.empty()) { Var key(it->first); @@ -324,7 +347,7 @@ public: return cloneHolder(pVarHolder, _val); } - const Struct& value() const + const ValueType& value() const { return _val; } @@ -339,6 +362,11 @@ public: return true; } + bool isOrdered() const + { + return false; + } + bool isInteger() const { return false; @@ -364,36 +392,41 @@ public: return _val.size(); } - Var& operator [] (const std::string& name) + Var& operator [] (const KeyType& name) { return _val[name]; } - const Var& operator [] (const std::string& name) const + const Var& operator [] (const KeyType& name) const { return _val[name]; } private: - Struct _val; + ValueType _val; }; template <> -class VarHolderImpl >: public VarHolder +class VarHolderImpl, std::set > > : public VarHolder { public: - VarHolderImpl(const Struct& val): _val(val) + typedef int KeyType; + typedef std::map MapType; + typedef std::set SetType; + typedef Struct ValueType; + + VarHolderImpl(const ValueType& val) : _val(val) { } ~VarHolderImpl() { } - + const std::type_info& type() const { - return typeid(Struct); + return typeid(ValueType); } void convert(Int8&) const @@ -405,7 +438,7 @@ public: { throw BadCastException("Cannot cast Struct type to Int16"); } - + void convert(Int32&) const { throw BadCastException("Cannot cast Struct type to Int32"); @@ -425,7 +458,7 @@ public: { throw BadCastException("Cannot cast Struct type to UInt16"); } - + void convert(UInt32&) const { throw BadCastException("Cannot cast Struct type to UInt32"); @@ -459,8 +492,8 @@ public: void convert(std::string& val) const { val.append("{ "); - Struct::ConstIterator it = _val.begin(); - Struct::ConstIterator itEnd = _val.end(); + ValueType::ConstIterator it = _val.begin(); + ValueType::ConstIterator itEnd = _val.end(); if (!_val.empty()) { Var key(it->first); @@ -477,7 +510,7 @@ public: val.append(" : "); Impl::appendJSONValue(val, it->second); } - val.append(" }"); + val.append(" }"); } void convert(Poco::DateTime&) const @@ -499,8 +532,8 @@ public: { return cloneHolder(pVarHolder, _val); } - - const Struct& value() const + + const ValueType& value() const { return _val; } @@ -515,6 +548,200 @@ public: return true; } + bool isOrdered() const + { + return false; + } + + bool isInteger() const + { + return false; + } + + bool isSigned() const + { + return false; + } + + bool isNumeric() const + { + return false; + } + + bool isString() const + { + return false; + } + + std::size_t size() const + { + return _val.size(); + } + + Var& operator [] (const KeyType& name) + { + return _val[name]; + } + + const Var& operator [] (const KeyType& name) const + { + return _val[name]; + } + +private: + ValueType _val; +}; + + +#ifdef POCO_ENABLE_CPP11 + + +template <> +class VarHolderImpl, Poco::OrderedSet > > : public VarHolder +{ +public: + typedef std::string KeyType; + typedef Poco::OrderedMap MapType; + typedef Poco::OrderedSet SetType; + typedef Struct ValueType; + + VarHolderImpl(const ValueType& val) : _val(val) + { + } + + ~VarHolderImpl() + { + } + + const std::type_info& type() const + { + return typeid(ValueType); + } + + void convert(Int8&) const + { + throw BadCastException("Cannot cast Struct type to Int8"); + } + + void convert(Int16&) const + { + throw BadCastException("Cannot cast Struct type to Int16"); + } + + void convert(Int32&) const + { + throw BadCastException("Cannot cast Struct type to Int32"); + } + + void convert(Int64&) const + { + throw BadCastException("Cannot cast Struct type to Int64"); + } + + void convert(UInt8&) const + { + throw BadCastException("Cannot cast Struct type to UInt8"); + } + + void convert(UInt16&) const + { + throw BadCastException("Cannot cast Struct type to UInt16"); + } + + void convert(UInt32&) const + { + throw BadCastException("Cannot cast Struct type to UInt32"); + } + + void convert(UInt64&) const + { + throw BadCastException("Cannot cast Struct type to UInt64"); + } + + void convert(bool&) const + { + throw BadCastException("Cannot cast Struct type to bool"); + } + + void convert(float&) const + { + throw BadCastException("Cannot cast Struct type to float"); + } + + void convert(double&) const + { + throw BadCastException("Cannot cast Struct type to double"); + } + + void convert(char&) const + { + throw BadCastException("Cannot cast Struct type to char"); + } + + void convert(std::string& val) const + { + val.append("{ "); + ValueType::ConstIterator it = _val.begin(); + ValueType::ConstIterator itEnd = _val.end(); + if (!_val.empty()) + { + Var key(it->first); + Impl::appendJSONKey(val, key); + val.append(" : "); + Impl::appendJSONValue(val, it->second); + ++it; + } + for (; it != itEnd; ++it) + { + val.append(", "); + Var key(it->first); + Impl::appendJSONKey(val, key); + val.append(" : "); + Impl::appendJSONValue(val, it->second); + } + val.append(" }"); + } + + void convert(Poco::DateTime&) const + { + throw BadCastException("Struct -> Poco::DateTime"); + } + + void convert(Poco::LocalDateTime&) const + { + throw BadCastException("Struct -> Poco::LocalDateTime"); + } + + void convert(Poco::Timestamp&) const + { + throw BadCastException("Struct -> Poco::Timestamp"); + } + + VarHolder* clone(Placeholder* pVarHolder = 0) const + { + return cloneHolder(pVarHolder, _val); + } + + const ValueType& value() const + { + return _val; + } + + bool isArray() const + { + return false; + } + + bool isStruct() const + { + return true; + } + + bool isOrdered() const + { + return true; + } + bool isInteger() const { return false; @@ -540,26 +767,218 @@ public: return _val.size(); } - Var& operator [] (int name) + Var& operator [] (const KeyType& name) { return _val[name]; } - const Var& operator [] (int name) const + const Var& operator [] (const KeyType& name) const { return _val[name]; } private: - Struct _val; + ValueType _val; }; +template <> +class VarHolderImpl, Poco::OrderedSet > > : public VarHolder +{ +public: + typedef int KeyType; + typedef Poco::OrderedMap MapType; + typedef Poco::OrderedSet SetType; + typedef Struct ValueType; + + VarHolderImpl(const ValueType& val) : _val(val) + { + } + + ~VarHolderImpl() + { + } + + const std::type_info& type() const + { + return typeid(ValueType); + } + + void convert(Int8&) const + { + throw BadCastException("Cannot cast Struct type to Int8"); + } + + void convert(Int16&) const + { + throw BadCastException("Cannot cast Struct type to Int16"); + } + + void convert(Int32&) const + { + throw BadCastException("Cannot cast Struct type to Int32"); + } + + void convert(Int64&) const + { + throw BadCastException("Cannot cast Struct type to Int64"); + } + + void convert(UInt8&) const + { + throw BadCastException("Cannot cast Struct type to UInt8"); + } + + void convert(UInt16&) const + { + throw BadCastException("Cannot cast Struct type to UInt16"); + } + + void convert(UInt32&) const + { + throw BadCastException("Cannot cast Struct type to UInt32"); + } + + void convert(UInt64&) const + { + throw BadCastException("Cannot cast Struct type to UInt64"); + } + + void convert(bool&) const + { + throw BadCastException("Cannot cast Struct type to bool"); + } + + void convert(float&) const + { + throw BadCastException("Cannot cast Struct type to float"); + } + + void convert(double&) const + { + throw BadCastException("Cannot cast Struct type to double"); + } + + void convert(char&) const + { + throw BadCastException("Cannot cast Struct type to char"); + } + + void convert(std::string& val) const + { + val.append("{ "); + ValueType::ConstIterator it = _val.begin(); + ValueType::ConstIterator itEnd = _val.end(); + if (!_val.empty()) + { + Var key(it->first); + Impl::appendJSONKey(val, key); + val.append(" : "); + Impl::appendJSONValue(val, it->second); + ++it; + } + for (; it != itEnd; ++it) + { + val.append(", "); + Var key(it->first); + Impl::appendJSONKey(val, key); + val.append(" : "); + Impl::appendJSONValue(val, it->second); + } + val.append(" }"); + } + + void convert(Poco::DateTime&) const + { + throw BadCastException("Struct -> Poco::DateTime"); + } + + void convert(Poco::LocalDateTime&) const + { + throw BadCastException("Struct -> Poco::LocalDateTime"); + } + + void convert(Poco::Timestamp&) const + { + throw BadCastException("Struct -> Poco::Timestamp"); + } + + VarHolder* clone(Placeholder* pVarHolder = 0) const + { + return cloneHolder(pVarHolder, _val); + } + + const ValueType& value() const + { + return _val; + } + + bool isArray() const + { + return false; + } + + bool isStruct() const + { + return true; + } + + bool isOrdered() const + { + return true; + } + + bool isInteger() const + { + return false; + } + + bool isSigned() const + { + return false; + } + + bool isNumeric() const + { + return false; + } + + bool isString() const + { + return false; + } + + std::size_t size() const + { + return _val.size(); + } + + Var& operator [] (const KeyType& name) + { + return _val[name]; + } + + const Var& operator [] (const KeyType& name) const + { + return _val[name]; + } + +private: + ValueType _val; +}; + + +#endif // POCO_ENABLE_CPP11 + + } // namespace Dynamic typedef Dynamic::Struct DynamicStruct; +#ifdef POCO_ENABLE_CPP11 +typedef Dynamic::Struct, Poco::OrderedSet > OrderedDynamicStruct; +#endif // POCO_ENABLE_CPP11 } // namespace Poco diff --git a/Foundation/include/Poco/Dynamic/Var.h b/Foundation/include/Poco/Dynamic/Var.h index bd06afe54..acc0d1326 100644 --- a/Foundation/include/Poco/Dynamic/Var.h +++ b/Foundation/include/Poco/Dynamic/Var.h @@ -21,16 +21,20 @@ #include "Poco/Foundation.h" #include "Poco/Format.h" #include "Poco/SharedPtr.h" +#include "Poco/OrderedMap.h" +#include "Poco/OrderedSet.h" #include "Poco/Dynamic/VarHolder.h" #include "Poco/Dynamic/VarIterator.h" #include +#include +#include namespace Poco { namespace Dynamic { -template +template class Struct; @@ -449,6 +453,10 @@ public: bool isStruct() const; /// Returns true if Var represents a struct. + bool isOrdered() const; + /// Returns true if Var represents an ordered struct, + /// false if struct is sorted. + char& at(std::size_t n); /// Returns character at position n. This function only works with /// Var containing a std::string. @@ -595,7 +603,11 @@ private: throw E(errorMessage); } - Var& structIndexOperator(VarHolderImpl >* pStr, int n) const; + template + Var& structIndexOperator(T* pStr, N n) const + { + return pStr->operator[](n); + } #ifdef POCO_NO_SOO @@ -822,6 +834,13 @@ inline bool Var::isStruct() const } +inline bool Var::isOrdered() const +{ + VarHolder* pHolder = content(); + return pHolder ? pHolder->isOrdered() : false; +} + + inline bool Var::isInteger() const { VarHolder* pHolder = content(); diff --git a/Foundation/include/Poco/Dynamic/VarHolder.h b/Foundation/include/Poco/Dynamic/VarHolder.h index e0af6a6d5..39a4fcb0a 100644 --- a/Foundation/include/Poco/Dynamic/VarHolder.h +++ b/Foundation/include/Poco/Dynamic/VarHolder.h @@ -241,6 +241,10 @@ public: /// Returns false. Must be properly overriden in a type /// specialization in order to support the diagnostic. + virtual bool isOrdered() const; + /// Returns false. Must be properly overriden in a type + /// specialization in order to support the diagnostic. + virtual bool isInteger() const; /// Returns false. Must be properly overriden in a type /// specialization in order to support the diagnostic. @@ -612,6 +616,13 @@ inline bool VarHolder::isStruct() const return false; } + +inline bool VarHolder::isOrdered() const +{ + return false; +} + + inline bool VarHolder::isInteger() const { return false; diff --git a/Foundation/include/Poco/SharedPtr.h b/Foundation/include/Poco/SharedPtr.h index 899d44883..44c790ea0 100644 --- a/Foundation/include/Poco/SharedPtr.h +++ b/Foundation/include/Poco/SharedPtr.h @@ -113,6 +113,8 @@ class SharedPtr /// is required. { public: + typedef C Type; + SharedPtr(): _pCounter(new RC), _ptr(0) { } diff --git a/Foundation/src/Var.cpp b/Foundation/src/Var.cpp index 03deaef85..c99e86cb3 100644 --- a/Foundation/src/Var.cpp +++ b/Foundation/src/Var.cpp @@ -362,8 +362,16 @@ Var& Var::getAt(std::size_t n) return holderImpl, InvalidAccessException>("Not a deque.")->operator[](n); else if (isStruct()) - return structIndexOperator(holderImpl, - InvalidAccessException>("Not a struct."), static_cast(n)); + { +#ifdef POCO_ENABLE_CPP11 + if (isOrdered()) + return structIndexOperator(holderImpl, OrderedSet >, + InvalidAccessException>("Not a struct."), static_cast(n)); + else +#endif // POCO_ENABLE_CPP11 + return structIndexOperator(holderImpl, std::set >, + InvalidAccessException>("Not a struct."), static_cast(n)); + } else if (!isString() && !isEmpty() && (n == 0)) return *this; @@ -385,8 +393,17 @@ char& Var::at(std::size_t n) Var& Var::getAt(const std::string& name) { - return holderImpl("Not a struct.")->operator[](name); + if (isStruct()) + { +#ifdef POCO_ENABLE_CPP11 + if (isOrdered()) + return structIndexOperator(holderImpl("Not a struct."), name); + else +#endif // POCO_ENABLE_CPP11 + return structIndexOperator(holderImpl("Not a struct."), name); + } + + throw InvalidAccessException("Not a struct."); } @@ -626,11 +643,11 @@ std::string Var::toString(const Var& any) return res; } - +/* Var& Var::structIndexOperator(VarHolderImpl >* pStr, int n) const { return pStr->operator[](n); } - +*/ } } // namespace Poco::Dynamic diff --git a/Foundation/src/VarIterator.cpp b/Foundation/src/VarIterator.cpp index d816c23c4..1667ae51c 100644 --- a/Foundation/src/VarIterator.cpp +++ b/Foundation/src/VarIterator.cpp @@ -14,7 +14,7 @@ #include "Poco/Dynamic/VarIterator.h" #include "Poco/Dynamic/Var.h" -#include "Poco/Dynamic/Struct.h" +//#include "Poco/Dynamic/Struct.h" #undef min #undef max #include diff --git a/Foundation/testsuite/src/VarTest.cpp b/Foundation/testsuite/src/VarTest.cpp index 747467e9f..2124dd257 100644 --- a/Foundation/testsuite/src/VarTest.cpp +++ b/Foundation/testsuite/src/VarTest.cpp @@ -20,7 +20,6 @@ #include - #if defined(_MSC_VER) && _MSC_VER < 1400 #pragma warning(disable:4800)//forcing value to bool 'true' or 'false' #endif @@ -2256,6 +2255,34 @@ void VarTest::testDynamicStructBasics() } +void VarTest::testOrderedDynamicStructBasics() +{ +#ifdef POCO_ENABLE_CPP11 + OrderedDynamicStruct aStruct; + assertTrue(aStruct.empty()); + assertTrue(aStruct.size() == 0); + assertTrue(aStruct.members().empty()); + + aStruct.insert("First Name", "Little"); + assertTrue(!aStruct.empty()); + assertTrue(aStruct.size() == 1); + assertTrue(*(aStruct.members().begin()) == "First Name"); + assertTrue(aStruct["First Name"] == "Little"); + aStruct.insert("Last Name", "POCO"); + assertTrue(aStruct.members().size() == 2); + aStruct.erase("First Name"); + assertTrue(aStruct.size() == 1); + assertTrue(*(aStruct.members().begin()) == "Last Name"); + aStruct.insert("Age", 1); + assertTrue(aStruct["Age"] == 1); + assertTrue(aStruct.members().size() == 2); + assertTrue(*(aStruct.members().begin()) == "Last Name"); + aStruct.clear(); + assertTrue(aStruct.size() == 0); +#endif // POCO_ENABLE_CPP11 +} + + void VarTest::testDynamicStructString() { DynamicStruct aStruct; @@ -2290,6 +2317,45 @@ void VarTest::testDynamicStructString() } +void VarTest::testOrderedDynamicStructString() +{ +#ifdef POCO_ENABLE_CPP11 + OrderedDynamicStruct aStruct; + aStruct["First Name"] = "Junior"; + aStruct["Last Name"] = "POCO"; + Var a1(aStruct); + assertTrue(a1["First Name"] == "Junior"); + assertTrue(a1["Last Name"] == "POCO"); + a1["First Name"] = "Senior"; + assertTrue(a1["First Name"] == "Senior"); + testGetIdxMustThrow(a1, 0); + + typedef Struct, OrderedSet > OrderedStruct; + OrderedStruct s1; + s1["1"] = 1; + s1["2"] = 2; + s1["3"] = 3; + + OrderedStruct s2(s1); + assertTrue(s2["1"] == 1); + assertTrue(s2["2"] == 2); + assertTrue(s2["3"] == 3); + + OrderedMap m1; + m1["2"] = 2; + m1["1"] = 1; + m1["3"] = 3; + assertTrue (m1.begin()->first == "2"); + assertTrue(m1.begin()->second == 2); + + OrderedStruct m2(m1); + assertTrue(m2["1"] == 1); + assertTrue(m2["2"] == 2); + assertTrue(m2["3"] == 3); +#endif // POCO_ENABLE_CPP11 +} + + void VarTest::testDynamicStructInt() { Dynamic::Struct aStruct; @@ -2325,6 +2391,47 @@ void VarTest::testDynamicStructInt() } +void VarTest::testOrderedDynamicStructInt() +{ +#ifdef POCO_ENABLE_CPP11 + typedef Struct, OrderedSet > OrderedStruct; + OrderedStruct aStruct; + aStruct[0] = "POCO"; + aStruct[1] = "Junior"; + aStruct[2] = 100; + aStruct[3] = 10; + + Var a1(aStruct); + assertTrue(a1[0] == "POCO"); + assertTrue(a1[1] == "Junior"); + assertTrue(a1[2] == 100); + assertTrue(a1[3] == 10); + a1[0] = "Senior"; + assertTrue(a1[0] == "Senior"); + + OrderedStruct s1; + s1[1] = "1"; + s1[2] = "2"; + s1[3] = "3"; + + OrderedStruct s2(s1); + assertTrue(s2[1] == "1"); + assertTrue(s2[2] == "2"); + assertTrue(s2[3] == "3"); + + OrderedMap m1; + m1[1] = "2"; + m1[2] = "1"; + m1[3] = "3"; + + OrderedStruct m2(m1); + assertTrue(m2[1] == "2"); + assertTrue(m2[2] == "1"); + assertTrue(m2[3] == "3"); +#endif // POCO_ENABLE_CPP11 +} + + void VarTest::testDynamicPair() { Pair aPair; @@ -2414,6 +2521,22 @@ void VarTest::testStructToString() } +void VarTest::testOrderedStructToString() +{ +#ifdef POCO_ENABLE_CPP11 + OrderedDynamicStruct aStruct; + aStruct["First Name"] = "Junior"; + aStruct["Last Name"] = "POCO"; + aStruct["Age"] = 1; + Var a1(aStruct); + std::string res = a1.convert(); + std::string expected = "{ \"First Name\" : \"Junior\", \"Last Name\" : \"POCO\", \"Age\" : 1 }"; + assertTrue(res == expected); + assertTrue(aStruct.toString() == res); +#endif // POCO_ENABLE_CPP11 +} + + void VarTest::testStructToStringEscape() { DynamicStruct aStruct; @@ -2959,11 +3082,15 @@ CppUnit::Test* VarTest::suite() CppUnit_addTest(pSuite, VarTest, testArrayIdxOperator); CppUnit_addTest(pSuite, VarTest, testDynamicPair); CppUnit_addTest(pSuite, VarTest, testDynamicStructBasics); + CppUnit_addTest(pSuite, VarTest, testOrderedDynamicStructBasics); CppUnit_addTest(pSuite, VarTest, testDynamicStructString); + CppUnit_addTest(pSuite, VarTest, testOrderedDynamicStructString); CppUnit_addTest(pSuite, VarTest, testDynamicStructInt); + CppUnit_addTest(pSuite, VarTest, testOrderedDynamicStructInt); CppUnit_addTest(pSuite, VarTest, testArrayToString); CppUnit_addTest(pSuite, VarTest, testArrayToStringEscape); CppUnit_addTest(pSuite, VarTest, testStructToString); + CppUnit_addTest(pSuite, VarTest, testOrderedStructToString); CppUnit_addTest(pSuite, VarTest, testStructToStringEscape); CppUnit_addTest(pSuite, VarTest, testArrayOfStructsToString); CppUnit_addTest(pSuite, VarTest, testStructWithArraysToString); diff --git a/Foundation/testsuite/src/VarTest.h b/Foundation/testsuite/src/VarTest.h index 3351f0929..5be921bd5 100644 --- a/Foundation/testsuite/src/VarTest.h +++ b/Foundation/testsuite/src/VarTest.h @@ -55,11 +55,15 @@ public: void testArrayIdxOperator(); void testDynamicPair(); void testDynamicStructBasics(); + void testOrderedDynamicStructBasics(); void testDynamicStructString(); + void testOrderedDynamicStructString(); void testDynamicStructInt(); + void testOrderedDynamicStructInt(); void testArrayToString(); void testArrayToStringEscape(); void testStructToString(); + void testOrderedStructToString(); void testStructToStringEscape(); void testArrayOfStructsToString(); void testStructWithArraysToString(); diff --git a/JSON/include/Poco/JSON/Object.h b/JSON/include/Poco/JSON/Object.h index 3b259b5f3..925d45d56 100644 --- a/JSON/include/Poco/JSON/Object.h +++ b/JSON/include/Poco/JSON/Object.h @@ -224,6 +224,16 @@ public: static Poco::DynamicStruct makeStruct(const Object::Ptr& obj); /// Utility function for creation of struct. +#ifdef POCO_ENABLE_CPP11 + + static Poco::OrderedDynamicStruct makeOrderedStruct(const Object::Ptr& obj); + /// Utility function for creation of ordered struct. + + operator const Poco::OrderedDynamicStruct& () const; + /// Cast operator to Poco::OrderedDynamiStruct. + +#endif // POCO_ENABLE_CPP11 + operator const Poco::DynamicStruct& () const; /// Cast operator to Poco::DynamiStruct. @@ -235,10 +245,21 @@ public: private: typedef std::deque KeyList; typedef Poco::DynamicStruct::Ptr StructPtr; +#ifdef POCO_ENABLE_CPP11 + typedef Poco::OrderedDynamicStruct::Ptr OrdStructPtr; +#endif // POCO_ENABLE_CPP11 - void resetDynStruct() const; void syncKeys(const KeyList& keys); + template + void resetDynStruct(T& pStruct) const + { + if (!pStruct) + pStruct = new typename T::Type; + else + pStruct->clear(); + } + template void doStringify(const C& container, std::ostream& out, unsigned int indent, unsigned int step) const { @@ -272,6 +293,59 @@ private: out << '}'; } + template + static S makeStructImpl(const Object::Ptr& obj) + { + S ds; + + if (obj->_preserveInsOrder) + { + KeyList::const_iterator it = obj->_keys.begin(); + KeyList::const_iterator end = obj->_keys.end(); + for (; it != end; ++it) + { + if (obj->isObject((*it)->first)) + { + Object::Ptr pObj = obj->getObject((*it)->first); + S str = makeStructImpl(pObj); + ds.insert((*it)->first, str); + } + else if (obj->isArray((*it)->first)) + { + Array::Ptr pArr = obj->getArray((*it)->first); + std::vector v = Poco::JSON::Array::makeArray(pArr); + ds.insert((*it)->first, v); + } + else + ds.insert((*it)->first, (*it)->second); + } + } + else + { + ConstIterator it = obj->begin(); + ConstIterator end = obj->end(); + for (; it != end; ++it) + { + if (obj->isObject(it)) + { + Object::Ptr pObj = obj->getObject(it->first); + S str = makeStructImpl(pObj); + ds.insert(it->first, str); + } + else if (obj->isArray(it)) + { + Array::Ptr pArr = obj->getArray(it->first); + std::vector v = Poco::JSON::Array::makeArray(pArr); + ds.insert(it->first, v); + } + else + ds.insert(it->first, it->second); + } + } + + return ds; + } + const std::string& getKey(ValueMap::const_iterator& it) const; const Dynamic::Var& getValue(ValueMap::const_iterator& it) const; const std::string& getKey(KeyList::const_iterator& it) const; @@ -285,8 +359,11 @@ private: // because Object can be returned stringified from Dynamic::Var::toString(), // so it must know whether to escape unicode or not. bool _escapeUnicode; - mutable StructPtr _pStruct; - mutable bool _modified; + mutable StructPtr _pStruct; +#ifdef POCO_ENABLE_CPP11 + mutable OrdStructPtr _pOrdStruct; +#endif // POCO_ENABLE_CPP11 + mutable bool _modified; }; diff --git a/JSON/src/Object.cpp b/JSON/src/Object.cpp index a2eeef90e..48afba83c 100644 --- a/JSON/src/Object.cpp +++ b/JSON/src/Object.cpp @@ -220,52 +220,51 @@ Object& Object::set(const std::string& key, const Dynamic::Var& value) Poco::DynamicStruct Object::makeStruct(const Object::Ptr& obj) { - Poco::DynamicStruct ds; - - ConstIterator it = obj->begin(); - ConstIterator end = obj->end(); - for (; it != end; ++it) - { - if (obj->isObject(it)) - { - Object::Ptr pObj = obj->getObject(it->first); - DynamicStruct str = makeStruct(pObj); - ds.insert(it->first, str); - } - else if (obj->isArray(it)) - { - Array::Ptr pArr = obj->getArray(it->first); - std::vector v = Poco::JSON::Array::makeArray(pArr); - ds.insert(it->first, v); - } - else - ds.insert(it->first, it->second); - } - - return ds; + return makeStructImpl(obj); } +#ifdef POCO_ENABLE_CPP11 + + +Poco::OrderedDynamicStruct Object::makeOrderedStruct(const Object::Ptr& obj) +{ + return makeStructImpl(obj); +} + +/* +void Object::resetOrdDynStruct() const +{ + if (!_pOrdStruct) + _pOrdStruct = new Poco::OrderedDynamicStruct; + else + _pOrdStruct->clear(); +} +*/ + +#endif // POCO_ENABLE_CPP11 + +/* void Object::resetDynStruct() const { if (!_pStruct) _pStruct = new Poco::DynamicStruct; else _pStruct->clear(); -} +}*/ Object::operator const Poco::DynamicStruct& () const { if (!_values.size()) { - resetDynStruct(); + resetDynStruct(_pStruct); } else if (_modified) { ValueMap::const_iterator it = _values.begin(); ValueMap::const_iterator end = _values.end(); - resetDynStruct(); + resetDynStruct(_pStruct); for (; it != end; ++it) { if (isObject(it)) @@ -287,6 +286,68 @@ Object::operator const Poco::DynamicStruct& () const } +#ifdef POCO_ENABLE_CPP11 + + +Object::operator const Poco::OrderedDynamicStruct& () const +{ + if (!_values.size()) + { + resetDynStruct(_pOrdStruct); + } + else if (_modified) + { + if (_preserveInsOrder) + { + KeyList::const_iterator it = _keys.begin(); + KeyList::const_iterator end = _keys.end(); + resetDynStruct(_pOrdStruct); + for (; it != end; ++it) + { + if (isObject((*it)->first)) + { + _pOrdStruct->insert((*it)->first, makeOrderedStruct(getObject((*it)->first))); + } + else if (isArray((*it)->first)) + { + _pOrdStruct->insert((*it)->first, Poco::JSON::Array::makeArray(getArray((*it)->first))); + } + else + { + _pOrdStruct->insert((*it)->first, (*it)->second); + } + } + } + else + { + ValueMap::const_iterator it = _values.begin(); + ValueMap::const_iterator end = _values.end(); + resetDynStruct(_pOrdStruct); + for (; it != end; ++it) + { + if (isObject(it)) + { + _pOrdStruct->insert(it->first, makeOrderedStruct(getObject(it->first))); + } + else if (isArray(it)) + { + _pOrdStruct->insert(it->first, Poco::JSON::Array::makeArray(getArray(it->first))); + } + else + { + _pOrdStruct->insert(it->first, it->second); + } + } + } + } + + return *_pOrdStruct; +} + + +#endif // POCO_ENABLE_CPP11 + + void Object::clear() { _values.clear(); diff --git a/JSON/testsuite/src/JSONTest.cpp b/JSON/testsuite/src/JSONTest.cpp index 82c6e4491..b8ab39224 100644 --- a/JSON/testsuite/src/JSONTest.cpp +++ b/JSON/testsuite/src/JSONTest.cpp @@ -1587,7 +1587,6 @@ void JSONTest::testStringifyPreserveOrder() std::ostringstream ostr; Stringifier::condense(result, ostr); - std::cout << ostr.str() << std::endl; assertTrue (ostr.str() == "{\"Simpsons\":{\"husband\":{\"name\":\"Homer\",\"age\":38},\"wife\":{\"name\":\"Marge\",\"age\":36}," "\"children\":[\"Bart\",\"Lisa\",\"Maggie\"]," "\"address\":{\"number\":742,\"street\":\"Evergreen Terrace\",\"town\":\"Springfield\"}}}"); @@ -1674,7 +1673,12 @@ void JSONTest::testStringifyPreserveOrder() "}"); Poco::DynamicStruct ds = *result.extract(); + assertTrue(ds.toString() == "{ \"Simpsons\" : { \"address\" : { \"number\" : 742, \"street\" : \"Evergreen Terrace\", \"town\" : \"Springfield\" }, " + "\"children\" : [ \"Bart\", \"Lisa\", \"Maggie\" ], " + "\"husband\" : { \"age\" : 38, \"name\" : \"Homer\" }, " + "\"wife\" : { \"age\" : 36, \"name\" : \"Marge\" } } }"); assertTrue (ds["Simpsons"].isStruct()); + assertFalse(ds["Simpsons"].isOrdered()); assertTrue (ds["Simpsons"]["husband"].isStruct()); assertTrue (ds["Simpsons"]["husband"]["name"] == "Homer"); assertTrue (ds["Simpsons"]["husband"]["age"] == 38); @@ -1692,6 +1696,17 @@ void JSONTest::testStringifyPreserveOrder() assertTrue (ds["Simpsons"]["address"]["number"] == 742); assertTrue (ds["Simpsons"]["address"]["street"] == "Evergreen Terrace"); assertTrue (ds["Simpsons"]["address"]["town"] == "Springfield"); + +#ifdef POCO_ENABLE_CPP11 + Poco::OrderedDynamicStruct ods = *result.extract(); + assertTrue(ods["Simpsons"].isStruct()); + assertTrue(ods["Simpsons"].isOrdered()); + assertTrue(ods.toString() == "{ \"Simpsons\" : { \"husband\" : { \"name\" : \"Homer\", \"age\" : 38 }, " + "\"wife\" : { \"name\" : \"Marge\", \"age\" : 36 }, " + "\"children\" : [ \"Bart\", \"Lisa\", \"Maggie\" ], " + "\"address\" : { \"number\" : 742, \"street\" : \"Evergreen Terrace\", " + "\"town\" : \"Springfield\" } } }"); +#endif // POCO_ENABLE_CPP11 }