diff --git a/include/rapidjson/pointer.h b/include/rapidjson/pointer.h index 28c94ad..82a4458 100644 --- a/include/rapidjson/pointer.h +++ b/include/rapidjson/pointer.h @@ -31,6 +31,8 @@ public: SizeType index; //!< A valid index if not equal to kInvalidIndex. }; + static const SizeType kInvalidIndex = ~SizeType(0); + GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), @@ -91,22 +93,96 @@ public: } } - ValueType* Get(ValueType& root) const; + ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + bool exist = true; + for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + if (v->GetType() != kObjectType && v->GetType() != kArrayType) + if (t->index == kInvalidIndex) + v->SetObject(); + else + v->SetArray(); + + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + if (m == v->MemberEnd()) { + v->AddMember(Value(t->name, t->length, allocator).Move(), Value().Move(), allocator); + v = &(--v->MemberEnd())->value; // Assumes AddMember() appends at the end + exist = false; + } + else + v = &m->value; + } + break; + case kArrayType: + if (t->index == kInvalidIndex) + v->SetArray(); // Change to Array + if (t->index >= v->Size()) { + v->Reserve(t->index - 1, allocator); + while (t->index >= v->Size()) + v->PushBack(Value().Move(), allocator); + exist = false; + } + v = &((*v)[t->index]); + break; + } + } + + if (alreadyExist) + *alreadyExist = exist; + + return *v; + } + + ValueType* Get(ValueType& root) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + if (m == v->MemberEnd()) + return 0; + v = &m->value; + } + break; + case kArrayType: + if (t->index == kInvalidIndex || t->index >= v->Size()) + return 0; + v = &((*v)[t->index]); + break; + default: + return 0; + } + } + return v; + } + const ValueType* Get(const ValueType& root) const { return Get(const_cast(root)); } - ValueType* Get(ValueType& root, const ValueType& defaultValue) const; - const ValueType* Get(const ValueType& root, const ValueType& defaultValue) const; + ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + Value& v = Create(root, allocator, &alreadyExist); + if (!alreadyExist) + v = Value(defaultValue, allocator); + return v; + } // Move semantics, create parents if non-exist - void Set(ValueType& root, ValueType& value) const; + ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = value; + } // Create parents if non-exist - void Swap(ValueType& root, ValueType& value) const; - - static const size_t kDefaultTokenCapacity = 4; - static const SizeType kInvalidIndex = ~SizeType(0); + ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).Swap(value); + } private: void Parse(const Ch* source, size_t length) { diff --git a/test/unittest/pointertest.cpp b/test/unittest/pointertest.cpp index 28173f9..dbe04c0 100644 --- a/test/unittest/pointertest.cpp +++ b/test/unittest/pointertest.cpp @@ -25,7 +25,7 @@ using namespace rapidjson; -static const char cJson[] = "{\n" +static const char kJson[] = "{\n" " \"foo\":[\"bar\", \"baz\"],\n" " \"\" : 0,\n" " \"a/b\" : 1,\n" @@ -131,7 +131,7 @@ TEST(Pointer, Parse) { EXPECT_EQ(Pointer::kInvalidIndex, p.GetTokens()[0].index); } - { + if (sizeof(SizeType) == 4) { // Invalid index (overflow) Pointer p("/4294967296"); EXPECT_TRUE(p.IsValid()); @@ -165,3 +165,60 @@ TEST(Pointer, Stringify) { EXPECT_STREQ(sources[i], s.GetString()); } } + +TEST(Pointer, Create) { + Document d; + EXPECT_EQ(&d, &Pointer("").Create(d, d.GetAllocator())); + EXPECT_EQ(&d["foo"], &Pointer("/foo").Create(d, d.GetAllocator())); + EXPECT_EQ(&d["foo"][0], &Pointer("/foo/0").Create(d, d.GetAllocator())); +} + +TEST(Pointer, Get) { + Document d; + d.Parse(kJson); + + EXPECT_EQ(&d, Pointer("").Get(d)); + EXPECT_EQ(&d["foo"], Pointer("/foo").Get(d)); + EXPECT_EQ(&d["foo"][0], Pointer("/foo/0").Get(d)); + EXPECT_EQ(&d[""], Pointer("/").Get(d)); + EXPECT_EQ(&d["a/b"], Pointer("/a~1b").Get(d)); + EXPECT_EQ(&d["c%d"], Pointer("/c%d").Get(d)); + EXPECT_EQ(&d["e^f"], Pointer("/e^f").Get(d)); + EXPECT_EQ(&d["g|h"], Pointer("/g|h").Get(d)); + EXPECT_EQ(&d["i\\j"], Pointer("/i\\j").Get(d)); + EXPECT_EQ(&d["k\"l"], Pointer("/k\"l").Get(d)); + EXPECT_EQ(&d[" "], Pointer("/ ").Get(d)); + EXPECT_EQ(&d["m~n"], Pointer("/m~0n").Get(d)); +} + +TEST(Pointer, GetWithDefault) { + Document d; + d.Parse(kJson); + + Document::AllocatorType& a = d.GetAllocator(); + const Value v("qux"); + EXPECT_TRUE(Value("bar") == Pointer("/foo/0").GetWithDefault(d, v, a)); + EXPECT_TRUE(Value("baz") == Pointer("/foo/1").GetWithDefault(d, v, a)); + EXPECT_TRUE(Value("qux") == Pointer("/foo/2").GetWithDefault(d, v, a)); +} + +TEST(Pointer, Set) { + Document d; + d.Parse(kJson); + Document::AllocatorType& a = d.GetAllocator(); + + Pointer("/foo/0").Set(d, Value(123).Move(), a); + EXPECT_EQ(123, d["foo"][0].GetInt()); + + Pointer("/foo/2").Set(d, Value(456).Move(), a); + EXPECT_EQ(456, d["foo"][2].GetInt()); +} + +TEST(Pointer, Swap) { + Document d; + d.Parse(kJson); + Document::AllocatorType& a = d.GetAllocator(); + Pointer("/foo/0").Swap(d, *Pointer("/foo/1").Get(d), a); + EXPECT_STREQ("baz", d["foo"][0].GetString()); + EXPECT_STREQ("bar", d["foo"][1].GetString()); +}