| // Copyright 2011 Baptiste Lepilleur | |
| // 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> | |
| #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR | |
| #include "json_batchallocator.h" | |
| #endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR | |
| #endif // if !defined(JSON_IS_AMALGAMATION) | |
| #include <math.h> | |
| #include <sstream> | |
| #include <utility> | |
| #include <cstring> | |
| #include <cassert> | |
| #ifdef JSON_USE_CPPTL | |
| #include <cpptl/conststring.h> | |
| #endif | |
| #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 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); | |
| /// Unknown size marker | |
| static const unsigned int unknown = (unsigned)-1; | |
| #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) | |
| template <typename T, typename U> | |
| static inline bool InRange(double d, T min, U 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 + 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, | |
| unsigned int length = unknown) { | |
| if (length == unknown) | |
| length = (unsigned int)strlen(value); | |
| // Avoid an integer overflow in the call to malloc below by limiting length | |
| // to a sane value. | |
| if (length >= (unsigned)Value::maxInt) | |
| length = Value::maxInt - 1; | |
| char* newString = static_cast<char*>(malloc(length + 1)); | |
| JSON_ASSERT_MESSAGE(newString != 0, | |
| "in Json::Value::duplicateStringValue(): " | |
| "Failed to allocate string value buffer"); | |
| memcpy(newString, value, length); | |
| newString[length] = 0; | |
| return newString; | |
| } | |
| /** Free the string duplicated by duplicateStringValue(). | |
| */ | |
| static inline void releaseStringValue(char* value) { free(value); } | |
| } // namespace Json | |
| // ////////////////////////////////////////////////////////////////// | |
| // ////////////////////////////////////////////////////////////////// | |
| // ////////////////////////////////////////////////////////////////// | |
| // ValueInternals... | |
| // ////////////////////////////////////////////////////////////////// | |
| // ////////////////////////////////////////////////////////////////// | |
| // ////////////////////////////////////////////////////////////////// | |
| #if !defined(JSON_IS_AMALGAMATION) | |
| #ifdef JSON_VALUE_USE_INTERNAL_MAP | |
| #include "json_internalarray.inl" | |
| #include "json_internalmap.inl" | |
| #endif // JSON_VALUE_USE_INTERNAL_MAP | |
| #include "json_valueiterator.inl" | |
| #endif // if !defined(JSON_IS_AMALGAMATION) | |
| namespace Json { | |
| // ////////////////////////////////////////////////////////////////// | |
| // ////////////////////////////////////////////////////////////////// | |
| // ////////////////////////////////////////////////////////////////// | |
| // class Value::CommentInfo | |
| // ////////////////////////////////////////////////////////////////// | |
| // ////////////////////////////////////////////////////////////////// | |
| // ////////////////////////////////////////////////////////////////// | |
| Value::CommentInfo::CommentInfo() : comment_(0) {} | |
| Value::CommentInfo::~CommentInfo() { | |
| if (comment_) | |
| releaseStringValue(comment_); | |
| } | |
| void Value::CommentInfo::setComment(const char* text) { | |
| if (comment_) | |
| releaseStringValue(comment_); | |
| 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. | |
| comment_ = duplicateStringValue(text); | |
| } | |
| // ////////////////////////////////////////////////////////////////// | |
| // ////////////////////////////////////////////////////////////////// | |
| // ////////////////////////////////////////////////////////////////// | |
| // class Value::CZString | |
| // ////////////////////////////////////////////////////////////////// | |
| // ////////////////////////////////////////////////////////////////// | |
| // ////////////////////////////////////////////////////////////////// | |
| #ifndef JSON_VALUE_USE_INTERNAL_MAP | |
| // Notes: index_ indicates if the string was allocated when | |
| // a string is stored. | |
| Value::CZString::CZString(ArrayIndex index) : cstr_(0), index_(index) {} | |
| Value::CZString::CZString(const char* cstr, DuplicationPolicy allocate) | |
| : cstr_(allocate == duplicate ? duplicateStringValue(cstr) : cstr), | |
| index_(allocate) {} | |
| Value::CZString::CZString(const CZString& other) | |
| : cstr_(other.index_ != noDuplication && other.cstr_ != 0 | |
| ? duplicateStringValue(other.cstr_) | |
| : other.cstr_), | |
| index_(other.cstr_ | |
| ? static_cast<ArrayIndex>(other.index_ == noDuplication | |
| ? noDuplication : duplicate) | |
| : other.index_) {} | |
| Value::CZString::~CZString() { | |
| if (cstr_ && index_ == duplicate) | |
| releaseStringValue(const_cast<char*>(cstr_)); | |
| } | |
| void Value::CZString::swap(CZString& other) { | |
| std::swap(cstr_, other.cstr_); | |
| std::swap(index_, other.index_); | |
| } | |
| Value::CZString& Value::CZString::operator=(CZString other) { | |
| swap(other); | |
| return *this; | |
| } | |
| bool Value::CZString::operator<(const CZString& other) const { | |
| if (cstr_) | |
| return strcmp(cstr_, other.cstr_) < 0; | |
| return index_ < other.index_; | |
| } | |
| bool Value::CZString::operator==(const CZString& other) const { | |
| if (cstr_) | |
| return strcmp(cstr_, other.cstr_) == 0; | |
| return index_ == other.index_; | |
| } | |
| ArrayIndex Value::CZString::index() const { return index_; } | |
| const char* Value::CZString::c_str() const { return cstr_; } | |
| bool Value::CZString::isStaticString() const { return index_ == noDuplication; } | |
| #endif // ifndef JSON_VALUE_USE_INTERNAL_MAP | |
| // ////////////////////////////////////////////////////////////////// | |
| // ////////////////////////////////////////////////////////////////// | |
| // ////////////////////////////////////////////////////////////////// | |
| // 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 type) { | |
| initBasic(type); | |
| switch (type) { | |
| case nullValue: | |
| break; | |
| case intValue: | |
| case uintValue: | |
| value_.int_ = 0; | |
| break; | |
| case realValue: | |
| value_.real_ = 0.0; | |
| break; | |
| case stringValue: | |
| value_.string_ = 0; | |
| break; | |
| #ifndef JSON_VALUE_USE_INTERNAL_MAP | |
| case arrayValue: | |
| case objectValue: | |
| value_.map_ = new ObjectValues(); | |
| break; | |
| #else | |
| case arrayValue: | |
| value_.array_ = arrayAllocator()->newArray(); | |
| break; | |
| case objectValue: | |
| value_.map_ = mapAllocator()->newMap(); | |
| break; | |
| #endif | |
| case booleanValue: | |
| value_.bool_ = false; | |
| break; | |
| default: | |
| JSON_ASSERT_UNREACHABLE; | |
| } | |
| } | |
| Value::Value(Int value) { | |
| initBasic(intValue); | |
| value_.int_ = value; | |
| } | |
| Value::Value(UInt value) { | |
| initBasic(uintValue); | |
| value_.uint_ = value; | |
| } | |
| #if defined(JSON_HAS_INT64) | |
| Value::Value(Int64 value) { | |
| initBasic(intValue); | |
| value_.int_ = value; | |
| } | |
| Value::Value(UInt64 value) { | |
| initBasic(uintValue); | |
| value_.uint_ = value; | |
| } | |
| #endif // defined(JSON_HAS_INT64) | |
| Value::Value(double value) { | |
| initBasic(realValue); | |
| value_.real_ = value; | |
| } | |
| Value::Value(const char* value) { | |
| initBasic(stringValue, true); | |
| value_.string_ = duplicateStringValue(value); | |
| } | |
| Value::Value(const char* beginValue, const char* endValue) { | |
| initBasic(stringValue, true); | |
| value_.string_ = | |
| duplicateStringValue(beginValue, (unsigned int)(endValue - beginValue)); | |
| } | |
| Value::Value(const std::string& value) { | |
| initBasic(stringValue, true); | |
| value_.string_ = | |
| duplicateStringValue(value.c_str(), (unsigned int)value.length()); | |
| } | |
| Value::Value(const StaticString& value) { | |
| initBasic(stringValue); | |
| value_.string_ = const_cast<char*>(value.c_str()); | |
| } | |
| #ifdef JSON_USE_CPPTL | |
| Value::Value(const CppTL::ConstString& value) { | |
| initBasic(stringValue, true); | |
| value_.string_ = duplicateStringValue(value, value.length()); | |
| } | |
| #endif | |
| Value::Value(bool value) { | |
| initBasic(booleanValue); | |
| value_.bool_ = value; | |
| } | |
| Value::Value(const Value& other) | |
| : type_(other.type_), allocated_(false) | |
| #ifdef JSON_VALUE_USE_INTERNAL_MAP | |
| , | |
| itemIsUsed_(0) | |
| #endif | |
| , | |
| comments_(0), start_(other.start_), limit_(other.limit_) { | |
| switch (type_) { | |
| case nullValue: | |
| case intValue: | |
| case uintValue: | |
| case realValue: | |
| case booleanValue: | |
| value_ = other.value_; | |
| break; | |
| case stringValue: | |
| if (other.value_.string_) { | |
| value_.string_ = duplicateStringValue(other.value_.string_); | |
| allocated_ = true; | |
| } else { | |
| value_.string_ = 0; | |
| allocated_ = false; | |
| } | |
| break; | |
| #ifndef JSON_VALUE_USE_INTERNAL_MAP | |
| case arrayValue: | |
| case objectValue: | |
| value_.map_ = new ObjectValues(*other.value_.map_); | |
| break; | |
| #else | |
| case arrayValue: | |
| value_.array_ = arrayAllocator()->newArrayCopy(*other.value_.array_); | |
| break; | |
| case objectValue: | |
| value_.map_ = mapAllocator()->newMapCopy(*other.value_.map_); | |
| break; | |
| #endif | |
| default: | |
| JSON_ASSERT_UNREACHABLE; | |
| } | |
| if (other.comments_) { | |
| comments_ = new CommentInfo[numberOfCommentPlacement]; | |
| for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { | |
| const CommentInfo& otherComment = other.comments_[comment]; | |
| if (otherComment.comment_) | |
| comments_[comment].setComment(otherComment.comment_); | |
| } | |
| } | |
| } | |
| Value::~Value() { | |
| switch (type_) { | |
| case nullValue: | |
| case intValue: | |
| case uintValue: | |
| case realValue: | |
| case booleanValue: | |
| break; | |
| case stringValue: | |
| if (allocated_) | |
| releaseStringValue(value_.string_); | |
| break; | |
| #ifndef JSON_VALUE_USE_INTERNAL_MAP | |
| case arrayValue: | |
| case objectValue: | |
| delete value_.map_; | |
| break; | |
| #else | |
| case arrayValue: | |
| arrayAllocator()->destructArray(value_.array_); | |
| break; | |
| case objectValue: | |
| mapAllocator()->destructMap(value_.map_); | |
| break; | |
| #endif | |
| default: | |
| JSON_ASSERT_UNREACHABLE; | |
| } | |
| if (comments_) | |
| delete[] comments_; | |
| } | |
| Value& Value::operator=(Value other) { | |
| swap(other); | |
| return *this; | |
| } | |
| void Value::swap(Value& other) { | |
| ValueType temp = type_; | |
| type_ = other.type_; | |
| other.type_ = temp; | |
| std::swap(value_, other.value_); | |
| int temp2 = allocated_; | |
| allocated_ = other.allocated_; | |
| other.allocated_ = temp2; | |
| std::swap(start_, other.start_); | |
| std::swap(limit_, other.limit_); | |
| } | |
| ValueType Value::type() const { return 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 = type_ - other.type_; | |
| if (typeDelta) | |
| return typeDelta < 0 ? true : false; | |
| switch (type_) { | |
| case nullValue: | |
| return false; | |
| case intValue: | |
| return value_.int_ < other.value_.int_; | |
| case uintValue: | |
| return value_.uint_ < other.value_.uint_; | |
| case realValue: | |
| return value_.real_ < other.value_.real_; | |
| case booleanValue: | |
| return value_.bool_ < other.value_.bool_; | |
| case stringValue: | |
| return (value_.string_ == 0 && other.value_.string_) || | |
| (other.value_.string_ && value_.string_ && | |
| strcmp(value_.string_, other.value_.string_) < 0); | |
| #ifndef JSON_VALUE_USE_INTERNAL_MAP | |
| case arrayValue: | |
| case objectValue: { | |
| int delta = int(value_.map_->size() - other.value_.map_->size()); | |
| if (delta) | |
| return delta < 0; | |
| return (*value_.map_) < (*other.value_.map_); | |
| } | |
| #else | |
| case arrayValue: | |
| return value_.array_->compare(*(other.value_.array_)) < 0; | |
| case objectValue: | |
| return value_.map_->compare(*(other.value_.map_)) < 0; | |
| #endif | |
| 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 (type_ != temp) | |
| return false; | |
| switch (type_) { | |
| case nullValue: | |
| return true; | |
| case intValue: | |
| return value_.int_ == other.value_.int_; | |
| case uintValue: | |
| return value_.uint_ == other.value_.uint_; | |
| case realValue: | |
| return value_.real_ == other.value_.real_; | |
| case booleanValue: | |
| return value_.bool_ == other.value_.bool_; | |
| case stringValue: | |
| return (value_.string_ == other.value_.string_) || | |
| (other.value_.string_ && value_.string_ && | |
| strcmp(value_.string_, other.value_.string_) == 0); | |
| #ifndef JSON_VALUE_USE_INTERNAL_MAP | |
| case arrayValue: | |
| case objectValue: | |
| return value_.map_->size() == other.value_.map_->size() && | |
| (*value_.map_) == (*other.value_.map_); | |
| #else | |
| case arrayValue: | |
| return value_.array_->compare(*(other.value_.array_)) == 0; | |
| case objectValue: | |
| return value_.map_->compare(*(other.value_.map_)) == 0; | |
| #endif | |
| 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"); | |
| return value_.string_; | |
| } | |
| std::string Value::asString() const { | |
| switch (type_) { | |
| case nullValue: | |
| return ""; | |
| case stringValue: | |
| return value_.string_ ? value_.string_ : ""; | |
| case booleanValue: | |
| return value_.bool_ ? "true" : "false"; | |
| case intValue: | |
| return valueToString(value_.int_); | |
| case uintValue: | |
| return valueToString(value_.uint_); | |
| case realValue: | |
| return valueToString(value_.real_); | |
| default: | |
| JSON_FAIL_MESSAGE("Type is not convertible to string"); | |
| } | |
| } | |
| #ifdef JSON_USE_CPPTL | |
| CppTL::ConstString Value::asConstString() const { | |
| return CppTL::ConstString(asString().c_str()); | |
| } | |
| #endif | |
| Value::Int Value::asInt() const { | |
| switch (type_) { | |
| case intValue: | |
| JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); | |
| return Int(value_.int_); | |
| case uintValue: | |
| JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range"); | |
| return Int(value_.uint_); | |
| case realValue: | |
| JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), | |
| "double out of Int range"); | |
| return Int(value_.real_); | |
| case nullValue: | |
| return 0; | |
| case booleanValue: | |
| return value_.bool_ ? 1 : 0; | |
| default: | |
| break; | |
| } | |
| JSON_FAIL_MESSAGE("Value is not convertible to Int."); | |
| } | |
| Value::UInt Value::asUInt() const { | |
| switch (type_) { | |
| case intValue: | |
| JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); | |
| return UInt(value_.int_); | |
| case uintValue: | |
| JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range"); | |
| return UInt(value_.uint_); | |
| case realValue: | |
| JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), | |
| "double out of UInt range"); | |
| return UInt(value_.real_); | |
| case nullValue: | |
| return 0; | |
| case booleanValue: | |
| return 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 (type_) { | |
| case intValue: | |
| return Int64(value_.int_); | |
| case uintValue: | |
| JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); | |
| return Int64(value_.uint_); | |
| case realValue: | |
| JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), | |
| "double out of Int64 range"); | |
| return Int64(value_.real_); | |
| case nullValue: | |
| return 0; | |
| case booleanValue: | |
| return value_.bool_ ? 1 : 0; | |
| default: | |
| break; | |
| } | |
| JSON_FAIL_MESSAGE("Value is not convertible to Int64."); | |
| } | |
| Value::UInt64 Value::asUInt64() const { | |
| switch (type_) { | |
| case intValue: | |
| JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); | |
| return UInt64(value_.int_); | |
| case uintValue: | |
| return UInt64(value_.uint_); | |
| case realValue: | |
| JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), | |
| "double out of UInt64 range"); | |
| return UInt64(value_.real_); | |
| case nullValue: | |
| return 0; | |
| case booleanValue: | |
| return 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 asInt64(); | |
| #endif | |
| } | |
| LargestUInt Value::asLargestUInt() const { | |
| #if defined(JSON_NO_INT64) | |
| return asUInt(); | |
| #else | |
| return asUInt64(); | |
| #endif | |
| } | |
| double Value::asDouble() const { | |
| switch (type_) { | |
| case intValue: | |
| return static_cast<double>(value_.int_); | |
| case uintValue: | |
| #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) | |
| return static_cast<double>(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 value_.real_; | |
| case nullValue: | |
| return 0.0; | |
| case booleanValue: | |
| return value_.bool_ ? 1.0 : 0.0; | |
| default: | |
| break; | |
| } | |
| JSON_FAIL_MESSAGE("Value is not convertible to double."); | |
| } | |
| float Value::asFloat() const { | |
| switch (type_) { | |
| case intValue: | |
| return static_cast<float>(value_.int_); | |
| case uintValue: | |
| #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) | |
| return static_cast<float>(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 static_cast<float>(value_.real_); | |
| case nullValue: | |
| return 0.0; | |
| case booleanValue: | |
| return value_.bool_ ? 1.0f : 0.0f; | |
| default: | |
| break; | |
| } | |
| JSON_FAIL_MESSAGE("Value is not convertible to float."); | |
| } | |
| bool Value::asBool() const { | |
| switch (type_) { | |
| case booleanValue: | |
| return value_.bool_; | |
| case nullValue: | |
| return false; | |
| case intValue: | |
| return value_.int_ ? true : false; | |
| case uintValue: | |
| return value_.uint_ ? true : false; | |
| case realValue: | |
| return value_.real_ ? true : false; | |
| default: | |
| break; | |
| } | |
| JSON_FAIL_MESSAGE("Value is not convertible to bool."); | |
| } | |
| bool Value::isConvertibleTo(ValueType other) const { | |
| switch (other) { | |
| case nullValue: | |
| return (isNumeric() && asDouble() == 0.0) || | |
| (type_ == booleanValue && value_.bool_ == false) || | |
| (type_ == stringValue && asString() == "") || | |
| (type_ == arrayValue && value_.map_->size() == 0) || | |
| (type_ == objectValue && value_.map_->size() == 0) || | |
| type_ == nullValue; | |
| case intValue: | |
| return isInt() || | |
| (type_ == realValue && InRange(value_.real_, minInt, maxInt)) || | |
| type_ == booleanValue || type_ == nullValue; | |
| case uintValue: | |
| return isUInt() || | |
| (type_ == realValue && InRange(value_.real_, 0, maxUInt)) || | |
| type_ == booleanValue || type_ == nullValue; | |
| case realValue: | |
| return isNumeric() || type_ == booleanValue || type_ == nullValue; | |
| case booleanValue: | |
| return isNumeric() || type_ == booleanValue || type_ == nullValue; | |
| case stringValue: | |
| return isNumeric() || type_ == booleanValue || type_ == stringValue || | |
| type_ == nullValue; | |
| case arrayValue: | |
| return type_ == arrayValue || type_ == nullValue; | |
| case objectValue: | |
| return type_ == objectValue || type_ == nullValue; | |
| } | |
| JSON_ASSERT_UNREACHABLE; | |
| return false; | |
| } | |
| /// Number of values in array or object | |
| ArrayIndex Value::size() const { | |
| switch (type_) { | |
| case nullValue: | |
| case intValue: | |
| case uintValue: | |
| case realValue: | |
| case booleanValue: | |
| case stringValue: | |
| return 0; | |
| #ifndef JSON_VALUE_USE_INTERNAL_MAP | |
| case arrayValue: // size of the array is highest index + 1 | |
| if (!value_.map_->empty()) { | |
| ObjectValues::const_iterator itLast = value_.map_->end(); | |
| --itLast; | |
| return (*itLast).first.index() + 1; | |
| } | |
| return 0; | |
| case objectValue: | |
| return ArrayIndex(value_.map_->size()); | |
| #else | |
| case arrayValue: | |
| return Int(value_.array_->size()); | |
| case objectValue: | |
| return Int(value_.map_->size()); | |
| #endif | |
| } | |
| JSON_ASSERT_UNREACHABLE; | |
| return 0; // unreachable; | |
| } | |
| bool Value::empty() const { | |
| if (isNull() || isArray() || isObject()) | |
| return size() == 0u; | |
| else | |
| return false; | |
| } | |
| bool Value::operator!() const { return isNull(); } | |
| void Value::clear() { | |
| JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue || | |
| type_ == objectValue, | |
| "in Json::Value::clear(): requires complex value"); | |
| start_ = 0; | |
| limit_ = 0; | |
| switch (type_) { | |
| #ifndef JSON_VALUE_USE_INTERNAL_MAP | |
| case arrayValue: | |
| case objectValue: | |
| value_.map_->clear(); | |
| break; | |
| #else | |
| case arrayValue: | |
| value_.array_->clear(); | |
| break; | |
| case objectValue: | |
| value_.map_->clear(); | |
| break; | |
| #endif | |
| default: | |
| break; | |
| } | |
| } | |
| void Value::resize(ArrayIndex newSize) { | |
| JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, | |
| "in Json::Value::resize(): requires arrayValue"); | |
| if (type_ == nullValue) | |
| *this = Value(arrayValue); | |
| #ifndef JSON_VALUE_USE_INTERNAL_MAP | |
| ArrayIndex oldSize = size(); | |
| if (newSize == 0) | |
| clear(); | |
| else if (newSize > oldSize) | |
| (*this)[newSize - 1]; | |
| else { | |
| for (ArrayIndex index = newSize; index < oldSize; ++index) { | |
| value_.map_->erase(index); | |
| } | |
| assert(size() == newSize); | |
| } | |
| #else | |
| value_.array_->resize(newSize); | |
| #endif | |
| } | |
| Value& Value::operator[](ArrayIndex index) { | |
| JSON_ASSERT_MESSAGE( | |
| type_ == nullValue || type_ == arrayValue, | |
| "in Json::Value::operator[](ArrayIndex): requires arrayValue"); | |
| if (type_ == nullValue) | |
| *this = Value(arrayValue); | |
| #ifndef JSON_VALUE_USE_INTERNAL_MAP | |
| CZString key(index); | |
| ObjectValues::iterator it = value_.map_->lower_bound(key); | |
| if (it != value_.map_->end() && (*it).first == key) | |
| return (*it).second; | |
| ObjectValues::value_type defaultValue(key, null); | |
| it = value_.map_->insert(it, defaultValue); | |
| return (*it).second; | |
| #else | |
| return value_.array_->resolveReference(index); | |
| #endif | |
| } | |
| 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 (type_ == nullValue) | |
| return null; | |
| #ifndef JSON_VALUE_USE_INTERNAL_MAP | |
| CZString key(index); | |
| ObjectValues::const_iterator it = value_.map_->find(key); | |
| if (it == value_.map_->end()) | |
| return null; | |
| return (*it).second; | |
| #else | |
| Value* value = value_.array_->find(index); | |
| return value ? *value : null; | |
| #endif | |
| } | |
| 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)]; | |
| } | |
| Value& Value::operator[](const char* key) { | |
| return resolveReference(key, false); | |
| } | |
| void Value::initBasic(ValueType type, bool allocated) { | |
| type_ = type; | |
| allocated_ = allocated; | |
| #ifdef JSON_VALUE_USE_INTERNAL_MAP | |
| itemIsUsed_ = 0; | |
| #endif | |
| comments_ = 0; | |
| start_ = 0; | |
| limit_ = 0; | |
| } | |
| Value& Value::resolveReference(const char* key, bool isStatic) { | |
| JSON_ASSERT_MESSAGE( | |
| type_ == nullValue || type_ == objectValue, | |
| "in Json::Value::resolveReference(): requires objectValue"); | |
| if (type_ == nullValue) | |
| *this = Value(objectValue); | |
| #ifndef JSON_VALUE_USE_INTERNAL_MAP | |
| CZString actualKey( | |
| key, isStatic ? CZString::noDuplication : CZString::duplicateOnCopy); | |
| ObjectValues::iterator it = value_.map_->lower_bound(actualKey); | |
| if (it != value_.map_->end() && (*it).first == actualKey) | |
| return (*it).second; | |
| ObjectValues::value_type defaultValue(actualKey, null); | |
| it = value_.map_->insert(it, defaultValue); | |
| Value& value = (*it).second; | |
| return value; | |
| #else | |
| return value_.map_->resolveReference(key, isStatic); | |
| #endif | |
| } | |
| Value Value::get(ArrayIndex index, const Value& defaultValue) const { | |
| const Value* value = &((*this)[index]); | |
| return value == &null ? defaultValue : *value; | |
| } | |
| bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } | |
| const Value& Value::operator[](const char* key) const { | |
| JSON_ASSERT_MESSAGE( | |
| type_ == nullValue || type_ == objectValue, | |
| "in Json::Value::operator[](char const*)const: requires objectValue"); | |
| if (type_ == nullValue) | |
| return null; | |
| #ifndef JSON_VALUE_USE_INTERNAL_MAP | |
| CZString actualKey(key, CZString::noDuplication); | |
| ObjectValues::const_iterator it = value_.map_->find(actualKey); | |
| if (it == value_.map_->end()) | |
| return null; | |
| return (*it).second; | |
| #else | |
| const Value* value = value_.map_->find(key); | |
| return value ? *value : null; | |
| #endif | |
| } | |
| Value& Value::operator[](const std::string& key) { | |
| return (*this)[key.c_str()]; | |
| } | |
| const Value& Value::operator[](const std::string& key) const { | |
| return (*this)[key.c_str()]; | |
| } | |
| Value& Value::operator[](const StaticString& key) { | |
| return resolveReference(key, true); | |
| } | |
| #ifdef JSON_USE_CPPTL | |
| Value& Value::operator[](const CppTL::ConstString& key) { | |
| return (*this)[key.c_str()]; | |
| } | |
| const Value& Value::operator[](const CppTL::ConstString& key) const { | |
| return (*this)[key.c_str()]; | |
| } | |
| #endif | |
| Value& Value::append(const Value& value) { return (*this)[size()] = value; } | |
| Value Value::get(const char* key, const Value& defaultValue) const { | |
| const Value* value = &((*this)[key]); | |
| return value == &null ? defaultValue : *value; | |
| } | |
| Value Value::get(const std::string& key, const Value& defaultValue) const { | |
| return get(key.c_str(), defaultValue); | |
| } | |
| Value Value::removeMember(const char* key) { | |
| JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, | |
| "in Json::Value::removeMember(): requires objectValue"); | |
| if (type_ == nullValue) | |
| return null; | |
| #ifndef JSON_VALUE_USE_INTERNAL_MAP | |
| CZString actualKey(key, CZString::noDuplication); | |
| ObjectValues::iterator it = value_.map_->find(actualKey); | |
| if (it == value_.map_->end()) | |
| return null; | |
| Value old(it->second); | |
| value_.map_->erase(it); | |
| return old; | |
| #else | |
| Value* value = value_.map_->find(key); | |
| if (value) { | |
| Value old(*value); | |
| value_.map_.remove(key); | |
| return old; | |
| } else { | |
| return null; | |
| } | |
| #endif | |
| } | |
| Value Value::removeMember(const std::string& key) { | |
| return removeMember(key.c_str()); | |
| } | |
| #ifdef JSON_USE_CPPTL | |
| Value Value::get(const CppTL::ConstString& key, | |
| const Value& defaultValue) const { | |
| return get(key.c_str(), defaultValue); | |
| } | |
| #endif | |
| bool Value::isMember(const char* key) const { | |
| const Value* value = &((*this)[key]); | |
| return value != &null; | |
| } | |
| bool Value::isMember(const std::string& key) const { | |
| return isMember(key.c_str()); | |
| } | |
| #ifdef JSON_USE_CPPTL | |
| bool Value::isMember(const CppTL::ConstString& key) const { | |
| return isMember(key.c_str()); | |
| } | |
| #endif | |
| Value::Members Value::getMemberNames() const { | |
| JSON_ASSERT_MESSAGE( | |
| type_ == nullValue || type_ == objectValue, | |
| "in Json::Value::getMemberNames(), value must be objectValue"); | |
| if (type_ == nullValue) | |
| return Value::Members(); | |
| Members members; | |
| members.reserve(value_.map_->size()); | |
| #ifndef JSON_VALUE_USE_INTERNAL_MAP | |
| ObjectValues::const_iterator it = value_.map_->begin(); | |
| ObjectValues::const_iterator itEnd = value_.map_->end(); | |
| for (; it != itEnd; ++it) | |
| members.push_back(std::string((*it).first.c_str())); | |
| #else | |
| ValueInternalMap::IteratorState it; | |
| ValueInternalMap::IteratorState itEnd; | |
| value_.map_->makeBeginIterator(it); | |
| value_.map_->makeEndIterator(itEnd); | |
| for (; !ValueInternalMap::equals(it, itEnd); ValueInternalMap::increment(it)) | |
| members.push_back(std::string(ValueInternalMap::key(it))); | |
| #endif | |
| 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 type_ == nullValue; } | |
| bool Value::isBool() const { return type_ == booleanValue; } | |
| bool Value::isInt() const { | |
| switch (type_) { | |
| case intValue: | |
| return value_.int_ >= minInt && value_.int_ <= maxInt; | |
| case uintValue: | |
| return value_.uint_ <= UInt(maxInt); | |
| case realValue: | |
| return value_.real_ >= minInt && value_.real_ <= maxInt && | |
| IsIntegral(value_.real_); | |
| default: | |
| break; | |
| } | |
| return false; | |
| } | |
| bool Value::isUInt() const { | |
| switch (type_) { | |
| case intValue: | |
| return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); | |
| case uintValue: | |
| return value_.uint_ <= maxUInt; | |
| case realValue: | |
| return value_.real_ >= 0 && value_.real_ <= maxUInt && | |
| IsIntegral(value_.real_); | |
| default: | |
| break; | |
| } | |
| return false; | |
| } | |
| bool Value::isInt64() const { | |
| #if defined(JSON_HAS_INT64) | |
| switch (type_) { | |
| case intValue: | |
| return true; | |
| case uintValue: | |
| return 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 value_.real_ >= double(minInt64) && | |
| value_.real_ < double(maxInt64) && IsIntegral(value_.real_); | |
| default: | |
| break; | |
| } | |
| #endif // JSON_HAS_INT64 | |
| return false; | |
| } | |
| bool Value::isUInt64() const { | |
| #if defined(JSON_HAS_INT64) | |
| switch (type_) { | |
| case intValue: | |
| return 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 value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble && | |
| IsIntegral(value_.real_); | |
| default: | |
| break; | |
| } | |
| #endif // JSON_HAS_INT64 | |
| return false; | |
| } | |
| bool Value::isIntegral() const { | |
| #if defined(JSON_HAS_INT64) | |
| return isInt64() || isUInt64(); | |
| #else | |
| return isInt() || isUInt(); | |
| #endif | |
| } | |
| bool Value::isDouble() const { return type_ == realValue || isIntegral(); } | |
| bool Value::isNumeric() const { return isIntegral() || isDouble(); } | |
| bool Value::isString() const { return type_ == stringValue; } | |
| bool Value::isArray() const { return type_ == arrayValue; } | |
| bool Value::isObject() const { return type_ == objectValue; } | |
| void Value::setComment(const char* comment, CommentPlacement placement) { | |
| if (!comments_) | |
| comments_ = new CommentInfo[numberOfCommentPlacement]; | |
| comments_[placement].setComment(comment); | |
| } | |
| void Value::setComment(const std::string& comment, CommentPlacement placement) { | |
| setComment(comment.c_str(), placement); | |
| } | |
| bool Value::hasComment(CommentPlacement placement) const { | |
| return comments_ != 0 && comments_[placement].comment_ != 0; | |
| } | |
| std::string Value::getComment(CommentPlacement placement) const { | |
| if (hasComment(placement)) | |
| return comments_[placement].comment_; | |
| return ""; | |
| } | |
| void Value::setOffsetStart(size_t start) { start_ = start; } | |
| void Value::setOffsetLimit(size_t limit) { limit_ = limit; } | |
| size_t Value::getOffsetStart() const { return start_; } | |
| size_t Value::getOffsetLimit() const { return limit_; } | |
| std::string Value::toStyledString() const { | |
| StyledWriter writer; | |
| return writer.write(*this); | |
| } | |
| Value::const_iterator Value::begin() const { | |
| switch (type_) { | |
| #ifdef JSON_VALUE_USE_INTERNAL_MAP | |
| case arrayValue: | |
| if (value_.array_) { | |
| ValueInternalArray::IteratorState it; | |
| value_.array_->makeBeginIterator(it); | |
| return const_iterator(it); | |
| } | |
| break; | |
| case objectValue: | |
| if (value_.map_) { | |
| ValueInternalMap::IteratorState it; | |
| value_.map_->makeBeginIterator(it); | |
| return const_iterator(it); | |
| } | |
| break; | |
| #else | |
| case arrayValue: | |
| case objectValue: | |
| if (value_.map_) | |
| return const_iterator(value_.map_->begin()); | |
| break; | |
| #endif | |
| default: | |
| break; | |
| } | |
| return const_iterator(); | |
| } | |
| Value::const_iterator Value::end() const { | |
| switch (type_) { | |
| #ifdef JSON_VALUE_USE_INTERNAL_MAP | |
| case arrayValue: | |
| if (value_.array_) { | |
| ValueInternalArray::IteratorState it; | |
| value_.array_->makeEndIterator(it); | |
| return const_iterator(it); | |
| } | |
| break; | |
| case objectValue: | |
| if (value_.map_) { | |
| ValueInternalMap::IteratorState it; | |
| value_.map_->makeEndIterator(it); | |
| return const_iterator(it); | |
| } | |
| break; | |
| #else | |
| case arrayValue: | |
| case objectValue: | |
| if (value_.map_) | |
| return const_iterator(value_.map_->end()); | |
| break; | |
| #endif | |
| default: | |
| break; | |
| } | |
| return const_iterator(); | |
| } | |
| Value::iterator Value::begin() { | |
| switch (type_) { | |
| #ifdef JSON_VALUE_USE_INTERNAL_MAP | |
| case arrayValue: | |
| if (value_.array_) { | |
| ValueInternalArray::IteratorState it; | |
| value_.array_->makeBeginIterator(it); | |
| return iterator(it); | |
| } | |
| break; | |
| case objectValue: | |
| if (value_.map_) { | |
| ValueInternalMap::IteratorState it; | |
| value_.map_->makeBeginIterator(it); | |
| return iterator(it); | |
| } | |
| break; | |
| #else | |
| case arrayValue: | |
| case objectValue: | |
| if (value_.map_) | |
| return iterator(value_.map_->begin()); | |
| break; | |
| #endif | |
| default: | |
| break; | |
| } | |
| return iterator(); | |
| } | |
| Value::iterator Value::end() { | |
| switch (type_) { | |
| #ifdef JSON_VALUE_USE_INTERNAL_MAP | |
| case arrayValue: | |
| if (value_.array_) { | |
| ValueInternalArray::IteratorState it; | |
| value_.array_->makeEndIterator(it); | |
| return iterator(it); | |
| } | |
| break; | |
| case objectValue: | |
| if (value_.map_) { | |
| ValueInternalMap::IteratorState it; | |
| value_.map_->makeEndIterator(it); | |
| return iterator(it); | |
| } | |
| break; | |
| #else | |
| case arrayValue: | |
| case objectValue: | |
| if (value_.map_) | |
| return iterator(value_.map_->end()); | |
| break; | |
| #endif | |
| 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 std::string& key) | |
| : key_(key.c_str()), index_(), kind_(kindKey) {} | |
| // class Path | |
| // ////////////////////////////////////////////////////////////////// | |
| Path::Path(const std::string& path, | |
| const PathArgument& a1, | |
| const PathArgument& a2, | |
| const PathArgument& a3, | |
| const PathArgument& a4, | |
| const PathArgument& a5) { | |
| InArgs in; | |
| in.push_back(&a1); | |
| in.push_back(&a2); | |
| in.push_back(&a3); | |
| in.push_back(&a4); | |
| in.push_back(&a5); | |
| makePath(path, in); | |
| } | |
| void Path::makePath(const std::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 == '%') | |
| addPathInArg(path, in, itInArg, PathArgument::kindIndex); | |
| else { | |
| ArrayIndex index = 0; | |
| for (; current != end && *current >= '0' && *current <= '9'; ++current) | |
| index = index * 10 + ArrayIndex(*current - '0'); | |
| args_.push_back(index); | |
| } | |
| if (current == end || *current++ != ']') | |
| invalidPath(path, int(current - path.c_str())); | |
| } else if (*current == '%') { | |
| addPathInArg(path, in, itInArg, PathArgument::kindKey); | |
| ++current; | |
| } else if (*current == '.') { | |
| ++current; | |
| } else { | |
| const char* beginName = current; | |
| while (current != end && !strchr("[.", *current)) | |
| ++current; | |
| args_.push_back(std::string(beginName, current)); | |
| } | |
| } | |
| } | |
| void Path::addPathInArg(const std::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 { | |
| args_.push_back(**itInArg); | |
| } | |
| } | |
| void Path::invalidPath(const std::string& /*path*/, int /*location*/) { | |
| // Error: invalid path. | |
| } | |
| const Value& Path::resolve(const Value& root) const { | |
| const Value* node = &root; | |
| for (Args::const_iterator it = args_.begin(); it != 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... | |
| } | |
| node = &((*node)[arg.index_]); | |
| } else if (arg.kind_ == PathArgument::kindKey) { | |
| if (!node->isObject()) { | |
| // Error: unable to resolve path (object value expected at position...) | |
| } | |
| node = &((*node)[arg.key_]); | |
| if (node == &Value::null) { | |
| // Error: unable to resolve path (object has no member named '' at | |
| // position...) | |
| } | |
| } | |
| } | |
| return *node; | |
| } | |
| Value Path::resolve(const Value& root, const Value& defaultValue) const { | |
| const Value* node = &root; | |
| for (Args::const_iterator it = args_.begin(); it != 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::null) | |
| return defaultValue; | |
| } | |
| } | |
| return *node; | |
| } | |
| Value& Path::make(Value& root) const { | |
| Value* node = &root; | |
| for (Args::const_iterator it = args_.begin(); it != 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 |