// Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License.
staticconst SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token
//! Error code of parsing. /*! \ingroup RAPIDJSON_ERRORS \seeGenericPointer::GenericPointer,GenericPointer::GetParseErrorCode
*/ enum PointerParseErrorCode {
kPointerParseErrorNone = 0, //!< The parse is successful
kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/'
kPointerParseErrorInvalidEscape, //!< Invalid escape
kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment
kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment
};
//! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator. /*! ThisclassimplementsRFC6901"JavaScriptObjectNotation(JSON)Pointer" (https://tools.ietf.org/html/rfc6901).
GenericPointerdependsonGenericDocumentandGenericValue. \tparamValueTypeThevaluetypeoftheDOMtree.E.g.GenericValue<UTF8<>> \tparamAllocatorTheallocatortypeforallocatingmemoryforinternalrepresentation. \noteGenericPointerusessameencodingofValueType. However,AllocatorofGenericPointerisindependentofAllocatorofValue.
*/ template <typename ValueType, typename Allocator = CrtAllocator> class GenericPointer { public: typedeftypename ValueType::EncodingType EncodingType; //!< Encoding type from Value typedeftypename EncodingType::Ch Ch; //!< Character type from Value
//! A token is the basic units of internal representation. /*! AJSONpointerstringrepresentation"/foo/123"isparsedtotwotokens: "foo"and123.123willberepresentedinbothnumericformandstringform. Theyareresolvedaccordingtotheactualvaluetype(objectorarray).
ThisstructispublicsothatusercancreateaPointerwithoutparsingand allocation,usingaspecialconstructor.
*/ struct Token { const Ch* name; //!< Name of the token. It has null character at the end but it can contain null character.
SizeType length; //!< Length of the name.
SizeType index; //!< A valid array index, if it is not equal to kPointerInvalidIndex.
};
//! Destructor.
~GenericPointer() { if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated.
Allocator::Free(tokens_);
RAPIDJSON_DELETE(ownAllocator_);
}
//! Assignment operator.
GenericPointer& operator=(const GenericPointer& rhs) { if (this != &rhs) { // Do not delete ownAllcator if (nameBuffer_)
Allocator::Free(tokens_);
//! Stringify the pointer into URI fragment representation. /*! \tparamOutputStreamTypeofoutputstream. \paramosTheoutputstream.
*/ template<typename OutputStream> bool StringifyUriFragment(OutputStream& os) const { return Stringify<true, OutputStream>(os);
}
//@}
//!@name Create value //@{
//! Create a value in a subtree. /*! Ifthevalueisnotexist,itcreatesallparentvaluesandaJSONNullvalue. Soitalwayssucceedandreturnthenewlycreatedorexistingvalue.
\paramrootRootvalueofaDOMsubtreetoberesolved.Itcanbeanyvalueotherthandocumentroot. \paramallocatorAllocatorforcreatingthevaluesifthespecifiedvalueoritsparentsarenotexist. \paramalreadyExistIfnon-null,itstoreswhethertheresolvedvalueisalreadyexist. \returnTheresolvednewlycreated(aJSONNullvalue),oralreadyexistsvalue.
*/
ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const {
RAPIDJSON_ASSERT(IsValid());
ValueType* v = &root; bool exist = true; for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { if (v->IsArray() && t->name[0] == '-' && t->length == 1) {
v->PushBack(Value().Move(), allocator);
v = &((*v)[v->Size() - 1]);
exist = false;
} else { if (t->index == kPointerInvalidIndex) { // must be object name if (!v->IsObject())
v->SetObject(); // Change to Object
} else { // object name or array index if (!v->IsArray() && !v->IsObject())
v->SetArray(); // Change to Array
}
if (v->IsArray()) { 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]);
} else { typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(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;
}
}
}
if (alreadyExist)
*alreadyExist = exist;
return *v;
}
//! Creates a value in a document. /*! \paramdocumentAdocumenttoberesolved. \paramallocatorAllocatorforcreatingthevaluesifthespecifiedvalueoritsparentsarenotexist. \paramalreadyExistIfnon-null,itstoreswhethertheresolvedvalueisalreadyexist. \returnTheresolvednewlycreated,oralreadyexistsvalue.
*/ template <typename stackAllocator>
ValueType& Create(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, bool* alreadyExist = 0) const { return Create(document, document.GetAllocator(), alreadyExist);
}
//@}
//!@name Query value //@{
//! Query a value in a subtree. /*! \paramrootRootvalueofaDOMsub-treetoberesolved.Itcanbeanyvalueotherthandocumentroot. \returnPointertothevalueifitcanberesolved.Otherwisenull.
*/
ValueType* Get(ValueType& root) const {
RAPIDJSON_ASSERT(IsValid());
ValueType* v = &root; for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { switch (v->GetType()) { case kObjectType:
{ typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length)); if (m == v->MemberEnd()) return0;
v = &m->value;
} break; case kArrayType: if (t->index == kPointerInvalidIndex || t->index >= v->Size()) return0;
v = &((*v)[t->index]); break; default: return0;
}
} return v;
}
//! Query a const value in a const subtree. /*! \paramrootRootvalueofaDOMsub-treetoberesolved.Itcanbeanyvalueotherthandocumentroot. \returnPointertothevalueifitcanberesolved.Otherwisenull.
*/ const ValueType* Get(const ValueType& root) const { return Get(const_cast<ValueType&>(root)); }
//@}
//!@name Query a value with default //@{
//! Query a value in a subtree with default value. /*! SimilartoGet(),butifthespecifiedvaluedonotexists,itcreatesallparentsandclonethedefaultvalue. Sothatthisfunctionalwayssucceed.
//! Query a value in a subtree with default null-terminated string.
ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const { bool alreadyExist;
Value& v = Create(root, allocator, &alreadyExist); return alreadyExist ? v : v.SetString(defaultValue, allocator);
}
#if RAPIDJSON_HAS_STDSTRING //! Query a value in a subtree with default std::basic_string.
ValueType& GetWithDefault(ValueType& root, const std::basic_string<Ch>& defaultValue, typename ValueType::AllocatorType& allocator) const { bool alreadyExist;
Value& v = Create(root, allocator, &alreadyExist); return alreadyExist ? v : v.SetString(defaultValue, allocator);
} #endif
//! Query a value in a subtree with default primitive value. /*! \tparamT\tparamTEither\refType,\cint,\cunsigned,\cint64_t,\cuint64_t,\cbool
*/ template <typename T>
RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&))
GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const { return GetWithDefault(root, ValueType(defaultValue).Move(), allocator);
}
//! Query a value in a document with default value. template <typename stackAllocator>
ValueType& GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const ValueType& defaultValue) const { return GetWithDefault(document, defaultValue, document.GetAllocator());
}
//! Query a value in a document with default null-terminated string. template <typename stackAllocator>
ValueType& GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const Ch* defaultValue) const { return GetWithDefault(document, defaultValue, document.GetAllocator());
}
#if RAPIDJSON_HAS_STDSTRING //! Query a value in a document with default std::basic_string. template <typename stackAllocator>
ValueType& GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const std::basic_string<Ch>& defaultValue) const { return GetWithDefault(document, defaultValue, document.GetAllocator());
} #endif
//! Query a value in a document with default primitive value. /*! \tparamT\tparamTEither\refType,\cint,\cunsigned,\cint64_t,\cuint64_t,\cbool
*/ template <typename T, typename stackAllocator>
RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&))
GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, T defaultValue) const { return GetWithDefault(document, defaultValue, document.GetAllocator());
}
//@}
//!@name Set a value //@{
//! Set a value in a subtree, with move semantics. /*! Itcreatesallparentsiftheyarenotexistortypesaredifferenttothetokens. Sothisfunctionalwayssucceedsbutpotentiallyremoveexistingvalues.
//! Set a value in a subtree, with copy semantics.
ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const { return Create(root, allocator).CopyFrom(value, allocator);
}
//! Set a null-terminated string in a subtree.
ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const { return Create(root, allocator) = ValueType(value, allocator).Move();
}
#if RAPIDJSON_HAS_STDSTRING //! Set a std::basic_string in a subtree.
ValueType& Set(ValueType& root, const std::basic_string<Ch>& value, typenameValueType::AllocatorType& allocator) const { return Create(root, allocator) = ValueType(value, allocator).Move();
} #endif
//! Set a primitive value in a subtree. /*! \tparamT\tparamTEither\refType,\cint,\cunsigned,\cint64_t,\cuint64_t,\cbool
*/ template <typename T>
RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&))
Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const { return Create(root, allocator) = ValueType(value).Move();
}
//! Set a value in a document, with move semantics. template <typename stackAllocator>
ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, ValueType& value) const { return Create(document) = value;
}
//! Set a value in a document, with copy semantics. template <typename stackAllocator>
ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const ValueType& value) const { return Create(document).CopyFrom(value, document.GetAllocator());
}
//! Set a null-terminated string in a document. template <typename stackAllocator>
ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const Ch* value) const { return Create(document) = ValueType(value, document.GetAllocator()).Move();
}
#if RAPIDJSON_HAS_STDSTRING //! Sets a std::basic_string in a document. template <typename stackAllocator>
ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const std::basic_string<Ch>& value) const { return Create(document) = ValueType(value, document.GetAllocator()).Move();
} #endif
//! Set a primitive value in a document. /*! \tparamT\tparamTEither\refType,\cint,\cunsigned,\cint64_t,\cuint64_t,\cbool
*/ template <typename T, typename stackAllocator>
RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&))
Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, T value) const { return Create(document) = value;
}
//@}
//!@name Swap a value //@{
//! Swap a value with a value in a subtree. /*! Itcreatesallparentsiftheyarenotexistortypesaredifferenttothetokens. Sothisfunctionalwayssucceedsbutpotentiallyremoveexistingvalues.
//! Swap a value with a value in a document. template <typename stackAllocator>
ValueType& Swap(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, ValueType& value) const { return Create(document).Swap(value);
}
//@}
//! Erase a value in a subtree. /*! \paramrootRootvalueofaDOMsub-treetoberesolved.Itcanbeanyvalueotherthandocumentroot. \returnWhethertheresolvedvalueisfoundanderased.
\noteErasingwithanemptypointer\cPointer(""),i.e.theroot,alwaysfailandreturnfalse.
*/ bool Erase(ValueType& root) const {
RAPIDJSON_ASSERT(IsValid()); if (tokenCount_ == 0) // Cannot erase the root returnfalse;
ValueType* v = &root; const Token* last = tokens_ + (tokenCount_ - 1); for (const Token *t = tokens_; t != last; ++t) { switch (v->GetType()) { case kObjectType:
{ typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length)); if (m == v->MemberEnd()) returnfalse;
v = &m->value;
} break; case kArrayType: if (t->index == kPointerInvalidIndex || t->index >= v->Size()) returnfalse;
v = &((*v)[t->index]); break; default: returnfalse;
}
}
switch (v->GetType()) { case kObjectType: return v->EraseMember(GenericStringRef<Ch>(last->name, last->length)); case kArrayType: if (last->index == kPointerInvalidIndex || last->index >= v->Size()) returnfalse;
v->Erase(v->Begin() + last->index); returntrue; default: returnfalse;
}
}
private: //! Clone the content from rhs to this. /*! \paramrhsSourcepointer. \paramextraTokenExtratokenstobeallocated. \paramextraNameBufferSizeExtranamebuffersize(innumberofCh)tobeallocated. \returnStartofnon-occupiednamebuffer,forstoringextranames.
*/
Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { if (!allocator_) // allocator is independently owned.
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t)
nameBufferSize += t->length;
// Adjust pointers to name buffer
std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t)
t->name += diff;
return nameBuffer_ + nameBufferSize;
}
//! Check whether a character should be percent-encoded. /*! AccordingtoRFC39862.3UnreservedCharacters. \paramcThecharacter(codeunit)tobetested.
*/ bool NeedPercentEncode(Ch c) const { return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~');
}
//! Parse a JSON String or its URI fragment representation into tokens. /*! \paramsourceEitheraJSONPointerstring,oritsURIfragmentrepresentation.Notneedtobenullterminated. \paramlengthLengthofthesourcestring. \noteSourcecannotbeJSONStringRepresentationofJSONPointer,e.g.In"/\u0000",\u0000willnotbeunescaped.
*/ void Parse(const Ch* source, size_t length) {
RAPIDJSON_ASSERT(source != NULL);
RAPIDJSON_ASSERT(nameBuffer_ == 0);
RAPIDJSON_ASSERT(tokens_ == 0);
// Create own allocator if user did not supply. if (!allocator_)
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
// Count number of '/' as tokenCount
tokenCount_ = 0; for (const Ch* s = source; s != source + length; s++) if (*s == '/')
tokenCount_++;
private: const Ch* src_; //!< Current read position. const Ch* head_; //!< Original head of the string. const Ch* end_; //!< Past-the-end position. bool valid_; //!< Whether the parsing is valid.
};
//! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence. template <typename OutputStream> class PercentEncodeStream { public:
PercentEncodeStream(OutputStream& os) : os_(os) {} void Put(char c) { // UTF-8 must be byte unsignedchar u = static_cast<unsignedchar>(c); staticconstchar hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
os_.Put('%');
os_.Put(hexDigits[u >> 4]);
os_.Put(hexDigits[u & 15]);
} private:
OutputStream& os_;
};
Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_.
Allocator* ownAllocator_; //!< Allocator owned by this Pointer.
Ch* nameBuffer_; //!< A buffer containing all names in tokens.
Token* tokens_; //!< A list of tokens.
size_t tokenCount_; //!< Number of tokens in tokens_.
size_t parseErrorOffset_; //!< Offset in code unit when parsing fail.
PointerParseErrorCode parseErrorCode_; //!< Parsing error code.
};
//! GenericPointer for Value (UTF-8, default allocator). typedef GenericPointer<Value> Pointer;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.