Add optional unresolvedTokenIndex parameter to Pointer::Get() and related

This commit is contained in:
Milo Yip 2016-02-09 12:09:47 +08:00
parent e702d3dc70
commit cec8dcbc7a
2 changed files with 60 additions and 19 deletions

View File

@ -460,9 +460,18 @@ public:
//! Query a value in a subtree. //! Query a value in a subtree.
/*! /*!
\param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
\param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token.
\return Pointer to the value if it can be resolved. Otherwise null. \return Pointer to the value if it can be resolved. Otherwise null.
\note
There are only 3 situations when a value cannot be resolved:
1. A value in the path is not an array nor object.
2. An object value does not contain the token.
3. A token is out of range of an array value.
Use unresolvedTokenIndex to retrieve the token index.
*/ */
ValueType* Get(ValueType& root) const { ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const {
RAPIDJSON_ASSERT(IsValid()); RAPIDJSON_ASSERT(IsValid());
ValueType* v = &root; ValueType* v = &root;
for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
@ -471,18 +480,23 @@ public:
{ {
typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length)); typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
if (m == v->MemberEnd()) if (m == v->MemberEnd())
return 0; break;
v = &m->value; v = &m->value;
} }
break; continue;
case kArrayType: case kArrayType:
if (t->index == kPointerInvalidIndex || t->index >= v->Size()) if (t->index == kPointerInvalidIndex || t->index >= v->Size())
return 0; break;
v = &((*v)[t->index]); v = &((*v)[t->index]);
break; continue;
default: default:
return 0; break;
} }
// Error: unresolved token
if (unresolvedTokenIndex)
*unresolvedTokenIndex = static_cast<size_t>(t - tokens_);
return 0;
} }
return v; return v;
} }
@ -492,7 +506,9 @@ public:
\param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
\return Pointer to the value if it can be resolved. Otherwise null. \return Pointer to the value if it can be resolved. Otherwise null.
*/ */
const ValueType* Get(const ValueType& root) const { return Get(const_cast<ValueType&>(root)); } const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const {
return Get(const_cast<ValueType&>(root), unresolvedTokenIndex);
}
//@} //@}
@ -1053,23 +1069,23 @@ typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, c
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
template <typename T> template <typename T>
typename T::ValueType* GetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer) { typename T::ValueType* GetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, size_t* unresolvedTokenIndex = 0) {
return pointer.Get(root); return pointer.Get(root, unresolvedTokenIndex);
} }
template <typename T> template <typename T>
const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer<typename T::ValueType>& pointer) { const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer<typename T::ValueType>& pointer, size_t* unresolvedTokenIndex = 0) {
return pointer.Get(root); return pointer.Get(root, unresolvedTokenIndex);
} }
template <typename T, typename CharType, size_t N> template <typename T, typename CharType, size_t N>
typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N]) { typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) {
return GenericPointer<typename T::ValueType>(source, N - 1).Get(root); return GenericPointer<typename T::ValueType>(source, N - 1).Get(root, unresolvedTokenIndex);
} }
template <typename T, typename CharType, size_t N> template <typename T, typename CharType, size_t N>
const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N]) { const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) {
return GenericPointer<typename T::ValueType>(source, N - 1).Get(root); return GenericPointer<typename T::ValueType>(source, N - 1).Get(root, unresolvedTokenIndex);
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////

View File

@ -627,10 +627,15 @@ TEST(Pointer, Get) {
EXPECT_EQ(&d[" "], Pointer("/ ").Get(d)); EXPECT_EQ(&d[" "], Pointer("/ ").Get(d));
EXPECT_EQ(&d["m~n"], Pointer("/m~0n").Get(d)); EXPECT_EQ(&d["m~n"], Pointer("/m~0n").Get(d));
EXPECT_TRUE(Pointer("/abc").Get(d) == 0); EXPECT_TRUE(Pointer("/abc").Get(d) == 0);
EXPECT_TRUE(Pointer("/foo/2").Get(d) == 0); // Out of boundary size_t unresolvedTokenIndex;
EXPECT_TRUE(Pointer("/foo/a").Get(d) == 0); // "/foo" is an array, cannot query by "a" EXPECT_TRUE(Pointer("/foo/2").Get(d, &unresolvedTokenIndex) == 0); // Out of boundary
EXPECT_TRUE(Pointer("/foo/0/0").Get(d) == 0); // "/foo/0" is an string, cannot further query EXPECT_EQ(1, unresolvedTokenIndex);
EXPECT_TRUE(Pointer("/foo/0/a").Get(d) == 0); // "/foo/0" is an string, cannot further query EXPECT_TRUE(Pointer("/foo/a").Get(d, &unresolvedTokenIndex) == 0); // "/foo" is an array, cannot query by "a"
EXPECT_EQ(1, unresolvedTokenIndex);
EXPECT_TRUE(Pointer("/foo/0/0").Get(d, &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query
EXPECT_EQ(2, unresolvedTokenIndex);
EXPECT_TRUE(Pointer("/foo/0/a").Get(d, &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query
EXPECT_EQ(2, unresolvedTokenIndex);
} }
TEST(Pointer, GetWithDefault) { TEST(Pointer, GetWithDefault) {
@ -947,10 +952,30 @@ TEST(Pointer, GetValueByPointer) {
EXPECT_EQ(&d["foo"][0], GetValueByPointer(d, Pointer("/foo/0"))); EXPECT_EQ(&d["foo"][0], GetValueByPointer(d, Pointer("/foo/0")));
EXPECT_EQ(&d["foo"][0], GetValueByPointer(d, "/foo/0")); EXPECT_EQ(&d["foo"][0], GetValueByPointer(d, "/foo/0"));
size_t unresolvedTokenIndex;
EXPECT_TRUE(GetValueByPointer(d, "/foo/2", &unresolvedTokenIndex) == 0); // Out of boundary
EXPECT_EQ(1, unresolvedTokenIndex);
EXPECT_TRUE(GetValueByPointer(d, "/foo/a", &unresolvedTokenIndex) == 0); // "/foo" is an array, cannot query by "a"
EXPECT_EQ(1, unresolvedTokenIndex);
EXPECT_TRUE(GetValueByPointer(d, "/foo/0/0", &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query
EXPECT_EQ(2, unresolvedTokenIndex);
EXPECT_TRUE(GetValueByPointer(d, "/foo/0/a", &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query
EXPECT_EQ(2, unresolvedTokenIndex);
// const version // const version
const Value& v = d; const Value& v = d;
EXPECT_EQ(&d["foo"][0], GetValueByPointer(v, Pointer("/foo/0"))); EXPECT_EQ(&d["foo"][0], GetValueByPointer(v, Pointer("/foo/0")));
EXPECT_EQ(&d["foo"][0], GetValueByPointer(v, "/foo/0")); EXPECT_EQ(&d["foo"][0], GetValueByPointer(v, "/foo/0"));
EXPECT_TRUE(GetValueByPointer(v, "/foo/2", &unresolvedTokenIndex) == 0); // Out of boundary
EXPECT_EQ(1, unresolvedTokenIndex);
EXPECT_TRUE(GetValueByPointer(v, "/foo/a", &unresolvedTokenIndex) == 0); // "/foo" is an array, cannot query by "a"
EXPECT_EQ(1, unresolvedTokenIndex);
EXPECT_TRUE(GetValueByPointer(v, "/foo/0/0", &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query
EXPECT_EQ(2, unresolvedTokenIndex);
EXPECT_TRUE(GetValueByPointer(v, "/foo/0/a", &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query
EXPECT_EQ(2, unresolvedTokenIndex);
} }
TEST(Pointer, GetValueByPointerWithDefault_Pointer) { TEST(Pointer, GetValueByPointerWithDefault_Pointer) {