Files
CMake/Utilities/cmjsoncpp/src/lib_json/json_value.cpp
Oleksandr Koval 209daa20b2 Code style: add missed explicit 'this->'
CMake uses explicit 'this->' style. Using custom clang-tidy check we can
detect and fix places where 'this->' was missed.
2021-01-05 14:32:36 +02:00

1914 lines
52 KiB
C++

// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at
// http://jsoncpp.sourceforge.net/LICENSE
#if !defined(JSON_IS_AMALGAMATION)
# include <json/assertions.h>
# include <json/value.h>
# include <json/writer.h>
#endif // if !defined(JSON_IS_AMALGAMATION)
#include <sstream>
#include <utility>
#include <assert.h>
#include <math.h>
#include <string.h>
#ifdef JSON_USE_CPPTL
# include <cpptl/conststring.h>
#endif
#include <algorithm> // min()
#include <cstddef> // size_t
#define JSON_ASSERT_UNREACHABLE assert(false)
namespace Json {
// This is a walkaround to avoid the static initialization of Value::null.
// kNull must be word-aligned to avoid crashing on ARM. We use an alignment of
// 8 (instead of 4) as a bit of future-proofing.
#if defined(__ARMEL__)
# define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
#else
# define ALIGNAS(byte_alignment)
#endif
// static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 };
// const unsigned char& kNullRef = kNull[0];
// const Value& Value::null = reinterpret_cast<const Value&>(kNullRef);
// const Value& Value::nullRef = null;
// static
Value const& Value::nullSingleton()
{
static Value const nullStatic;
return nullStatic;
}
// for backwards compatibility, we'll leave these global references around, but
// DO NOT use them in JSONCPP library code any more!
Value const& Value::null = Value::nullSingleton();
Value const& Value::nullRef = Value::nullSingleton();
const Int Value::minInt = Int(~(UInt(-1) / 2));
const Int Value::maxInt = Int(UInt(-1) / 2);
const UInt Value::maxUInt = UInt(-1);
#if defined(JSON_HAS_INT64)
const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2));
const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2);
const UInt64 Value::maxUInt64 = UInt64(-1);
// The constant is hard-coded because some compiler have trouble
// converting Value::maxUInt64 to a double correctly (AIX/xlC).
// Assumes that UInt64 is a 64 bits integer.
static const double maxUInt64AsDouble = 18446744073709551615.0;
#endif // defined(JSON_HAS_INT64)
const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2));
const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2);
const LargestUInt Value::maxLargestUInt = LargestUInt(-1);
#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
template <typename T, typename U>
static inline bool InRange(double d, T min, U max)
{
// The casts can lose precision, but we are looking only for
// an approximate range. Might fail on edge cases though. ~cdunn
// return d >= static_cast<double>(min) && d <= static_cast<double>(max);
return d >= min && d <= max;
}
#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
static inline double integerToDouble(Json::UInt64 value)
{
return static_cast<double>(Int64(value / 2)) * 2.0 +
static_cast<double>(Int64(value & 1));
}
template <typename T>
static inline double integerToDouble(T value)
{
return static_cast<double>(value);
}
template <typename T, typename U>
static inline bool InRange(double d, T min, U max)
{
return d >= integerToDouble(min) && d <= integerToDouble(max);
}
#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
/** Duplicates the specified string value.
* @param value Pointer to the string to duplicate. Must be zero-terminated if
* length is "unknown".
* @param length Length of the value. if equals to unknown, then it will be
* computed using strlen(value).
* @return Pointer on the duplicate instance of string.
*/
static inline char* duplicateStringValue(const char* value, size_t length)
{
// Avoid an integer overflow in the call to malloc below by limiting length
// to a sane value.
if (length >= static_cast<size_t>(Value::maxInt))
length = Value::maxInt - 1;
char* newString = static_cast<char*>(malloc(length + 1));
if (newString == NULL) {
throwRuntimeError("in Json::Value::duplicateStringValue(): "
"Failed to allocate string value buffer");
}
memcpy(newString, value, length);
newString[length] = 0;
return newString;
}
/* Record the length as a prefix.
*/
static inline char* duplicateAndPrefixStringValue(const char* value,
unsigned int length)
{
// Avoid an integer overflow in the call to malloc below by limiting length
// to a sane value.
JSON_ASSERT_MESSAGE(length <= static_cast<unsigned>(Value::maxInt) -
sizeof(unsigned) - 1U,
"in Json::Value::duplicateAndPrefixStringValue(): "
"length too big for prefixing");
unsigned actualLength =
length + static_cast<unsigned>(sizeof(unsigned)) + 1U;
char* newString = static_cast<char*>(malloc(actualLength));
if (newString == 0) {
throwRuntimeError("in Json::Value::duplicateAndPrefixStringValue(): "
"Failed to allocate string value buffer");
}
*reinterpret_cast<unsigned*>(newString) = length;
memcpy(newString + sizeof(unsigned), value, length);
newString[actualLength - 1U] =
0; // to avoid buffer over-run accidents by users later
return newString;
}
inline static void decodePrefixedString(bool isPrefixed, char const* prefixed,
unsigned* length, char const** value)
{
if (!isPrefixed) {
*length = static_cast<unsigned>(strlen(prefixed));
*value = prefixed;
} else {
*length = *reinterpret_cast<unsigned const*>(prefixed);
*value = prefixed + sizeof(unsigned);
}
}
/** Free the string duplicated by
* duplicateStringValue()/duplicateAndPrefixStringValue().
*/
#if JSONCPP_USING_SECURE_MEMORY
static inline void releasePrefixedStringValue(char* value)
{
unsigned length = 0;
char const* valueDecoded;
decodePrefixedString(true, value, &length, &valueDecoded);
size_t const size = sizeof(unsigned) + length + 1U;
memset(value, 0, size);
free(value);
}
static inline void releaseStringValue(char* value, unsigned length)
{
// length==0 => we allocated the strings memory
size_t size = (length == 0) ? strlen(value) : length;
memset(value, 0, size);
free(value);
}
#else // !JSONCPP_USING_SECURE_MEMORY
static inline void releasePrefixedStringValue(char* value)
{
free(value);
}
static inline void releaseStringValue(char* value, unsigned)
{
free(value);
}
#endif // JSONCPP_USING_SECURE_MEMORY
} // namespace Json
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// ValueInternals...
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
#if !defined(JSON_IS_AMALGAMATION)
# include "json_valueiterator.inl"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
Exception::Exception(JSONCPP_STRING const& msg)
: msg_(msg)
{
}
Exception::~Exception() JSONCPP_NOEXCEPT
{
}
char const* Exception::what() const JSONCPP_NOEXCEPT
{
return this->msg_.c_str();
}
RuntimeError::RuntimeError(JSONCPP_STRING const& msg)
: Exception(msg)
{
}
LogicError::LogicError(JSONCPP_STRING const& msg)
: Exception(msg)
{
}
JSONCPP_NORETURN void throwRuntimeError(JSONCPP_STRING const& msg)
{
throw RuntimeError(msg);
}
JSONCPP_NORETURN void throwLogicError(JSONCPP_STRING const& msg)
{
throw LogicError(msg);
}
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class Value::CommentInfo
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
Value::CommentInfo::CommentInfo()
: comment_(0)
{
}
Value::CommentInfo::~CommentInfo()
{
if (this->comment_)
releaseStringValue(this->comment_, 0u);
}
void Value::CommentInfo::setComment(const char* text, size_t len)
{
if (this->comment_) {
releaseStringValue(this->comment_, 0u);
this->comment_ = 0;
}
JSON_ASSERT(text != 0);
JSON_ASSERT_MESSAGE(
text[0] == '\0' || text[0] == '/',
"in Json::Value::setComment(): Comments must start with /");
// It seems that /**/ style comments are acceptable as well.
this->comment_ = duplicateStringValue(text, len);
}
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class Value::CZString
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// Notes: policy_ indicates if the string was allocated when
// a string is stored.
Value::CZString::CZString(ArrayIndex aindex)
: cstr_(0)
, index_(aindex)
{
}
Value::CZString::CZString(char const* str, unsigned ulength,
DuplicationPolicy allocate)
: cstr_(str)
{
// allocate != duplicate
this->storage_.policy_ = allocate & 0x3;
this->storage_.length_ = ulength & 0x3FFFFFFF;
}
Value::CZString::CZString(const CZString& other)
{
this->cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != 0
? duplicateStringValue(other.cstr_, other.storage_.length_)
: other.cstr_);
this->storage_.policy_ =
static_cast<unsigned>(
other.cstr_ ? (static_cast<DuplicationPolicy>(other.storage_.policy_) ==
noDuplication
? noDuplication
: duplicate)
: static_cast<DuplicationPolicy>(other.storage_.policy_)) &
3U;
this->storage_.length_ = other.storage_.length_;
}
#if JSON_HAS_RVALUE_REFERENCES
Value::CZString::CZString(CZString&& other)
: cstr_(other.cstr_)
, index_(other.index_)
{
other.cstr_ = nullptr;
}
#endif
Value::CZString::~CZString()
{
if (this->cstr_ && this->storage_.policy_ == duplicate) {
releaseStringValue(const_cast<char*>(this->cstr_),
this->storage_.length_ +
1u); //+1 for null terminating character for sake of
//completeness but not actually necessary
}
}
void Value::CZString::swap(CZString& other)
{
std::swap(this->cstr_, other.cstr_);
std::swap(this->index_, other.index_);
}
Value::CZString& Value::CZString::operator=(const CZString& other)
{
this->cstr_ = other.cstr_;
this->index_ = other.index_;
return *this;
}
#if JSON_HAS_RVALUE_REFERENCES
Value::CZString& Value::CZString::operator=(CZString&& other)
{
this->cstr_ = other.cstr_;
this->index_ = other.index_;
other.cstr_ = nullptr;
return *this;
}
#endif
bool Value::CZString::operator<(const CZString& other) const
{
if (!this->cstr_)
return this->index_ < other.index_;
// return strcmp(cstr_, other.cstr_) < 0;
// Assume both are strings.
unsigned this_len = this->storage_.length_;
unsigned other_len = other.storage_.length_;
unsigned min_len = std::min<unsigned>(this_len, other_len);
JSON_ASSERT(this->cstr_ && other.cstr_);
int comp = memcmp(this->cstr_, other.cstr_, min_len);
if (comp < 0)
return true;
if (comp > 0)
return false;
return (this_len < other_len);
}
bool Value::CZString::operator==(const CZString& other) const
{
if (!this->cstr_)
return this->index_ == other.index_;
// return strcmp(cstr_, other.cstr_) == 0;
// Assume both are strings.
unsigned this_len = this->storage_.length_;
unsigned other_len = other.storage_.length_;
if (this_len != other_len)
return false;
JSON_ASSERT(this->cstr_ && other.cstr_);
int comp = memcmp(this->cstr_, other.cstr_, this_len);
return comp == 0;
}
ArrayIndex Value::CZString::index() const
{
return this->index_;
}
// const char* Value::CZString::c_str() const { return cstr_; }
const char* Value::CZString::data() const
{
return this->cstr_;
}
unsigned Value::CZString::length() const
{
return this->storage_.length_;
}
bool Value::CZString::isStaticString() const
{
return this->storage_.policy_ == noDuplication;
}
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class Value::Value
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
/*! internal Default constructor initialization must be equivalent to:
* memset( this, 0, sizeof(Value) )
* This optimization is used in ValueInternalMap fast allocator.
*/
Value::Value(ValueType vtype)
{
static char const emptyString[] = "";
this->initBasic(vtype);
switch (vtype) {
case nullValue:
break;
case intValue:
case uintValue:
this->value_.int_ = 0;
break;
case realValue:
this->value_.real_ = 0.0;
break;
case stringValue:
// allocated_ == false, so this is safe.
this->value_.string_ =
const_cast<char*>(static_cast<char const*>(emptyString));
break;
case arrayValue:
case objectValue:
this->value_.map_ = new ObjectValues();
break;
case booleanValue:
this->value_.bool_ = false;
break;
default:
JSON_ASSERT_UNREACHABLE;
}
}
Value::Value(Int value)
{
this->initBasic(intValue);
this->value_.int_ = value;
}
Value::Value(UInt value)
{
this->initBasic(uintValue);
this->value_.uint_ = value;
}
#if defined(JSON_HAS_INT64)
Value::Value(Int64 value)
{
this->initBasic(intValue);
this->value_.int_ = value;
}
Value::Value(UInt64 value)
{
this->initBasic(uintValue);
this->value_.uint_ = value;
}
#endif // defined(JSON_HAS_INT64)
Value::Value(double value)
{
this->initBasic(realValue);
this->value_.real_ = value;
}
Value::Value(const char* value)
{
this->initBasic(stringValue, true);
JSON_ASSERT_MESSAGE(value != NULL, "Null Value Passed to Value Constructor");
this->value_.string_ =
duplicateAndPrefixStringValue(value, static_cast<unsigned>(strlen(value)));
}
Value::Value(const char* beginValue, const char* endValue)
{
this->initBasic(stringValue, true);
this->value_.string_ = duplicateAndPrefixStringValue(
beginValue, static_cast<unsigned>(endValue - beginValue));
}
Value::Value(const JSONCPP_STRING& value)
{
this->initBasic(stringValue, true);
this->value_.string_ = duplicateAndPrefixStringValue(
value.data(), static_cast<unsigned>(value.length()));
}
Value::Value(const StaticString& value)
{
this->initBasic(stringValue);
this->value_.string_ = const_cast<char*>(value.c_str());
}
#ifdef JSON_USE_CPPTL
Value::Value(const CppTL::ConstString& value)
{
initBasic(stringValue, true);
value_.string_ = duplicateAndPrefixStringValue(
value, static_cast<unsigned>(value.length()));
}
#endif
Value::Value(bool value)
{
this->initBasic(booleanValue);
this->value_.bool_ = value;
}
Value::Value(Value const& other)
: type_(other.type_)
, allocated_(false)
, comments_(0)
, start_(other.start_)
, limit_(other.limit_)
{
switch (this->type_) {
case nullValue:
case intValue:
case uintValue:
case realValue:
case booleanValue:
this->value_ = other.value_;
break;
case stringValue:
if (other.value_.string_ && other.allocated_) {
unsigned len;
char const* str;
decodePrefixedString(other.allocated_, other.value_.string_, &len,
&str);
this->value_.string_ = duplicateAndPrefixStringValue(str, len);
this->allocated_ = true;
} else {
this->value_.string_ = other.value_.string_;
this->allocated_ = false;
}
break;
case arrayValue:
case objectValue:
this->value_.map_ = new ObjectValues(*other.value_.map_);
break;
default:
JSON_ASSERT_UNREACHABLE;
}
if (other.comments_) {
this->comments_ = new CommentInfo[numberOfCommentPlacement];
for (int comment = 0; comment < numberOfCommentPlacement; ++comment) {
const CommentInfo& otherComment = other.comments_[comment];
if (otherComment.comment_)
this->comments_[comment].setComment(otherComment.comment_,
strlen(otherComment.comment_));
}
}
}
#if JSON_HAS_RVALUE_REFERENCES
// Move constructor
Value::Value(Value&& other)
{
this->initBasic(nullValue);
this->swap(other);
}
#endif
Value::~Value()
{
switch (this->type_) {
case nullValue:
case intValue:
case uintValue:
case realValue:
case booleanValue:
break;
case stringValue:
if (this->allocated_)
releasePrefixedStringValue(this->value_.string_);
break;
case arrayValue:
case objectValue:
delete this->value_.map_;
break;
default:
JSON_ASSERT_UNREACHABLE;
}
delete[] this->comments_;
this->value_.uint_ = 0;
}
Value& Value::operator=(Value other)
{
this->swap(other);
return *this;
}
void Value::swapPayload(Value& other)
{
ValueType temp = this->type_;
this->type_ = other.type_;
other.type_ = temp;
std::swap(this->value_, other.value_);
int temp2 = this->allocated_;
this->allocated_ = other.allocated_;
other.allocated_ = temp2 & 0x1;
}
void Value::copyPayload(const Value& other)
{
this->type_ = other.type_;
this->value_ = other.value_;
this->allocated_ = other.allocated_;
}
void Value::swap(Value& other)
{
this->swapPayload(other);
std::swap(this->comments_, other.comments_);
std::swap(this->start_, other.start_);
std::swap(this->limit_, other.limit_);
}
void Value::copy(const Value& other)
{
this->copyPayload(other);
this->comments_ = other.comments_;
this->start_ = other.start_;
this->limit_ = other.limit_;
}
ValueType Value::type() const
{
return this->type_;
}
int Value::compare(const Value& other) const
{
if (*this < other)
return -1;
if (*this > other)
return 1;
return 0;
}
bool Value::operator<(const Value& other) const
{
int typeDelta = this->type_ - other.type_;
if (typeDelta)
return typeDelta < 0 ? true : false;
switch (this->type_) {
case nullValue:
return false;
case intValue:
return this->value_.int_ < other.value_.int_;
case uintValue:
return this->value_.uint_ < other.value_.uint_;
case realValue:
return this->value_.real_ < other.value_.real_;
case booleanValue:
return this->value_.bool_ < other.value_.bool_;
case stringValue: {
if ((this->value_.string_ == 0) || (other.value_.string_ == 0)) {
if (other.value_.string_)
return true;
else
return false;
}
unsigned this_len;
unsigned other_len;
char const* this_str;
char const* other_str;
decodePrefixedString(this->allocated_, this->value_.string_, &this_len,
&this_str);
decodePrefixedString(other.allocated_, other.value_.string_, &other_len,
&other_str);
unsigned min_len = std::min<unsigned>(this_len, other_len);
JSON_ASSERT(this_str && other_str);
int comp = memcmp(this_str, other_str, min_len);
if (comp < 0)
return true;
if (comp > 0)
return false;
return (this_len < other_len);
}
case arrayValue:
case objectValue: {
int delta = int(this->value_.map_->size() - other.value_.map_->size());
if (delta)
return delta < 0;
return (*this->value_.map_) < (*other.value_.map_);
}
default:
JSON_ASSERT_UNREACHABLE;
}
return false; // unreachable
}
bool Value::operator<=(const Value& other) const
{
return !(other < *this);
}
bool Value::operator>=(const Value& other) const
{
return !(*this < other);
}
bool Value::operator>(const Value& other) const
{
return other < *this;
}
bool Value::operator==(const Value& other) const
{
// if ( type_ != other.type_ )
// GCC 2.95.3 says:
// attempt to take address of bit-field structure member `Json::Value::type_'
// Beats me, but a temp solves the problem.
int temp = other.type_;
if (this->type_ != temp)
return false;
switch (this->type_) {
case nullValue:
return true;
case intValue:
return this->value_.int_ == other.value_.int_;
case uintValue:
return this->value_.uint_ == other.value_.uint_;
case realValue:
return this->value_.real_ == other.value_.real_;
case booleanValue:
return this->value_.bool_ == other.value_.bool_;
case stringValue: {
if ((this->value_.string_ == 0) || (other.value_.string_ == 0)) {
return (this->value_.string_ == other.value_.string_);
}
unsigned this_len;
unsigned other_len;
char const* this_str;
char const* other_str;
decodePrefixedString(this->allocated_, this->value_.string_, &this_len,
&this_str);
decodePrefixedString(other.allocated_, other.value_.string_, &other_len,
&other_str);
if (this_len != other_len)
return false;
JSON_ASSERT(this_str && other_str);
int comp = memcmp(this_str, other_str, this_len);
return comp == 0;
}
case arrayValue:
case objectValue:
return this->value_.map_->size() == other.value_.map_->size() &&
(*this->value_.map_) == (*other.value_.map_);
default:
JSON_ASSERT_UNREACHABLE;
}
return false; // unreachable
}
bool Value::operator!=(const Value& other) const
{
return !(*this == other);
}
const char* Value::asCString() const
{
JSON_ASSERT_MESSAGE(type_ == stringValue,
"in Json::Value::asCString(): requires stringValue");
if (this->value_.string_ == 0)
return 0;
unsigned this_len;
char const* this_str;
decodePrefixedString(this->allocated_, this->value_.string_, &this_len,
&this_str);
return this_str;
}
#if JSONCPP_USING_SECURE_MEMORY
unsigned Value::getCStringLength() const
{
JSON_ASSERT_MESSAGE(type_ == stringValue,
"in Json::Value::asCString(): requires stringValue");
if (value_.string_ == 0)
return 0;
unsigned this_len;
char const* this_str;
decodePrefixedString(this->allocated_, this->value_.string_, &this_len,
&this_str);
return this_len;
}
#endif
bool Value::getString(char const** str, char const** cend) const
{
if (this->type_ != stringValue)
return false;
if (this->value_.string_ == 0)
return false;
unsigned length;
decodePrefixedString(this->allocated_, this->value_.string_, &length, str);
*cend = *str + length;
return true;
}
JSONCPP_STRING Value::asString() const
{
switch (this->type_) {
case nullValue:
return "";
case stringValue: {
if (this->value_.string_ == 0)
return "";
unsigned this_len;
char const* this_str;
decodePrefixedString(this->allocated_, this->value_.string_, &this_len,
&this_str);
return JSONCPP_STRING(this_str, this_len);
}
case booleanValue:
return this->value_.bool_ ? "true" : "false";
case intValue:
return valueToString(this->value_.int_);
case uintValue:
return valueToString(this->value_.uint_);
case realValue:
return valueToString(this->value_.real_);
default:
JSON_FAIL_MESSAGE("Type is not convertible to string");
}
}
#ifdef JSON_USE_CPPTL
CppTL::ConstString Value::asConstString() const
{
unsigned len;
char const* str;
decodePrefixedString(allocated_, value_.string_, &len, &str);
return CppTL::ConstString(str, len);
}
#endif
Value::Int Value::asInt() const
{
switch (this->type_) {
case intValue:
JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range");
return Int(this->value_.int_);
case uintValue:
JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range");
return Int(this->value_.uint_);
case realValue:
JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt),
"double out of Int range");
return Int(this->value_.real_);
case nullValue:
return 0;
case booleanValue:
return this->value_.bool_ ? 1 : 0;
default:
break;
}
JSON_FAIL_MESSAGE("Value is not convertible to Int.");
}
Value::UInt Value::asUInt() const
{
switch (this->type_) {
case intValue:
JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range");
return UInt(this->value_.int_);
case uintValue:
JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range");
return UInt(this->value_.uint_);
case realValue:
JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt),
"double out of UInt range");
return UInt(this->value_.real_);
case nullValue:
return 0;
case booleanValue:
return this->value_.bool_ ? 1 : 0;
default:
break;
}
JSON_FAIL_MESSAGE("Value is not convertible to UInt.");
}
#if defined(JSON_HAS_INT64)
Value::Int64 Value::asInt64() const
{
switch (this->type_) {
case intValue:
return Int64(this->value_.int_);
case uintValue:
JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range");
return Int64(this->value_.uint_);
case realValue:
JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64),
"double out of Int64 range");
return Int64(this->value_.real_);
case nullValue:
return 0;
case booleanValue:
return this->value_.bool_ ? 1 : 0;
default:
break;
}
JSON_FAIL_MESSAGE("Value is not convertible to Int64.");
}
Value::UInt64 Value::asUInt64() const
{
switch (this->type_) {
case intValue:
JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range");
return UInt64(this->value_.int_);
case uintValue:
return UInt64(this->value_.uint_);
case realValue:
JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64),
"double out of UInt64 range");
return UInt64(this->value_.real_);
case nullValue:
return 0;
case booleanValue:
return this->value_.bool_ ? 1 : 0;
default:
break;
}
JSON_FAIL_MESSAGE("Value is not convertible to UInt64.");
}
#endif // if defined(JSON_HAS_INT64)
LargestInt Value::asLargestInt() const
{
#if defined(JSON_NO_INT64)
return asInt();
#else
return this->asInt64();
#endif
}
LargestUInt Value::asLargestUInt() const
{
#if defined(JSON_NO_INT64)
return asUInt();
#else
return this->asUInt64();
#endif
}
double Value::asDouble() const
{
switch (this->type_) {
case intValue:
return static_cast<double>(this->value_.int_);
case uintValue:
#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
return static_cast<double>(this->value_.uint_);
#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
return integerToDouble(value_.uint_);
#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
case realValue:
return this->value_.real_;
case nullValue:
return 0.0;
case booleanValue:
return this->value_.bool_ ? 1.0 : 0.0;
default:
break;
}
JSON_FAIL_MESSAGE("Value is not convertible to double.");
}
float Value::asFloat() const
{
switch (this->type_) {
case intValue:
return static_cast<float>(this->value_.int_);
case uintValue:
#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
return static_cast<float>(this->value_.uint_);
#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
// This can fail (silently?) if the value is bigger than MAX_FLOAT.
return static_cast<float>(integerToDouble(value_.uint_));
#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
case realValue:
return static_cast<float>(this->value_.real_);
case nullValue:
return 0.0;
case booleanValue:
return this->value_.bool_ ? 1.0f : 0.0f;
default:
break;
}
JSON_FAIL_MESSAGE("Value is not convertible to float.");
}
bool Value::asBool() const
{
switch (this->type_) {
case booleanValue:
return this->value_.bool_;
case nullValue:
return false;
case intValue:
return this->value_.int_ ? true : false;
case uintValue:
return this->value_.uint_ ? true : false;
case realValue:
// This is kind of strange. Not recommended.
return (this->value_.real_ != 0.0) ? true : false;
default:
break;
}
JSON_FAIL_MESSAGE("Value is not convertible to bool.");
}
bool Value::isConvertibleTo(ValueType other) const
{
switch (other) {
case nullValue:
return (this->isNumeric() && this->asDouble() == 0.0) ||
(this->type_ == booleanValue && this->value_.bool_ == false) ||
(this->type_ == stringValue && this->asString().empty()) ||
(this->type_ == arrayValue && this->value_.map_->size() == 0) ||
(this->type_ == objectValue && this->value_.map_->size() == 0) ||
this->type_ == nullValue;
case intValue:
return this->isInt() ||
(this->type_ == realValue &&
InRange(this->value_.real_, minInt, maxInt)) ||
this->type_ == booleanValue || this->type_ == nullValue;
case uintValue:
return this->isUInt() ||
(this->type_ == realValue &&
InRange(this->value_.real_, 0, maxUInt)) ||
this->type_ == booleanValue || this->type_ == nullValue;
case realValue:
return this->isNumeric() || this->type_ == booleanValue ||
this->type_ == nullValue;
case booleanValue:
return this->isNumeric() || this->type_ == booleanValue ||
this->type_ == nullValue;
case stringValue:
return this->isNumeric() || this->type_ == booleanValue ||
this->type_ == stringValue || this->type_ == nullValue;
case arrayValue:
return this->type_ == arrayValue || this->type_ == nullValue;
case objectValue:
return this->type_ == objectValue || this->type_ == nullValue;
}
JSON_ASSERT_UNREACHABLE;
return false;
}
/// Number of values in array or object
ArrayIndex Value::size() const
{
switch (this->type_) {
case nullValue:
case intValue:
case uintValue:
case realValue:
case booleanValue:
case stringValue:
return 0;
case arrayValue: // size of the array is highest index + 1
if (!this->value_.map_->empty()) {
ObjectValues::const_iterator itLast = this->value_.map_->end();
--itLast;
return (*itLast).first.index() + 1;
}
return 0;
case objectValue:
return ArrayIndex(this->value_.map_->size());
}
JSON_ASSERT_UNREACHABLE;
return 0; // unreachable;
}
bool Value::empty() const
{
if (this->isNull() || this->isArray() || this->isObject())
return this->size() == 0u;
else
return false;
}
bool Value::operator!() const
{
return this->isNull();
}
void Value::clear()
{
JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue ||
type_ == objectValue,
"in Json::Value::clear(): requires complex value");
this->start_ = 0;
this->limit_ = 0;
switch (this->type_) {
case arrayValue:
case objectValue:
this->value_.map_->clear();
break;
default:
break;
}
}
void Value::resize(ArrayIndex newSize)
{
JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue,
"in Json::Value::resize(): requires arrayValue");
if (this->type_ == nullValue)
*this = Value(arrayValue);
ArrayIndex oldSize = this->size();
if (newSize == 0)
this->clear();
else if (newSize > oldSize)
(*this)[newSize - 1];
else {
for (ArrayIndex index = newSize; index < oldSize; ++index) {
this->value_.map_->erase(index);
}
JSON_ASSERT(size() == newSize);
}
}
Value& Value::operator[](ArrayIndex index)
{
JSON_ASSERT_MESSAGE(
type_ == nullValue || type_ == arrayValue,
"in Json::Value::operator[](ArrayIndex): requires arrayValue");
if (this->type_ == nullValue)
*this = Value(arrayValue);
CZString key(index);
ObjectValues::iterator it = this->value_.map_->lower_bound(key);
if (it != this->value_.map_->end() && (*it).first == key)
return (*it).second;
ObjectValues::value_type defaultValue(key, nullSingleton());
it = this->value_.map_->insert(it, defaultValue);
return (*it).second;
}
Value& Value::operator[](int index)
{
JSON_ASSERT_MESSAGE(
index >= 0,
"in Json::Value::operator[](int index): index cannot be negative");
return (*this)[ArrayIndex(index)];
}
const Value& Value::operator[](ArrayIndex index) const
{
JSON_ASSERT_MESSAGE(
type_ == nullValue || type_ == arrayValue,
"in Json::Value::operator[](ArrayIndex)const: requires arrayValue");
if (this->type_ == nullValue)
return nullSingleton();
CZString key(index);
ObjectValues::const_iterator it = this->value_.map_->find(key);
if (it == this->value_.map_->end())
return nullSingleton();
return (*it).second;
}
const Value& Value::operator[](int index) const
{
JSON_ASSERT_MESSAGE(
index >= 0,
"in Json::Value::operator[](int index) const: index cannot be negative");
return (*this)[ArrayIndex(index)];
}
void Value::initBasic(ValueType vtype, bool allocated)
{
this->type_ = vtype;
this->allocated_ = allocated;
this->comments_ = 0;
this->start_ = 0;
this->limit_ = 0;
}
// Access an object value by name, create a null member if it does not exist.
// @pre Type of '*this' is object or null.
// @param key is null-terminated.
Value& Value::resolveReference(const char* key)
{
JSON_ASSERT_MESSAGE(
type_ == nullValue || type_ == objectValue,
"in Json::Value::resolveReference(): requires objectValue");
if (this->type_ == nullValue)
*this = Value(objectValue);
CZString actualKey(key, static_cast<unsigned>(strlen(key)),
CZString::noDuplication); // NOTE!
ObjectValues::iterator it = this->value_.map_->lower_bound(actualKey);
if (it != this->value_.map_->end() && (*it).first == actualKey)
return (*it).second;
ObjectValues::value_type defaultValue(actualKey, nullSingleton());
it = this->value_.map_->insert(it, defaultValue);
Value& value = (*it).second;
return value;
}
// @param key is not null-terminated.
Value& Value::resolveReference(char const* key, char const* cend)
{
JSON_ASSERT_MESSAGE(
type_ == nullValue || type_ == objectValue,
"in Json::Value::resolveReference(key, end): requires objectValue");
if (this->type_ == nullValue)
*this = Value(objectValue);
CZString actualKey(key, static_cast<unsigned>(cend - key),
CZString::duplicateOnCopy);
ObjectValues::iterator it = this->value_.map_->lower_bound(actualKey);
if (it != this->value_.map_->end() && (*it).first == actualKey)
return (*it).second;
ObjectValues::value_type defaultValue(actualKey, nullSingleton());
it = this->value_.map_->insert(it, defaultValue);
Value& value = (*it).second;
return value;
}
Value Value::get(ArrayIndex index, const Value& defaultValue) const
{
const Value* value = &((*this)[index]);
return value == &nullSingleton() ? defaultValue : *value;
}
bool Value::isValidIndex(ArrayIndex index) const
{
return index < this->size();
}
Value const* Value::find(char const* key, char const* cend) const
{
JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue,
"in Json::Value::find(key, end, found): requires "
"objectValue or nullValue");
if (this->type_ == nullValue)
return NULL;
CZString actualKey(key, static_cast<unsigned>(cend - key),
CZString::noDuplication);
ObjectValues::const_iterator it = this->value_.map_->find(actualKey);
if (it == this->value_.map_->end())
return NULL;
return &(*it).second;
}
const Value& Value::operator[](const char* key) const
{
Value const* found = this->find(key, key + strlen(key));
if (!found)
return nullSingleton();
return *found;
}
Value const& Value::operator[](JSONCPP_STRING const& key) const
{
Value const* found = this->find(key.data(), key.data() + key.length());
if (!found)
return nullSingleton();
return *found;
}
Value& Value::operator[](const char* key)
{
return this->resolveReference(key, key + strlen(key));
}
Value& Value::operator[](const JSONCPP_STRING& key)
{
return this->resolveReference(key.data(), key.data() + key.length());
}
Value& Value::operator[](const StaticString& key)
{
return this->resolveReference(key.c_str());
}
#ifdef JSON_USE_CPPTL
Value& Value::operator[](const CppTL::ConstString& key)
{
return resolveReference(key.c_str(), key.end_c_str());
}
Value const& Value::operator[](CppTL::ConstString const& key) const
{
Value const* found = find(key.c_str(), key.end_c_str());
if (!found)
return nullSingleton();
return *found;
}
#endif
Value& Value::append(const Value& value)
{
return (*this)[this->size()] = value;
}
#if JSON_HAS_RVALUE_REFERENCES
Value& Value::append(Value&& value)
{
return (*this)[this->size()] = value;
}
#endif
Value Value::get(char const* key, char const* cend,
Value const& defaultValue) const
{
Value const* found = this->find(key, cend);
return !found ? defaultValue : *found;
}
Value Value::get(char const* key, Value const& defaultValue) const
{
return this->get(key, key + strlen(key), defaultValue);
}
Value Value::get(JSONCPP_STRING const& key, Value const& defaultValue) const
{
return this->get(key.data(), key.data() + key.length(), defaultValue);
}
bool Value::removeMember(const char* key, const char* cend, Value* removed)
{
if (this->type_ != objectValue) {
return false;
}
CZString actualKey(key, static_cast<unsigned>(cend - key),
CZString::noDuplication);
ObjectValues::iterator it = this->value_.map_->find(actualKey);
if (it == this->value_.map_->end())
return false;
*removed = it->second;
this->value_.map_->erase(it);
return true;
}
bool Value::removeMember(const char* key, Value* removed)
{
return this->removeMember(key, key + strlen(key), removed);
}
bool Value::removeMember(JSONCPP_STRING const& key, Value* removed)
{
return this->removeMember(key.data(), key.data() + key.length(), removed);
}
Value Value::removeMember(const char* key)
{
JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue,
"in Json::Value::removeMember(): requires objectValue");
if (this->type_ == nullValue)
return nullSingleton();
Value removed; // null
this->removeMember(key, key + strlen(key), &removed);
return removed; // still null if removeMember() did nothing
}
Value Value::removeMember(const JSONCPP_STRING& key)
{
return this->removeMember(key.c_str());
}
bool Value::removeIndex(ArrayIndex index, Value* removed)
{
if (this->type_ != arrayValue) {
return false;
}
CZString key(index);
ObjectValues::iterator it = this->value_.map_->find(key);
if (it == this->value_.map_->end()) {
return false;
}
*removed = it->second;
ArrayIndex oldSize = this->size();
// shift left all items left, into the place of the "removed"
for (ArrayIndex i = index; i < (oldSize - 1); ++i) {
CZString keey(i);
(*this->value_.map_)[keey] = (*this)[i + 1];
}
// erase the last one ("leftover")
CZString keyLast(oldSize - 1);
ObjectValues::iterator itLast = this->value_.map_->find(keyLast);
this->value_.map_->erase(itLast);
return true;
}
#ifdef JSON_USE_CPPTL
Value Value::get(const CppTL::ConstString& key,
const Value& defaultValue) const
{
return get(key.c_str(), key.end_c_str(), defaultValue);
}
#endif
bool Value::isMember(char const* key, char const* cend) const
{
Value const* value = this->find(key, cend);
return NULL != value;
}
bool Value::isMember(char const* key) const
{
return this->isMember(key, key + strlen(key));
}
bool Value::isMember(JSONCPP_STRING const& key) const
{
return this->isMember(key.data(), key.data() + key.length());
}
#ifdef JSON_USE_CPPTL
bool Value::isMember(const CppTL::ConstString& key) const
{
return isMember(key.c_str(), key.end_c_str());
}
#endif
Value::Members Value::getMemberNames() const
{
JSON_ASSERT_MESSAGE(
type_ == nullValue || type_ == objectValue,
"in Json::Value::getMemberNames(), value must be objectValue");
if (this->type_ == nullValue)
return Value::Members();
Members members;
members.reserve(this->value_.map_->size());
ObjectValues::const_iterator it = this->value_.map_->begin();
ObjectValues::const_iterator itEnd = this->value_.map_->end();
for (; it != itEnd; ++it) {
members.push_back(
JSONCPP_STRING((*it).first.data(), (*it).first.length()));
}
return members;
}
//
//# ifdef JSON_USE_CPPTL
// EnumMemberNames
// Value::enumMemberNames() const
//{
// if ( type_ == objectValue )
// {
// return CppTL::Enum::any( CppTL::Enum::transform(
// CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>()
// ), MemberNamesTransform() ) );
// }
// return EnumMemberNames();
//}
//
//
// EnumValues
// Value::enumValues() const
//{
// if ( type_ == objectValue || type_ == arrayValue )
// return CppTL::Enum::anyValues( *(value_.map_),
// CppTL::Type<const Value &>() );
// return EnumValues();
//}
//
//# endif
static bool IsIntegral(double d)
{
double integral_part;
return modf(d, &integral_part) == 0.0;
}
bool Value::isNull() const
{
return this->type_ == nullValue;
}
bool Value::isBool() const
{
return this->type_ == booleanValue;
}
bool Value::isInt() const
{
switch (this->type_) {
case intValue:
#if defined(JSON_HAS_INT64)
return this->value_.int_ >= minInt && this->value_.int_ <= maxInt;
#else
return true;
#endif
case uintValue:
return this->value_.uint_ <= UInt(maxInt);
case realValue:
return this->value_.real_ >= minInt && this->value_.real_ <= maxInt &&
IsIntegral(this->value_.real_);
default:
break;
}
return false;
}
bool Value::isUInt() const
{
switch (this->type_) {
case intValue:
#if defined(JSON_HAS_INT64)
return this->value_.int_ >= 0 &&
LargestUInt(this->value_.int_) <= LargestUInt(maxUInt);
#else
return value_.int_ >= 0;
#endif
case uintValue:
#if defined(JSON_HAS_INT64)
return this->value_.uint_ <= maxUInt;
#else
return true;
#endif
case realValue:
return this->value_.real_ >= 0 && this->value_.real_ <= maxUInt &&
IsIntegral(this->value_.real_);
default:
break;
}
return false;
}
bool Value::isInt64() const
{
#if defined(JSON_HAS_INT64)
switch (this->type_) {
case intValue:
return true;
case uintValue:
return this->value_.uint_ <= UInt64(maxInt64);
case realValue:
// Note that maxInt64 (= 2^63 - 1) is not exactly representable as a
// double, so double(maxInt64) will be rounded up to 2^63. Therefore we
// require the value to be strictly less than the limit.
return this->value_.real_ >= double(minInt64) &&
this->value_.real_ < double(maxInt64) &&
IsIntegral(this->value_.real_);
default:
break;
}
#endif // JSON_HAS_INT64
return false;
}
bool Value::isUInt64() const
{
#if defined(JSON_HAS_INT64)
switch (this->type_) {
case intValue:
return this->value_.int_ >= 0;
case uintValue:
return true;
case realValue:
// Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
// double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
// require the value to be strictly less than the limit.
return this->value_.real_ >= 0 &&
this->value_.real_ < maxUInt64AsDouble &&
IsIntegral(this->value_.real_);
default:
break;
}
#endif // JSON_HAS_INT64
return false;
}
bool Value::isIntegral() const
{
switch (this->type_) {
case intValue:
case uintValue:
return true;
case realValue:
#if defined(JSON_HAS_INT64)
// Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
// double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
// require the value to be strictly less than the limit.
return this->value_.real_ >= double(minInt64) &&
this->value_.real_ < maxUInt64AsDouble &&
IsIntegral(this->value_.real_);
#else
return value_.real_ >= minInt && value_.real_ <= maxUInt &&
IsIntegral(value_.real_);
#endif // JSON_HAS_INT64
default:
break;
}
return false;
}
bool Value::isDouble() const
{
return this->type_ == intValue || this->type_ == uintValue ||
this->type_ == realValue;
}
bool Value::isNumeric() const
{
return this->isDouble();
}
bool Value::isString() const
{
return this->type_ == stringValue;
}
bool Value::isArray() const
{
return this->type_ == arrayValue;
}
bool Value::isObject() const
{
return this->type_ == objectValue;
}
void Value::setComment(const char* comment, size_t len,
CommentPlacement placement)
{
if (!this->comments_)
this->comments_ = new CommentInfo[numberOfCommentPlacement];
if ((len > 0) && (comment[len - 1] == '\n')) {
// Always discard trailing newline, to aid indentation.
len -= 1;
}
this->comments_[placement].setComment(comment, len);
}
void Value::setComment(const char* comment, CommentPlacement placement)
{
this->setComment(comment, strlen(comment), placement);
}
void Value::setComment(const JSONCPP_STRING& comment,
CommentPlacement placement)
{
this->setComment(comment.c_str(), comment.length(), placement);
}
bool Value::hasComment(CommentPlacement placement) const
{
return this->comments_ != 0 && this->comments_[placement].comment_ != 0;
}
JSONCPP_STRING Value::getComment(CommentPlacement placement) const
{
if (this->hasComment(placement))
return this->comments_[placement].comment_;
return "";
}
void Value::setOffsetStart(ptrdiff_t start)
{
this->start_ = start;
}
void Value::setOffsetLimit(ptrdiff_t limit)
{
this->limit_ = limit;
}
ptrdiff_t Value::getOffsetStart() const
{
return this->start_;
}
ptrdiff_t Value::getOffsetLimit() const
{
return this->limit_;
}
JSONCPP_STRING Value::toStyledString() const
{
StreamWriterBuilder builder;
JSONCPP_STRING out = this->hasComment(commentBefore) ? "\n" : "";
out += Json::writeString(builder, *this);
out += "\n";
return out;
}
Value::const_iterator Value::begin() const
{
switch (this->type_) {
case arrayValue:
case objectValue:
if (this->value_.map_)
return const_iterator(this->value_.map_->begin());
break;
default:
break;
}
return const_iterator();
}
Value::const_iterator Value::end() const
{
switch (this->type_) {
case arrayValue:
case objectValue:
if (this->value_.map_)
return const_iterator(this->value_.map_->end());
break;
default:
break;
}
return const_iterator();
}
Value::iterator Value::begin()
{
switch (this->type_) {
case arrayValue:
case objectValue:
if (this->value_.map_)
return iterator(this->value_.map_->begin());
break;
default:
break;
}
return iterator();
}
Value::iterator Value::end()
{
switch (this->type_) {
case arrayValue:
case objectValue:
if (this->value_.map_)
return iterator(this->value_.map_->end());
break;
default:
break;
}
return iterator();
}
// class PathArgument
// //////////////////////////////////////////////////////////////////
PathArgument::PathArgument()
: key_()
, index_()
, kind_(kindNone)
{
}
PathArgument::PathArgument(ArrayIndex index)
: key_()
, index_(index)
, kind_(kindIndex)
{
}
PathArgument::PathArgument(const char* key)
: key_(key)
, index_()
, kind_(kindKey)
{
}
PathArgument::PathArgument(const JSONCPP_STRING& key)
: key_(key.c_str())
, index_()
, kind_(kindKey)
{
}
// class Path
// //////////////////////////////////////////////////////////////////
Path::Path(const JSONCPP_STRING& path, const PathArgument& a1,
const PathArgument& a2, const PathArgument& a3,
const PathArgument& a4, const PathArgument& a5)
{
InArgs in;
in.reserve(5);
in.push_back(&a1);
in.push_back(&a2);
in.push_back(&a3);
in.push_back(&a4);
in.push_back(&a5);
this->makePath(path, in);
}
void Path::makePath(const JSONCPP_STRING& path, const InArgs& in)
{
const char* current = path.c_str();
const char* end = current + path.length();
InArgs::const_iterator itInArg = in.begin();
while (current != end) {
if (*current == '[') {
++current;
if (*current == '%')
this->addPathInArg(path, in, itInArg, PathArgument::kindIndex);
else {
ArrayIndex index = 0;
for (; current != end && *current >= '0' && *current <= '9'; ++current)
index = index * 10 + ArrayIndex(*current - '0');
this->args_.push_back(index);
}
if (current == end || *++current != ']')
this->invalidPath(path, int(current - path.c_str()));
} else if (*current == '%') {
this->addPathInArg(path, in, itInArg, PathArgument::kindKey);
++current;
} else if (*current == '.' || *current == ']') {
++current;
} else {
const char* beginName = current;
while (current != end && !strchr("[.", *current))
++current;
this->args_.push_back(JSONCPP_STRING(beginName, current));
}
}
}
void Path::addPathInArg(const JSONCPP_STRING& /*path*/, const InArgs& in,
InArgs::const_iterator& itInArg,
PathArgument::Kind kind)
{
if (itInArg == in.end()) {
// Error: missing argument %d
} else if ((*itInArg)->kind_ != kind) {
// Error: bad argument type
} else {
this->args_.push_back(**itInArg++);
}
}
void Path::invalidPath(const JSONCPP_STRING& /*path*/, int /*location*/)
{
// Error: invalid path.
}
const Value& Path::resolve(const Value& root) const
{
const Value* node = &root;
for (Args::const_iterator it = this->args_.begin(); it != this->args_.end();
++it) {
const PathArgument& arg = *it;
if (arg.kind_ == PathArgument::kindIndex) {
if (!node->isArray() || !node->isValidIndex(arg.index_)) {
// Error: unable to resolve path (array value expected at position...
return Value::null;
}
node = &((*node)[arg.index_]);
} else if (arg.kind_ == PathArgument::kindKey) {
if (!node->isObject()) {
// Error: unable to resolve path (object value expected at position...)
return Value::null;
}
node = &((*node)[arg.key_]);
if (node == &Value::nullSingleton()) {
// Error: unable to resolve path (object has no member named '' at
// position...)
return Value::null;
}
}
}
return *node;
}
Value Path::resolve(const Value& root, const Value& defaultValue) const
{
const Value* node = &root;
for (Args::const_iterator it = this->args_.begin(); it != this->args_.end();
++it) {
const PathArgument& arg = *it;
if (arg.kind_ == PathArgument::kindIndex) {
if (!node->isArray() || !node->isValidIndex(arg.index_))
return defaultValue;
node = &((*node)[arg.index_]);
} else if (arg.kind_ == PathArgument::kindKey) {
if (!node->isObject())
return defaultValue;
node = &((*node)[arg.key_]);
if (node == &Value::nullSingleton())
return defaultValue;
}
}
return *node;
}
Value& Path::make(Value& root) const
{
Value* node = &root;
for (Args::const_iterator it = this->args_.begin(); it != this->args_.end();
++it) {
const PathArgument& arg = *it;
if (arg.kind_ == PathArgument::kindIndex) {
if (!node->isArray()) {
// Error: node is not an array at position ...
}
node = &((*node)[arg.index_]);
} else if (arg.kind_ == PathArgument::kindKey) {
if (!node->isObject()) {
// Error: node is not an object at position...
}
node = &((*node)[arg.key_]);
}
}
return *node;
}
} // namespace Json