#include "lcb-jsoncpp.h"
#ifndef JSON_IS_AMALGAMATION
#error "Compile with -I PATH_TO_JSON_DIRECTORY"
#endif
#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED
#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED
namespace Json {
static inline std::string codePointToUTF8(unsigned int cp) {
std::string result;
if (cp <= 0x7f) {
result.resize(1);
result[0] = static_cast<char>(cp);
} else if (cp <= 0x7FF) {
result.resize(2);
result[1] = static_cast<char>(0x80 | (0x3f & cp));
result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
} else if (cp <= 0xFFFF) {
result.resize(3);
result[2] = static_cast<char>(0x80 | (0x3f & cp));
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12)));
} else if (cp <= 0x10FFFF) {
result.resize(4);
result[3] = static_cast<char>(0x80 | (0x3f & cp));
result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
}
return result;
}
static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; }
enum {
uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1
};
typedef char UIntToStringBuffer[uintToStringBufferSize];
static inline void uintToString(LargestUInt value, char*& current) {
*--current = 0;
do {
*--current = static_cast<signed char>(value % 10U + static_cast<unsigned>('0'));
value /= 10;
} while (value != 0);
}
static inline void fixNumericLocale(char* begin, char* end) {
while (begin < end) {
if (*begin == ',') {
*begin = '.';
}
++begin;
}
}
}
#endif
#if !defined(JSON_IS_AMALGAMATION)
#include <json/assertions.h>
#include <json/reader.h>
#include <json/value.h>
#include "json_tool.h"
#endif #include <utility>
#include <cstdio>
#include <cassert>
#include <cstring>
#include <istream>
#include <sstream>
#include <memory>
#include <set>
#if defined(_MSC_VER) && _MSC_VER < 1500
#define snprintf _snprintf
#endif
#if defined(_MSC_VER) && _MSC_VER >= 1400
#pragma warning(disable : 4996)
#endif
static int const stackLimit_g = 1000;
static int stackDepth_g = 0;
namespace Json {
typedef std::auto_ptr<CharReader> CharReaderPtr;
Features::Features()
: allowComments_(true), strictRoot_(false)
{}
Features Features::all() { return Features(); }
Features Features::strictMode() {
Features features;
features.allowComments_ = false;
features.strictRoot_ = true;
return features;
}
static bool containsNewLine(Reader::Location begin, Reader::Location end) {
for (; begin < end; ++begin)
if (*begin == '\n' || *begin == '\r')
return true;
return false;
}
Reader::Reader()
: errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
lastValue_(), commentsBefore_(), features_(Features::all()),
collectComments_() {}
Reader::Reader(const Features& features)
: errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
lastValue_(), commentsBefore_(), features_(features), collectComments_() {
}
bool
Reader::parse(const std::string& document, Value& root, bool collectComments) {
document_ = document;
const char* begin = document_.c_str();
const char* end = begin + document_.length();
return parse(begin, end, root, collectComments);
}
bool Reader::parse(std::istream& sin, Value& root, bool collectComments) {
std::string doc;
std::getline(sin, doc, (char)EOF);
return parse(doc, root, collectComments);
}
bool Reader::parse(const char* beginDoc,
const char* endDoc,
Value& root,
bool collectComments) {
if (!features_.allowComments_) {
collectComments = false;
}
begin_ = beginDoc;
end_ = endDoc;
collectComments_ = collectComments;
current_ = begin_;
lastValueEnd_ = 0;
lastValue_ = 0;
commentsBefore_ = "";
errors_.clear();
while (!nodes_.empty())
nodes_.pop();
nodes_.push(&root);
stackDepth_g = 0; bool successful = readValue();
Token token;
skipCommentTokens(token);
if (collectComments_ && !commentsBefore_.empty())
root.setComment(commentsBefore_, commentAfter);
if (features_.strictRoot_) {
if (!root.isArray() && !root.isObject()) {
token.type_ = tokenError;
token.start_ = beginDoc;
token.end_ = endDoc;
addError(
"A valid JSON document must be either an array or an object value.",
token);
return false;
}
}
return successful;
}
bool Reader::readValue() {
if (stackDepth_g >= stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue().");
++stackDepth_g;
Token token;
skipCommentTokens(token);
bool successful = true;
if (collectComments_ && !commentsBefore_.empty()) {
currentValue().setComment(commentsBefore_, commentBefore);
commentsBefore_ = "";
}
switch (token.type_) {
case tokenObjectBegin:
successful = readObject(token);
break;
case tokenArrayBegin:
successful = readArray(token);
break;
case tokenNumber:
successful = decodeNumber(token);
break;
case tokenString:
successful = decodeString(token);
break;
case tokenTrue:
{
Value v(true);
currentValue().swapPayload(v);
}
break;
case tokenFalse:
{
Value v(false);
currentValue().swapPayload(v);
}
break;
case tokenNull:
{
Value v;
currentValue().swapPayload(v);
}
break;
default:
return addError("Syntax error: value, object or array expected.", token);
}
if (collectComments_) {
lastValueEnd_ = current_;
lastValue_ = ¤tValue();
}
--stackDepth_g;
return successful;
}
void Reader::skipCommentTokens(Token& token) {
if (features_.allowComments_) {
do {
readToken(token);
} while (token.type_ == tokenComment);
} else {
readToken(token);
}
}
bool Reader::readToken(Token& token) {
skipSpaces();
token.start_ = current_;
Char c = getNextChar();
bool ok = true;
switch (c) {
case '{':
token.type_ = tokenObjectBegin;
break;
case '}':
token.type_ = tokenObjectEnd;
break;
case '[':
token.type_ = tokenArrayBegin;
break;
case ']':
token.type_ = tokenArrayEnd;
break;
case '"':
token.type_ = tokenString;
ok = readString();
break;
case '/':
token.type_ = tokenComment;
ok = readComment();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
token.type_ = tokenNumber;
readNumber();
break;
case 't':
token.type_ = tokenTrue;
ok = match("rue", 3);
break;
case 'f':
token.type_ = tokenFalse;
ok = match("alse", 4);
break;
case 'n':
token.type_ = tokenNull;
ok = match("ull", 3);
break;
case ',':
token.type_ = tokenArraySeparator;
break;
case ':':
token.type_ = tokenMemberSeparator;
break;
case 0:
token.type_ = tokenEndOfStream;
break;
default:
ok = false;
break;
}
if (!ok)
token.type_ = tokenError;
token.end_ = current_;
return true;
}
void Reader::skipSpaces() {
while (current_ != end_) {
Char c = *current_;
if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
++current_;
else
break;
}
}
bool Reader::match(Location pattern, int patternLength) {
if (end_ - current_ < patternLength)
return false;
int index = patternLength;
while (index--)
if (current_[index] != pattern[index])
return false;
current_ += patternLength;
return true;
}
bool Reader::readComment() {
Location commentBegin = current_ - 1;
Char c = getNextChar();
bool successful = false;
if (c == '*')
successful = readCStyleComment();
else if (c == '/')
successful = readCppStyleComment();
if (!successful)
return false;
if (collectComments_) {
CommentPlacement placement = commentBefore;
if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
if (c != '*' || !containsNewLine(commentBegin, current_))
placement = commentAfterOnSameLine;
}
addComment(commentBegin, current_, placement);
}
return true;
}
static std::string normalizeEOL(Reader::Location begin, Reader::Location end) {
std::string normalized;
normalized.reserve(end - begin);
Reader::Location current = begin;
while (current != end) {
char c = *current++;
if (c == '\r') {
if (current != end && *current == '\n')
++current;
normalized += '\n';
} else {
normalized += c;
}
}
return normalized;
}
void
Reader::addComment(Location begin, Location end, CommentPlacement placement) {
assert(collectComments_);
const std::string& normalized = normalizeEOL(begin, end);
if (placement == commentAfterOnSameLine) {
assert(lastValue_ != 0);
lastValue_->setComment(normalized, placement);
} else {
commentsBefore_ += normalized;
}
}
bool Reader::readCStyleComment() {
while (current_ != end_) {
Char c = getNextChar();
if (c == '*' && *current_ == '/')
break;
}
return getNextChar() == '/';
}
bool Reader::readCppStyleComment() {
while (current_ != end_) {
Char c = getNextChar();
if (c == '\n')
break;
if (c == '\r') {
if (current_ != end_ && *current_ == '\n')
getNextChar();
break;
}
}
return true;
}
void Reader::readNumber() {
const char *p = current_;
char c = '0'; while (c >= '0' && c <= '9')
c = (current_ = p) < end_ ? *p++ : 0;
if (c == '.') {
c = (current_ = p) < end_ ? *p++ : 0;
while (c >= '0' && c <= '9')
c = (current_ = p) < end_ ? *p++ : 0;
}
if (c == 'e' || c == 'E') {
c = (current_ = p) < end_ ? *p++ : 0;
if (c == '+' || c == '-')
c = (current_ = p) < end_ ? *p++ : 0;
while (c >= '0' && c <= '9')
c = (current_ = p) < end_ ? *p++ : 0;
}
}
bool Reader::readString() {
Char c = 0;
while (current_ != end_) {
c = getNextChar();
if (c == '\\')
getNextChar();
else if (c == '"')
break;
}
return c == '"';
}
bool Reader::readObject(Token& ) {
Token tokenName;
std::string name;
Value init(objectValue);
currentValue().swapPayload(init);
while (readToken(tokenName)) {
bool initialTokenOk = true;
while (tokenName.type_ == tokenComment && initialTokenOk)
initialTokenOk = readToken(tokenName);
if (!initialTokenOk)
break;
if (tokenName.type_ == tokenObjectEnd && name.empty()) return true;
name = "";
if (tokenName.type_ == tokenString) {
if (!decodeString(tokenName, name))
return recoverFromError(tokenObjectEnd);
} else {
break;
}
Token colon;
if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
return addErrorAndRecover(
"Missing ':' after object member name", colon, tokenObjectEnd);
}
Value& value = currentValue()[name];
nodes_.push(&value);
bool ok = readValue();
nodes_.pop();
if (!ok) return recoverFromError(tokenObjectEnd);
Token comma;
if (!readToken(comma) ||
(comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
comma.type_ != tokenComment)) {
return addErrorAndRecover(
"Missing ',' or '}' in object declaration", comma, tokenObjectEnd);
}
bool finalizeTokenOk = true;
while (comma.type_ == tokenComment && finalizeTokenOk)
finalizeTokenOk = readToken(comma);
if (comma.type_ == tokenObjectEnd)
return true;
}
return addErrorAndRecover(
"Missing '}' or object member name", tokenName, tokenObjectEnd);
}
bool Reader::readArray(Token& ) {
Value init(arrayValue);
currentValue().swapPayload(init);
skipSpaces();
if (*current_ == ']') {
Token endArray;
readToken(endArray);
return true;
}
int index = 0;
for (;;) {
Value& value = currentValue()[index++];
nodes_.push(&value);
bool ok = readValue();
nodes_.pop();
if (!ok) return recoverFromError(tokenArrayEnd);
Token token;
ok = readToken(token);
while (token.type_ == tokenComment && ok) {
ok = readToken(token);
}
bool badTokenType =
(token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);
if (!ok || badTokenType) {
return addErrorAndRecover(
"Missing ',' or ']' in array declaration", token, tokenArrayEnd);
}
if (token.type_ == tokenArrayEnd)
break;
}
return true;
}
bool Reader::decodeNumber(Token& token) {
Value decoded;
if (!decodeNumber(token, decoded))
return false;
currentValue().swapPayload(decoded);
return true;
}
bool Reader::decodeNumber(Token& token, Value& decoded) {
Location current = token.start_;
bool isNegative = *current == '-';
if (isNegative)
++current;
Value::LargestUInt maxIntegerValue =
isNegative ? Value::LargestUInt(-Value::minLargestInt)
: Value::maxLargestUInt;
Value::LargestUInt threshold = maxIntegerValue / 10;
Value::LargestUInt value = 0;
while (current < token.end_) {
Char c = *current++;
if (c < '0' || c > '9')
return decodeDouble(token, decoded);
Value::UInt digit(c - '0');
if (value >= threshold) {
if (value > threshold || current != token.end_ ||
digit > maxIntegerValue % 10) {
return decodeDouble(token, decoded);
}
}
value = value * 10 + digit;
}
if (isNegative)
decoded = -Value::LargestInt(value);
else if (value <= Value::LargestUInt(Value::maxInt))
decoded = Value::LargestInt(value);
else
decoded = value;
return true;
}
bool Reader::decodeDouble(Token& token) {
Value decoded;
if (!decodeDouble(token, decoded))
return false;
currentValue().swapPayload(decoded);
return true;
}
bool Reader::decodeDouble(Token& token, Value& decoded) {
double value = 0;
std::string buffer(token.start_, token.end_);
std::istringstream is(buffer);
if (!(is >> value))
return addError("'" + std::string(token.start_, token.end_) +
"' is not a number.",
token);
decoded = value;
return true;
}
bool Reader::decodeString(Token& token) {
std::string decoded_string;
if (!decodeString(token, decoded_string))
return false;
Value decoded(decoded_string);
currentValue().swapPayload(decoded);
return true;
}
bool Reader::decodeString(Token& token, std::string& decoded) {
decoded.reserve(token.end_ - token.start_ - 2);
Location current = token.start_ + 1; Location end = token.end_ - 1; while (current != end) {
Char c = *current++;
if (c == '"')
break;
else if (c == '\\') {
if (current == end)
return addError("Empty escape sequence in string", token, current);
Char escape = *current++;
switch (escape) {
case '"':
decoded += '"';
break;
case '/':
decoded += '/';
break;
case '\\':
decoded += '\\';
break;
case 'b':
decoded += '\b';
break;
case 'f':
decoded += '\f';
break;
case 'n':
decoded += '\n';
break;
case 'r':
decoded += '\r';
break;
case 't':
decoded += '\t';
break;
case 'u': {
unsigned int unicode;
if (!decodeUnicodeCodePoint(token, current, end, unicode))
return false;
decoded += codePointToUTF8(unicode);
} break;
default:
return addError("Bad escape sequence in string", token, current);
}
} else {
decoded += c;
}
}
return true;
}
bool Reader::decodeUnicodeCodePoint(Token& token,
Location& current,
Location end,
unsigned int& unicode) {
if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
return false;
if (unicode >= 0xD800 && unicode <= 0xDBFF) {
if (end - current < 6)
return addError(
"additional six characters expected to parse unicode surrogate pair.",
token,
current);
unsigned int surrogatePair;
if (*(current++) == '\\' && *(current++) == 'u') {
if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
} else
return false;
} else
return addError("expecting another \\u token to begin the second half of "
"a unicode surrogate pair",
token,
current);
}
return true;
}
bool Reader::decodeUnicodeEscapeSequence(Token& token,
Location& current,
Location end,
unsigned int& unicode) {
if (end - current < 4)
return addError(
"Bad unicode escape sequence in string: four digits expected.",
token,
current);
unicode = 0;
for (int index = 0; index < 4; ++index) {
Char c = *current++;
unicode *= 16;
if (c >= '0' && c <= '9')
unicode += c - '0';
else if (c >= 'a' && c <= 'f')
unicode += c - 'a' + 10;
else if (c >= 'A' && c <= 'F')
unicode += c - 'A' + 10;
else
return addError(
"Bad unicode escape sequence in string: hexadecimal digit expected.",
token,
current);
}
return true;
}
bool
Reader::addError(const std::string& message, Token& token, Location extra) {
ErrorInfo info;
info.token_ = token;
info.message_ = message;
info.extra_ = extra;
errors_.push_back(info);
return false;
}
bool Reader::recoverFromError(TokenType skipUntilToken) {
int errorCount = int(errors_.size());
Token skip;
for (;;) {
if (!readToken(skip))
errors_.resize(errorCount); if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
break;
}
errors_.resize(errorCount);
return false;
}
bool Reader::addErrorAndRecover(const std::string& message,
Token& token,
TokenType skipUntilToken) {
addError(message, token);
return recoverFromError(skipUntilToken);
}
Value& Reader::currentValue() { return *(nodes_.top()); }
Reader::Char Reader::getNextChar() {
if (current_ == end_)
return 0;
return *current_++;
}
void Reader::getLocationLineAndColumn(Location location,
int& line,
int& column) const {
Location current = begin_;
Location lastLineStart = current;
line = 0;
while (current < location && current != end_) {
Char c = *current++;
if (c == '\r') {
if (*current == '\n')
++current;
lastLineStart = current;
++line;
} else if (c == '\n') {
lastLineStart = current;
++line;
}
}
column = int(location - lastLineStart) + 1;
++line;
}
std::string Reader::getLocationLineAndColumn(Location location) const {
int line, column;
getLocationLineAndColumn(location, line, column);
char buffer[18 + 16 + 16 + 1];
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
#if defined(WINCE)
_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
#else
sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
#endif
#else
snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
#endif
return buffer;
}
std::string Reader::getFormatedErrorMessages() const {
return getFormattedErrorMessages();
}
std::string Reader::getFormattedErrorMessages() const {
std::string formattedMessage;
for (Errors::const_iterator itError = errors_.begin();
itError != errors_.end();
++itError) {
const ErrorInfo& error = *itError;
formattedMessage +=
"* " + getLocationLineAndColumn(error.token_.start_) + "\n";
formattedMessage += " " + error.message_ + "\n";
if (error.extra_)
formattedMessage +=
"See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
}
return formattedMessage;
}
class OurFeatures {
public:
static OurFeatures all();
OurFeatures();
bool allowComments_;
bool strictRoot_;
bool allowDroppedNullPlaceholders_;
bool allowNumericKeys_;
bool allowSingleQuotes_;
bool failIfExtra_;
bool rejectDupKeys_;
int stackLimit_;
};
OurFeatures::OurFeatures()
: allowComments_(true), strictRoot_(false)
, allowDroppedNullPlaceholders_(false), allowNumericKeys_(false)
, allowSingleQuotes_(false)
, failIfExtra_(false)
{
}
OurFeatures OurFeatures::all() { return OurFeatures(); }
class OurReader {
public:
typedef char Char;
typedef const Char* Location;
struct StructuredError {
size_t offset_start;
size_t offset_limit;
std::string message;
};
OurReader(OurFeatures const& features);
bool parse(const char* beginDoc,
const char* endDoc,
Value& root,
bool collectComments = true);
std::string getFormattedErrorMessages() const;
private:
OurReader(OurReader const&); void operator=(OurReader const&);
enum TokenType {
tokenEndOfStream = 0,
tokenObjectBegin,
tokenObjectEnd,
tokenArrayBegin,
tokenArrayEnd,
tokenString,
tokenNumber,
tokenTrue,
tokenFalse,
tokenNull,
tokenArraySeparator,
tokenMemberSeparator,
tokenComment,
tokenError
};
class Token {
public:
TokenType type_;
Location start_;
Location end_;
};
class ErrorInfo {
public:
Token token_;
std::string message_;
Location extra_;
};
typedef std::deque<ErrorInfo> Errors;
bool readToken(Token& token);
void skipSpaces();
bool match(Location pattern, int patternLength);
bool readComment();
bool readCStyleComment();
bool readCppStyleComment();
bool readString();
bool readStringSingleQuote();
void readNumber();
bool readValue();
bool readObject(Token& token);
bool readArray(Token& token);
bool decodeNumber(Token& token);
bool decodeNumber(Token& token, Value& decoded);
bool decodeString(Token& token);
bool decodeString(Token& token, std::string& decoded);
bool decodeDouble(Token& token);
bool decodeDouble(Token& token, Value& decoded);
bool decodeUnicodeCodePoint(Token& token,
Location& current,
Location end,
unsigned int& unicode);
bool decodeUnicodeEscapeSequence(Token& token,
Location& current,
Location end,
unsigned int& unicode);
bool addError(const std::string& message, Token& token, Location extra = 0);
bool recoverFromError(TokenType skipUntilToken);
bool addErrorAndRecover(const std::string& message,
Token& token,
TokenType skipUntilToken);
void skipUntilSpace();
Value& currentValue();
Char getNextChar();
void
getLocationLineAndColumn(Location location, int& line, int& column) const;
std::string getLocationLineAndColumn(Location location) const;
void addComment(Location begin, Location end, CommentPlacement placement);
void skipCommentTokens(Token& token);
typedef std::stack<Value*> Nodes;
Nodes nodes_;
Errors errors_;
std::string document_;
Location begin_;
Location end_;
Location current_;
Location lastValueEnd_;
Value* lastValue_;
std::string commentsBefore_;
int stackDepth_;
OurFeatures const features_;
bool collectComments_;
};
OurReader::OurReader(OurFeatures const& features)
: errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
lastValue_(), commentsBefore_(), features_(features), collectComments_() {
}
bool OurReader::parse(const char* beginDoc,
const char* endDoc,
Value& root,
bool collectComments) {
if (!features_.allowComments_) {
collectComments = false;
}
begin_ = beginDoc;
end_ = endDoc;
collectComments_ = collectComments;
current_ = begin_;
lastValueEnd_ = 0;
lastValue_ = 0;
commentsBefore_ = "";
errors_.clear();
while (!nodes_.empty())
nodes_.pop();
nodes_.push(&root);
stackDepth_ = 0;
bool successful = readValue();
Token token;
skipCommentTokens(token);
if (features_.failIfExtra_) {
if (token.type_ != tokenError && token.type_ != tokenEndOfStream) {
addError("Extra non-whitespace after JSON value.", token);
return false;
}
}
if (collectComments_ && !commentsBefore_.empty())
root.setComment(commentsBefore_, commentAfter);
if (features_.strictRoot_) {
if (!root.isArray() && !root.isObject()) {
token.type_ = tokenError;
token.start_ = beginDoc;
token.end_ = endDoc;
addError(
"A valid JSON document must be either an array or an object value.",
token);
return false;
}
}
return successful;
}
bool OurReader::readValue() {
if (stackDepth_ >= features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue().");
++stackDepth_;
Token token;
skipCommentTokens(token);
bool successful = true;
if (collectComments_ && !commentsBefore_.empty()) {
currentValue().setComment(commentsBefore_, commentBefore);
commentsBefore_ = "";
}
switch (token.type_) {
case tokenObjectBegin:
successful = readObject(token);
break;
case tokenArrayBegin:
successful = readArray(token);
break;
case tokenNumber:
successful = decodeNumber(token);
break;
case tokenString:
successful = decodeString(token);
break;
case tokenTrue:
{
Value v(true);
currentValue().swapPayload(v);
}
break;
case tokenFalse:
{
Value v(false);
currentValue().swapPayload(v);
}
break;
case tokenNull:
{
Value v;
currentValue().swapPayload(v);
}
break;
case tokenArraySeparator:
case tokenObjectEnd:
case tokenArrayEnd:
if (features_.allowDroppedNullPlaceholders_) {
current_--;
Value v;
currentValue().swapPayload(v);
break;
} default:
return addError("Syntax error: value, object or array expected.", token);
}
if (collectComments_) {
lastValueEnd_ = current_;
lastValue_ = ¤tValue();
}
--stackDepth_;
return successful;
}
void OurReader::skipCommentTokens(Token& token) {
if (features_.allowComments_) {
do {
readToken(token);
} while (token.type_ == tokenComment);
} else {
readToken(token);
}
}
bool OurReader::readToken(Token& token) {
skipSpaces();
token.start_ = current_;
Char c = getNextChar();
bool ok = true;
switch (c) {
case '{':
token.type_ = tokenObjectBegin;
break;
case '}':
token.type_ = tokenObjectEnd;
break;
case '[':
token.type_ = tokenArrayBegin;
break;
case ']':
token.type_ = tokenArrayEnd;
break;
case '"':
token.type_ = tokenString;
ok = readString();
break;
case '\'':
if (features_.allowSingleQuotes_) {
token.type_ = tokenString;
ok = readStringSingleQuote();
break;
} case '/':
token.type_ = tokenComment;
ok = readComment();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
token.type_ = tokenNumber;
readNumber();
break;
case 't':
token.type_ = tokenTrue;
ok = match("rue", 3);
break;
case 'f':
token.type_ = tokenFalse;
ok = match("alse", 4);
break;
case 'n':
token.type_ = tokenNull;
ok = match("ull", 3);
break;
case ',':
token.type_ = tokenArraySeparator;
break;
case ':':
token.type_ = tokenMemberSeparator;
break;
case 0:
token.type_ = tokenEndOfStream;
break;
default:
ok = false;
break;
}
if (!ok)
token.type_ = tokenError;
token.end_ = current_;
return true;
}
void OurReader::skipSpaces() {
while (current_ != end_) {
Char c = *current_;
if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
++current_;
else
break;
}
}
bool OurReader::match(Location pattern, int patternLength) {
if (end_ - current_ < patternLength)
return false;
int index = patternLength;
while (index--)
if (current_[index] != pattern[index])
return false;
current_ += patternLength;
return true;
}
bool OurReader::readComment() {
Location commentBegin = current_ - 1;
Char c = getNextChar();
bool successful = false;
if (c == '*')
successful = readCStyleComment();
else if (c == '/')
successful = readCppStyleComment();
if (!successful)
return false;
if (collectComments_) {
CommentPlacement placement = commentBefore;
if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
if (c != '*' || !containsNewLine(commentBegin, current_))
placement = commentAfterOnSameLine;
}
addComment(commentBegin, current_, placement);
}
return true;
}
void
OurReader::addComment(Location begin, Location end, CommentPlacement placement) {
assert(collectComments_);
const std::string& normalized = normalizeEOL(begin, end);
if (placement == commentAfterOnSameLine) {
assert(lastValue_ != 0);
lastValue_->setComment(normalized, placement);
} else {
commentsBefore_ += normalized;
}
}
bool OurReader::readCStyleComment() {
while (current_ != end_) {
Char c = getNextChar();
if (c == '*' && *current_ == '/')
break;
}
return getNextChar() == '/';
}
bool OurReader::readCppStyleComment() {
while (current_ != end_) {
Char c = getNextChar();
if (c == '\n')
break;
if (c == '\r') {
if (current_ != end_ && *current_ == '\n')
getNextChar();
break;
}
}
return true;
}
void OurReader::readNumber() {
const char *p = current_;
char c = '0'; while (c >= '0' && c <= '9')
c = (current_ = p) < end_ ? *p++ : 0;
if (c == '.') {
c = (current_ = p) < end_ ? *p++ : 0;
while (c >= '0' && c <= '9')
c = (current_ = p) < end_ ? *p++ : 0;
}
if (c == 'e' || c == 'E') {
c = (current_ = p) < end_ ? *p++ : 0;
if (c == '+' || c == '-')
c = (current_ = p) < end_ ? *p++ : 0;
while (c >= '0' && c <= '9')
c = (current_ = p) < end_ ? *p++ : 0;
}
}
bool OurReader::readString() {
Char c = 0;
while (current_ != end_) {
c = getNextChar();
if (c == '\\')
getNextChar();
else if (c == '"')
break;
}
return c == '"';
}
bool OurReader::readStringSingleQuote() {
Char c = 0;
while (current_ != end_) {
c = getNextChar();
if (c == '\\')
getNextChar();
else if (c == '\'')
break;
}
return c == '\'';
}
bool OurReader::readObject(Token& ) {
Token tokenName;
std::string name;
Value init(objectValue);
currentValue().swapPayload(init);
while (readToken(tokenName)) {
bool initialTokenOk = true;
while (tokenName.type_ == tokenComment && initialTokenOk)
initialTokenOk = readToken(tokenName);
if (!initialTokenOk)
break;
if (tokenName.type_ == tokenObjectEnd && name.empty()) return true;
name = "";
if (tokenName.type_ == tokenString) {
if (!decodeString(tokenName, name))
return recoverFromError(tokenObjectEnd);
} else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
Value numberName;
if (!decodeNumber(tokenName, numberName))
return recoverFromError(tokenObjectEnd);
name = numberName.asString();
} else {
break;
}
Token colon;
if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
return addErrorAndRecover(
"Missing ':' after object member name", colon, tokenObjectEnd);
}
if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30");
if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
std::string msg = "Duplicate key: '" + name + "'";
return addErrorAndRecover(
msg, tokenName, tokenObjectEnd);
}
Value& value = currentValue()[name];
nodes_.push(&value);
bool ok = readValue();
nodes_.pop();
if (!ok) return recoverFromError(tokenObjectEnd);
Token comma;
if (!readToken(comma) ||
(comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
comma.type_ != tokenComment)) {
return addErrorAndRecover(
"Missing ',' or '}' in object declaration", comma, tokenObjectEnd);
}
bool finalizeTokenOk = true;
while (comma.type_ == tokenComment && finalizeTokenOk)
finalizeTokenOk = readToken(comma);
if (comma.type_ == tokenObjectEnd)
return true;
}
return addErrorAndRecover(
"Missing '}' or object member name", tokenName, tokenObjectEnd);
}
bool OurReader::readArray(Token& ) {
Value init(arrayValue);
currentValue().swapPayload(init);
skipSpaces();
if (*current_ == ']') {
Token endArray;
readToken(endArray);
return true;
}
int index = 0;
for (;;) {
Value& value = currentValue()[index++];
nodes_.push(&value);
bool ok = readValue();
nodes_.pop();
if (!ok) return recoverFromError(tokenArrayEnd);
Token token;
ok = readToken(token);
while (token.type_ == tokenComment && ok) {
ok = readToken(token);
}
bool badTokenType =
(token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);
if (!ok || badTokenType) {
return addErrorAndRecover(
"Missing ',' or ']' in array declaration", token, tokenArrayEnd);
}
if (token.type_ == tokenArrayEnd)
break;
}
return true;
}
bool OurReader::decodeNumber(Token& token) {
Value decoded;
if (!decodeNumber(token, decoded))
return false;
currentValue().swapPayload(decoded);
return true;
}
bool OurReader::decodeNumber(Token& token, Value& decoded) {
Location current = token.start_;
bool isNegative = *current == '-';
if (isNegative)
++current;
Value::LargestUInt maxIntegerValue =
isNegative ? Value::LargestUInt(-Value::minLargestInt)
: Value::maxLargestUInt;
Value::LargestUInt threshold = maxIntegerValue / 10;
Value::LargestUInt value = 0;
while (current < token.end_) {
Char c = *current++;
if (c < '0' || c > '9')
return decodeDouble(token, decoded);
Value::UInt digit(c - '0');
if (value >= threshold) {
if (value > threshold || current != token.end_ ||
digit > maxIntegerValue % 10) {
return decodeDouble(token, decoded);
}
}
value = value * 10 + digit;
}
if (isNegative)
decoded = -Value::LargestInt(value);
else if (value <= Value::LargestUInt(Value::maxInt))
decoded = Value::LargestInt(value);
else
decoded = value;
return true;
}
bool OurReader::decodeDouble(Token& token) {
Value decoded;
if (!decodeDouble(token, decoded))
return false;
currentValue().swapPayload(decoded);
return true;
}
bool OurReader::decodeDouble(Token& token, Value& decoded) {
double value = 0;
const int bufferSize = 32;
int count;
int length = int(token.end_ - token.start_);
if (length < 0) {
return addError("Unable to parse token length", token);
}
char format[] = "%lf";
if (length <= bufferSize) {
Char buffer[bufferSize + 1];
memcpy(buffer, token.start_, length);
buffer[length] = 0;
count = sscanf(buffer, format, &value);
} else {
std::string buffer(token.start_, token.end_);
count = sscanf(buffer.c_str(), format, &value);
}
if (count != 1)
return addError("'" + std::string(token.start_, token.end_) +
"' is not a number.",
token);
decoded = value;
return true;
}
bool OurReader::decodeString(Token& token) {
std::string decoded_string;
if (!decodeString(token, decoded_string))
return false;
Value decoded(decoded_string);
currentValue().swapPayload(decoded);
return true;
}
bool OurReader::decodeString(Token& token, std::string& decoded) {
decoded.reserve(token.end_ - token.start_ - 2);
Location current = token.start_ + 1; Location end = token.end_ - 1; while (current != end) {
Char c = *current++;
if (c == '"')
break;
else if (c == '\\') {
if (current == end)
return addError("Empty escape sequence in string", token, current);
Char escape = *current++;
switch (escape) {
case '"':
decoded += '"';
break;
case '/':
decoded += '/';
break;
case '\\':
decoded += '\\';
break;
case 'b':
decoded += '\b';
break;
case 'f':
decoded += '\f';
break;
case 'n':
decoded += '\n';
break;
case 'r':
decoded += '\r';
break;
case 't':
decoded += '\t';
break;
case 'u': {
unsigned int unicode;
if (!decodeUnicodeCodePoint(token, current, end, unicode))
return false;
decoded += codePointToUTF8(unicode);
} break;
default:
return addError("Bad escape sequence in string", token, current);
}
} else {
decoded += c;
}
}
return true;
}
bool OurReader::decodeUnicodeCodePoint(Token& token,
Location& current,
Location end,
unsigned int& unicode) {
if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
return false;
if (unicode >= 0xD800 && unicode <= 0xDBFF) {
if (end - current < 6)
return addError(
"additional six characters expected to parse unicode surrogate pair.",
token,
current);
unsigned int surrogatePair;
if (*(current++) == '\\' && *(current++) == 'u') {
if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
} else
return false;
} else
return addError("expecting another \\u token to begin the second half of "
"a unicode surrogate pair",
token,
current);
}
return true;
}
bool OurReader::decodeUnicodeEscapeSequence(Token& token,
Location& current,
Location end,
unsigned int& unicode) {
if (end - current < 4)
return addError(
"Bad unicode escape sequence in string: four digits expected.",
token,
current);
unicode = 0;
for (int index = 0; index < 4; ++index) {
Char c = *current++;
unicode *= 16;
if (c >= '0' && c <= '9')
unicode += c - '0';
else if (c >= 'a' && c <= 'f')
unicode += c - 'a' + 10;
else if (c >= 'A' && c <= 'F')
unicode += c - 'A' + 10;
else
return addError(
"Bad unicode escape sequence in string: hexadecimal digit expected.",
token,
current);
}
return true;
}
bool
OurReader::addError(const std::string& message, Token& token, Location extra) {
ErrorInfo info;
info.token_ = token;
info.message_ = message;
info.extra_ = extra;
errors_.push_back(info);
return false;
}
bool OurReader::recoverFromError(TokenType skipUntilToken) {
int errorCount = int(errors_.size());
Token skip;
for (;;) {
if (!readToken(skip))
errors_.resize(errorCount); if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
break;
}
errors_.resize(errorCount);
return false;
}
bool OurReader::addErrorAndRecover(const std::string& message,
Token& token,
TokenType skipUntilToken) {
addError(message, token);
return recoverFromError(skipUntilToken);
}
Value& OurReader::currentValue() { return *(nodes_.top()); }
OurReader::Char OurReader::getNextChar() {
if (current_ == end_)
return 0;
return *current_++;
}
void OurReader::getLocationLineAndColumn(Location location,
int& line,
int& column) const {
Location current = begin_;
Location lastLineStart = current;
line = 0;
while (current < location && current != end_) {
Char c = *current++;
if (c == '\r') {
if (*current == '\n')
++current;
lastLineStart = current;
++line;
} else if (c == '\n') {
lastLineStart = current;
++line;
}
}
column = int(location - lastLineStart) + 1;
++line;
}
std::string OurReader::getLocationLineAndColumn(Location location) const {
int line, column;
getLocationLineAndColumn(location, line, column);
char buffer[18 + 16 + 16 + 1];
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
#if defined(WINCE)
_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
#else
sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
#endif
#else
snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
#endif
return buffer;
}
std::string OurReader::getFormattedErrorMessages() const {
std::string formattedMessage;
for (Errors::const_iterator itError = errors_.begin();
itError != errors_.end();
++itError) {
const ErrorInfo& error = *itError;
formattedMessage +=
"* " + getLocationLineAndColumn(error.token_.start_) + "\n";
formattedMessage += " " + error.message_ + "\n";
if (error.extra_)
formattedMessage +=
"See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
}
return formattedMessage;
}
class OurCharReader : public CharReader {
bool const collectComments_;
OurReader reader_;
public:
OurCharReader(
bool collectComments,
OurFeatures const& features)
: collectComments_(collectComments)
, reader_(features)
{}
virtual bool parse(
char const* beginDoc, char const* endDoc,
Value* root, std::string* errs) {
bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
if (errs) {
*errs = reader_.getFormattedErrorMessages();
}
return ok;
}
};
CharReaderBuilder::CharReaderBuilder()
{
setDefaults(&settings_);
}
CharReaderBuilder::~CharReaderBuilder()
{}
CharReader* CharReaderBuilder::newCharReader() const
{
bool collectComments = settings_["collectComments"].asBool();
OurFeatures features = OurFeatures::all();
features.allowComments_ = settings_["allowComments"].asBool();
features.strictRoot_ = settings_["strictRoot"].asBool();
features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool();
features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
features.stackLimit_ = settings_["stackLimit"].asInt();
features.failIfExtra_ = settings_["failIfExtra"].asBool();
features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
return new OurCharReader(collectComments, features);
}
static void getValidReaderKeys(std::set<std::string>* valid_keys)
{
valid_keys->clear();
valid_keys->insert("collectComments");
valid_keys->insert("allowComments");
valid_keys->insert("strictRoot");
valid_keys->insert("allowDroppedNullPlaceholders");
valid_keys->insert("allowNumericKeys");
valid_keys->insert("allowSingleQuotes");
valid_keys->insert("stackLimit");
valid_keys->insert("failIfExtra");
valid_keys->insert("rejectDupKeys");
}
bool CharReaderBuilder::validate(Json::Value* invalid) const
{
Json::Value my_invalid;
if (!invalid) invalid = &my_invalid; Json::Value& inv = *invalid;
std::set<std::string> valid_keys;
getValidReaderKeys(&valid_keys);
Value::Members keys = settings_.getMemberNames();
size_t n = keys.size();
for (size_t i = 0; i < n; ++i) {
std::string const& key = keys[i];
if (valid_keys.find(key) == valid_keys.end()) {
inv[key] = settings_[key];
}
}
return 0u == inv.size();
}
Value& CharReaderBuilder::operator[](std::string key)
{
return settings_[key];
}
void CharReaderBuilder::strictMode(Json::Value* settings)
{
(*settings)["allowComments"] = false;
(*settings)["strictRoot"] = true;
(*settings)["allowDroppedNullPlaceholders"] = false;
(*settings)["allowNumericKeys"] = false;
(*settings)["allowSingleQuotes"] = false;
(*settings)["failIfExtra"] = true;
(*settings)["rejectDupKeys"] = true;
}
void CharReaderBuilder::setDefaults(Json::Value* settings)
{
(*settings)["collectComments"] = true;
(*settings)["allowComments"] = true;
(*settings)["strictRoot"] = false;
(*settings)["allowDroppedNullPlaceholders"] = false;
(*settings)["allowNumericKeys"] = false;
(*settings)["allowSingleQuotes"] = false;
(*settings)["stackLimit"] = 1000;
(*settings)["failIfExtra"] = false;
(*settings)["rejectDupKeys"] = false;
}
bool parseFromStream(
CharReader::Factory const& fact, std::istream& sin,
Value* root, std::string* errs)
{
std::ostringstream ssin;
ssin << sin.rdbuf();
std::string doc = ssin.str();
char const* begin = doc.data();
char const* end = begin + doc.size();
CharReaderPtr const reader(fact.newCharReader());
return reader->parse(begin, end, root, errs);
}
std::istream& operator>>(std::istream& sin, Value& root) {
CharReaderBuilder b;
std::string errs;
bool ok = parseFromStream(b, sin, &root, &errs);
if (!ok) {
fprintf(stderr,
"Error from reader: %s",
errs.c_str());
throwRuntimeError("reader error");
}
return sin;
}
}
namespace Json {
ValueIteratorBase::ValueIteratorBase()
: current_(), isNull_(true) {
}
ValueIteratorBase::ValueIteratorBase(
const Value::ObjectValues::iterator& current)
: current_(current), isNull_(false) {}
Value& ValueIteratorBase::deref() const {
return current_->second;
}
void ValueIteratorBase::increment() {
++current_;
}
void ValueIteratorBase::decrement() {
--current_;
}
ValueIteratorBase::difference_type
ValueIteratorBase::computeDistance(const SelfType& other) const {
#ifdef JSON_USE_CPPTL_SMALLMAP
return other.current_ - current_;
#else
if (isNull_ && other.isNull_) {
return 0;
}
difference_type myDistance = 0;
for (Value::ObjectValues::iterator it = current_; it != other.current_;
++it) {
++myDistance;
}
return myDistance;
#endif
}
bool ValueIteratorBase::isEqual(const SelfType& other) const {
if (isNull_) {
return other.isNull_;
}
return current_ == other.current_;
}
void ValueIteratorBase::copy(const SelfType& other) {
current_ = other.current_;
isNull_ = other.isNull_;
}
Value ValueIteratorBase::key() const {
const Value::CZString czstring = (*current_).first;
if (czstring.data()) {
if (czstring.isStaticString())
return Value(StaticString(czstring.data()));
return Value(czstring.data(), czstring.data() + czstring.length());
}
return Value(czstring.index());
}
UInt ValueIteratorBase::index() const {
const Value::CZString czstring = (*current_).first;
if (!czstring.data())
return czstring.index();
return Value::UInt(-1);
}
std::string ValueIteratorBase::name() const {
char const* keey;
char const* end;
keey = memberName(&end);
if (!keey) return std::string();
return std::string(keey, end);
}
char const* ValueIteratorBase::memberName() const {
const char* cname = (*current_).first.data();
return cname ? cname : "";
}
char const* ValueIteratorBase::memberName(char const** end) const {
const char* cname = (*current_).first.data();
if (!cname) {
*end = NULL;
return NULL;
}
*end = cname + (*current_).first.length();
return cname;
}
ValueConstIterator::ValueConstIterator() {}
ValueConstIterator::ValueConstIterator(
const Value::ObjectValues::iterator& current)
: ValueIteratorBase(current) {}
ValueConstIterator& ValueConstIterator::
operator=(const ValueIteratorBase& other) {
copy(other);
return *this;
}
ValueIterator::ValueIterator() {}
ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current)
: ValueIteratorBase(current) {}
ValueIterator::ValueIterator(const ValueConstIterator& other)
: ValueIteratorBase(other) {}
ValueIterator::ValueIterator(const ValueIterator& other)
: ValueIteratorBase(other) {}
ValueIterator& ValueIterator::operator=(const SelfType& other) {
copy(other);
return *this;
}
}
#if !defined(JSON_IS_AMALGAMATION)
#include <json/assertions.h>
#include <json/value.h>
#include <json/writer.h>
#endif #include <math.h>
#include <sstream>
#include <utility>
#include <cstring>
#include <cassert>
#ifdef JSON_USE_CPPTL
#include <cpptl/conststring.h>
#endif
#include <cstddef>
#include <algorithm>
#define JSON_ASSERT_UNREACHABLE assert(false)
namespace Json {
#if defined(__ARMEL__)
#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
#else
const Value Value::null;
#define ALIGNAS(byte_alignment)
#endif
static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 };
const unsigned char& kNullRef = kNull[0];
const Value& Value::nullRef = 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);
static const double maxUInt64AsDouble = 18446744073709551615.0;
#endif 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) {
return d >= min && d <= max;
}
#else
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
static inline char* duplicateStringValue(const char* value,
size_t length) {
if (length >= (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;
}
static inline char* duplicateAndPrefixStringValue(
const char* value,
unsigned int length)
{
JSON_ASSERT_MESSAGE(length <= (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; 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);
}
}
static inline void releaseStringValue(char* value) { free(value); }
}
#if !defined(JSON_IS_AMALGAMATION)
#include "json_valueiterator.inl"
#endif
namespace Json {
Exception::Exception(std::string const& msg)
: msg_(msg)
{}
Exception::~Exception() throw()
{}
char const* Exception::what() const throw()
{
return msg_.c_str();
}
RuntimeError::RuntimeError(std::string const& msg)
: Exception(msg)
{}
LogicError::LogicError(std::string const& msg)
: Exception(msg)
{}
void throwRuntimeError(std::string const& msg)
{
assert(false);
}
void throwLogicError(std::string const& msg)
{
assert(false);
}
Value::CommentInfo::CommentInfo() : comment_(0) {}
Value::CommentInfo::~CommentInfo() {
if (comment_)
releaseStringValue(comment_);
}
void Value::CommentInfo::setComment(const char* text, size_t len) {
if (comment_) {
releaseStringValue(comment_);
comment_ = 0;
}
JSON_ASSERT(text != 0);
JSON_ASSERT_MESSAGE(
text[0] == '\0' || text[0] == '/',
"in Json::Value::setComment(): Comments must start with /");
comment_ = duplicateStringValue(text, len);
}
Value::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {}
Value::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate)
: cstr_(str)
{
storage_.policy_ = allocate & 0x3;
storage_.length_ = ulength & 0x3FFFFFFF;
}
Value::CZString::CZString(const CZString& other)
: cstr_(other.storage_.policy_ != noDuplication && other.cstr_ != 0
? duplicateStringValue(other.cstr_, other.storage_.length_)
: other.cstr_)
{
storage_.policy_ = (other.cstr_
? (static_cast<DuplicationPolicy>(other.storage_.policy_) == noDuplication
? noDuplication : duplicate)
: static_cast<DuplicationPolicy>(other.storage_.policy_));
storage_.length_ = other.storage_.length_;
}
Value::CZString::~CZString() {
if (cstr_ && storage_.policy_ == 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 index_ < other.index_;
unsigned this_len = this->storage_.length_;
unsigned other_len = other.storage_.length_;
unsigned min_len = std::min(this_len, other_len);
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 (!cstr_) return index_ == other.index_;
unsigned this_len = this->storage_.length_;
unsigned other_len = other.storage_.length_;
if (this_len != other_len) return false;
int comp = memcmp(this->cstr_, other.cstr_, this_len);
return comp == 0;
}
ArrayIndex Value::CZString::index() const { return index_; }
const char* Value::CZString::data() const { return cstr_; }
unsigned Value::CZString::length() const { return storage_.length_; }
bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; }
Value::Value(ValueType vtype) {
initBasic(vtype);
switch (vtype) {
case nullValue:
break;
case intValue:
case uintValue:
value_.int_ = 0;
break;
case realValue:
value_.real_ = 0.0;
break;
case stringValue:
value_.string_ = 0;
break;
case arrayValue:
case objectValue:
value_.map_ = new ObjectValues();
break;
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
Value::Value(double value) {
initBasic(realValue);
value_.real_ = value;
}
Value::Value(const char* value) {
initBasic(stringValue, true);
value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(strlen(value)));
}
Value::Value(const char* beginValue, const char* endValue) {
initBasic(stringValue, true);
value_.string_ =
duplicateAndPrefixStringValue(beginValue, static_cast<unsigned>(endValue - beginValue));
}
Value::Value(const std::string& value) {
initBasic(stringValue, true);
value_.string_ =
duplicateAndPrefixStringValue(value.data(), static_cast<unsigned>(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_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(value.length()));
}
#endif
Value::Value(bool value) {
initBasic(booleanValue);
value_.bool_ = value;
}
Value::Value(Value const& other)
: type_(other.type_), allocated_(false)
,
comments_(0)
{
switch (type_) {
case nullValue:
case intValue:
case uintValue:
case realValue:
case booleanValue:
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);
value_.string_ = duplicateAndPrefixStringValue(str, len);
allocated_ = true;
} else {
value_.string_ = other.value_.string_;
allocated_ = false;
}
break;
case arrayValue:
case objectValue:
value_.map_ = new ObjectValues(*other.value_.map_);
break;
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_, strlen(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;
case arrayValue:
case objectValue:
delete value_.map_;
break;
default:
JSON_ASSERT_UNREACHABLE;
}
if (comments_)
delete[] comments_;
}
Value &Value::operator=(const Value &other) {
Value temp(other);
swap(temp);
return *this;
}
void Value::swapPayload(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 & 0x1;
}
void Value::swap(Value& other) {
swapPayload(other);
std::swap(comments_, other.comments_);
}
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:
{
if ((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(this_len, other_len);
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(value_.map_->size() - other.value_.map_->size());
if (delta)
return delta < 0;
return (*value_.map_) < (*other.value_.map_);
}
default:
JSON_ASSERT_UNREACHABLE;
}
return false; }
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 {
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:
{
if ((value_.string_ == 0) || (other.value_.string_ == 0)) {
return (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;
int comp = memcmp(this_str, other_str, this_len);
return comp == 0;
}
case arrayValue:
case objectValue:
return value_.map_->size() == other.value_.map_->size() &&
(*value_.map_) == (*other.value_.map_);
default:
JSON_ASSERT_UNREACHABLE;
}
return false; }
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 (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;
}
bool Value::getString(char const** str, char const** cend) const {
if (type_ != stringValue) return false;
if (value_.string_ == 0) return false;
unsigned length;
decodePrefixedString(this->allocated_, this->value_.string_, &length, str);
*cend = *str + length;
return true;
}
std::string Value::asString() const {
switch (type_) {
case nullValue:
return "";
case stringValue:
{
if (value_.string_ == 0) return "";
unsigned this_len;
char const* this_str;
decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
return std::string(this_str, this_len);
}
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 {
unsigned len;
char const* str;
decodePrefixedString(allocated_, value_.string_,
&len, &str);
return CppTL::ConstString(str, len);
}
#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
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
return integerToDouble(value_.uint_);
#endif 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
return integerToDouble(value_.uint_);
#endif 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_ != 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 (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;
}
ArrayIndex Value::size() const {
switch (type_) {
case nullValue:
case intValue:
case uintValue:
case realValue:
case booleanValue:
case stringValue:
return 0;
case arrayValue: 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());
}
JSON_ASSERT_UNREACHABLE;
return 0; }
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");
switch (type_) {
case arrayValue:
case objectValue:
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 (type_ == nullValue)
*this = Value(arrayValue);
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);
}
}
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);
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, nullRef);
it = 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 (type_ == nullValue)
return nullRef;
CZString key(index);
ObjectValues::const_iterator it = value_.map_->find(key);
if (it == value_.map_->end())
return nullRef;
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) {
type_ = vtype;
allocated_ = allocated;
comments_ = 0;
}
Value& Value::resolveReference(const char* key) {
JSON_ASSERT_MESSAGE(
type_ == nullValue || type_ == objectValue,
"in Json::Value::resolveReference(): requires objectValue");
if (type_ == nullValue)
*this = Value(objectValue);
CZString actualKey(
key, static_cast<unsigned>(strlen(key)), CZString::noDuplication); ObjectValues::iterator it = value_.map_->lower_bound(actualKey);
if (it != value_.map_->end() && (*it).first == actualKey)
return (*it).second;
ObjectValues::value_type defaultValue(actualKey, nullRef);
it = value_.map_->insert(it, defaultValue);
Value& value = (*it).second;
return value;
}
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 (type_ == nullValue)
*this = Value(objectValue);
CZString actualKey(
key, static_cast<unsigned>(cend-key), 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, nullRef);
it = 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 == &nullRef ? defaultValue : *value;
}
bool Value::isValidIndex(ArrayIndex index) const { return index < 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 (type_ == nullValue) return NULL;
CZString actualKey(key, static_cast<unsigned>(cend-key), CZString::noDuplication);
ObjectValues::const_iterator it = value_.map_->find(actualKey);
if (it == value_.map_->end()) return NULL;
return &(*it).second;
}
const Value& Value::operator[](const char* key) const
{
Value const* found = find(key, key + strlen(key));
if (!found) return nullRef;
return *found;
}
Value const& Value::operator[](std::string const& key) const
{
Value const* found = find(key.data(), key.data() + key.length());
if (!found) return nullRef;
return *found;
}
Value& Value::operator[](const char* key) {
return resolveReference(key, key + strlen(key));
}
Value& Value::operator[](const std::string& key) {
return resolveReference(key.data(), key.data() + key.length());
}
Value& Value::operator[](const StaticString& key) {
return 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 nullRef;
return *found;
}
#endif
Value& Value::append(const Value& value) { return (*this)[size()] = value; }
Value Value::get(char const* key, char const* cend, Value const& defaultValue) const
{
Value const* found = find(key, cend);
return !found ? defaultValue : *found;
}
Value Value::get(char const* key, Value const& defaultValue) const
{
return get(key, key + strlen(key), defaultValue);
}
Value Value::get(std::string const& key, Value const& defaultValue) const
{
return get(key.data(), key.data() + key.length(), defaultValue);
}
bool Value::removeMember(const char* key, const char* cend, Value* removed)
{
if (type_ != objectValue) {
return false;
}
CZString actualKey(key, static_cast<unsigned>(cend-key), CZString::noDuplication);
ObjectValues::iterator it = value_.map_->find(actualKey);
if (it == value_.map_->end())
return false;
*removed = it->second;
value_.map_->erase(it);
return true;
}
bool Value::removeMember(const char* key, Value* removed)
{
return removeMember(key, key + strlen(key), removed);
}
bool Value::removeMember(std::string const& key, Value* removed)
{
return 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 (type_ == nullValue)
return nullRef;
Value removed; removeMember(key, key + strlen(key), &removed);
return removed; }
Value Value::removeMember(const std::string& key)
{
return removeMember(key.c_str());
}
bool Value::removeIndex(ArrayIndex index, Value* removed) {
if (type_ != arrayValue) {
return false;
}
CZString key(index);
ObjectValues::iterator it = value_.map_->find(key);
if (it == value_.map_->end()) {
return false;
}
*removed = it->second;
ArrayIndex oldSize = size();
for (ArrayIndex i = index; i < (oldSize - 1); ++i){
CZString keey(i);
(*value_.map_)[keey] = (*this)[i + 1];
}
CZString keyLast(oldSize - 1);
ObjectValues::iterator itLast = value_.map_->find(keyLast);
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 = find(key, cend);
return NULL != value;
}
bool Value::isMember(char const* key) const
{
return isMember(key, key + strlen(key));
}
bool Value::isMember(std::string const& key) const
{
return 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 (type_ == nullValue)
return Value::Members();
Members members;
members.reserve(value_.map_->size());
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.data(),
(*it).first.length()));
}
return members;
}
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:
return value_.real_ >= double(minInt64) &&
value_.real_ < double(maxInt64) && IsIntegral(value_.real_);
default:
break;
}
#endif 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:
return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble &&
IsIntegral(value_.real_);
default:
break;
}
#endif 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, size_t len, CommentPlacement placement) {
if (!comments_)
comments_ = new CommentInfo[numberOfCommentPlacement];
if ((len > 0) && (comment[len-1] == '\n')) {
len -= 1;
}
comments_[placement].setComment(comment, len);
}
void Value::setComment(const char* comment, CommentPlacement placement) {
setComment(comment, strlen(comment), placement);
}
void Value::setComment(const std::string& comment, CommentPlacement placement) {
setComment(comment.c_str(), comment.length(), 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 "";
}
std::string Value::toStyledString() const {
StyledWriter writer;
return writer.write(*this);
}
Value::const_iterator Value::begin() const {
switch (type_) {
case arrayValue:
case objectValue:
if (value_.map_)
return const_iterator(value_.map_->begin());
break;
default:
break;
}
return const_iterator();
}
Value::const_iterator Value::end() const {
switch (type_) {
case arrayValue:
case objectValue:
if (value_.map_)
return const_iterator(value_.map_->end());
break;
default:
break;
}
return const_iterator();
}
Value::iterator Value::begin() {
switch (type_) {
case arrayValue:
case objectValue:
if (value_.map_)
return iterator(value_.map_->begin());
break;
default:
break;
}
return iterator();
}
Value::iterator Value::end() {
switch (type_) {
case arrayValue:
case objectValue:
if (value_.map_)
return iterator(value_.map_->end());
break;
default:
break;
}
return iterator();
}
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) {}
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& ,
const InArgs& in,
InArgs::const_iterator& itInArg,
PathArgument::Kind kind) {
if (itInArg == in.end()) {
} else if ((*itInArg)->kind_ != kind) {
} else {
args_.push_back(**itInArg);
}
}
void Path::invalidPath(const std::string& , int ) {
}
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_)) {
}
node = &((*node)[arg.index_]);
} else if (arg.kind_ == PathArgument::kindKey) {
if (!node->isObject()) {
}
node = &((*node)[arg.key_]);
if (node == &Value::nullRef) {
}
}
}
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::nullRef)
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()) {
}
node = &((*node)[arg.index_]);
} else if (arg.kind_ == PathArgument::kindKey) {
if (!node->isObject()) {
}
node = &((*node)[arg.key_]);
}
}
return *node;
}
}
#if !defined(JSON_IS_AMALGAMATION)
#include <json/writer.h>
#include "json_tool.h"
#endif #include <iomanip>
#include <memory>
#include <sstream>
#include <utility>
#include <set>
#include <cassert>
#include <cstring>
#include <cstdio>
#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800
#include <float.h>
#define isfinite _finite
#elif defined(__sun) && defined(__SVR4)
#include <ieeefp.h>
#define isfinite finite
#else
#include <cmath>
#define isfinite std::isfinite
#endif
#if defined(_MSC_VER) && _MSC_VER < 1500
#define snprintf _snprintf
#elif defined(__ANDROID__)
#define snprintf snprintf
#elif __cplusplus >= 201103L
#define snprintf std::snprintf
#endif
#if defined(__BORLANDC__)
#include <float.h>
#define isfinite _finite
#define snprintf _snprintf
#endif
#if defined(_MSC_VER) && _MSC_VER >= 1400
#pragma warning(disable : 4996)
#endif
namespace Json {
typedef std::auto_ptr<StreamWriter> StreamWriterPtr;
static bool containsControlCharacter(const char* str) {
while (*str) {
if (isControlCharacter(*(str++)))
return true;
}
return false;
}
static bool containsControlCharacter0(const char* str, unsigned len) {
char const* end = str + len;
while (end != str) {
if (isControlCharacter(*str) || 0==*str)
return true;
++str;
}
return false;
}
std::string valueToString(LargestInt value) {
UIntToStringBuffer buffer;
char* current = buffer + sizeof(buffer);
bool isNegative = value < 0;
if (isNegative)
value = -value;
uintToString(LargestUInt(value), current);
if (isNegative)
*--current = '-';
assert(current >= buffer);
return current;
}
std::string valueToString(LargestUInt value) {
UIntToStringBuffer buffer;
char* current = buffer + sizeof(buffer);
uintToString(value, current);
assert(current >= buffer);
return current;
}
#if defined(JSON_HAS_INT64)
std::string valueToString(Int value) {
return valueToString(LargestInt(value));
}
std::string valueToString(UInt value) {
return valueToString(LargestUInt(value));
}
#endif
std::string valueToString(double value) {
char buffer[32];
int len = -1;
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
#if defined(WINCE)
len = _snprintf(buffer, sizeof(buffer), "%.17g", value);
#else
len = sprintf_s(buffer, sizeof(buffer), "%.17g", value);
#endif
#else
if (isfinite(value)) {
len = snprintf(buffer, sizeof(buffer), "%.17g", value);
} else {
if (value != value) {
len = snprintf(buffer, sizeof(buffer), "null");
} else if (value < 0) {
len = snprintf(buffer, sizeof(buffer), "-1e+9999");
} else {
len = snprintf(buffer, sizeof(buffer), "1e+9999");
}
}
#endif
assert(len >= 0);
fixNumericLocale(buffer, buffer + len);
return buffer;
}
std::string valueToString(bool value) { return value ? "true" : "false"; }
std::string valueToQuotedString(const char* value) {
if (value == NULL)
return "";
if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL &&
!containsControlCharacter(value))
return std::string("\"") + value + "\"";
std::string::size_type maxsize =
strlen(value) * 2 + 3; std::string result;
result.reserve(maxsize); result += "\"";
for (const char* c = value; *c != 0; ++c) {
switch (*c) {
case '\"':
result += "\\\"";
break;
case '\\':
result += "\\\\";
break;
case '\b':
result += "\\b";
break;
case '\f':
result += "\\f";
break;
case '\n':
result += "\\n";
break;
case '\r':
result += "\\r";
break;
case '\t':
result += "\\t";
break;
default:
if (isControlCharacter(*c)) {
std::ostringstream oss;
oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
<< std::setw(4) << static_cast<int>(*c);
result += oss.str();
} else {
result += *c;
}
break;
}
}
result += "\"";
return result;
}
static char const* strnpbrk(char const* s, char const* accept, size_t n) {
assert((s || !n) && accept);
char const* const end = s + n;
for (char const* cur = s; cur < end; ++cur) {
int const c = *cur;
for (char const* a = accept; *a; ++a) {
if (*a == c) {
return cur;
}
}
}
return NULL;
}
static std::string valueToQuotedStringN(const char* value, unsigned length) {
if (value == NULL)
return "";
if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL &&
!containsControlCharacter0(value, length))
return std::string("\"") + value + "\"";
std::string::size_type maxsize =
length * 2 + 3; std::string result;
result.reserve(maxsize); result += "\"";
char const* end = value + length;
for (const char* c = value; c != end; ++c) {
switch (*c) {
case '\"':
result += "\\\"";
break;
case '\\':
result += "\\\\";
break;
case '\b':
result += "\\b";
break;
case '\f':
result += "\\f";
break;
case '\n':
result += "\\n";
break;
case '\r':
result += "\\r";
break;
case '\t':
result += "\\t";
break;
default:
if ((isControlCharacter(*c)) || (*c == 0)) {
std::ostringstream oss;
oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
<< std::setw(4) << static_cast<int>(*c);
result += oss.str();
} else {
result += *c;
}
break;
}
}
result += "\"";
return result;
}
Writer::~Writer() {}
FastWriter::FastWriter()
: yamlCompatiblityEnabled_(false) {}
void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; }
std::string FastWriter::write(const Value& root) {
document_ = "";
writeValue(root);
document_ += "\n";
return document_;
}
void FastWriter::writeValue(const Value& value) {
switch (value.type()) {
case nullValue:
document_ += "null";
break;
case intValue:
document_ += valueToString(value.asLargestInt());
break;
case uintValue:
document_ += valueToString(value.asLargestUInt());
break;
case realValue:
document_ += valueToString(value.asDouble());
break;
case stringValue:
{
char const* str;
char const* end;
bool ok = value.getString(&str, &end);
if (ok) document_ += valueToQuotedStringN(str, static_cast<unsigned>(end-str));
break;
}
case booleanValue:
document_ += valueToString(value.asBool());
break;
case arrayValue: {
document_ += '[';
int size = value.size();
for (int index = 0; index < size; ++index) {
if (index > 0)
document_ += ',';
writeValue(value[index]);
}
document_ += ']';
} break;
case objectValue: {
Value::Members members(value.getMemberNames());
document_ += '{';
for (Value::Members::iterator it = members.begin(); it != members.end();
++it) {
const std::string& name = *it;
if (it != members.begin())
document_ += ',';
document_ += valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length()));
document_ += yamlCompatiblityEnabled_ ? ": " : ":";
writeValue(value[name]);
}
document_ += '}';
} break;
}
}
StyledWriter::StyledWriter()
: rightMargin_(74), indentSize_(3), addChildValues_() {}
std::string StyledWriter::write(const Value& root) {
document_ = "";
addChildValues_ = false;
indentString_ = "";
writeCommentBeforeValue(root);
writeValue(root);
writeCommentAfterValueOnSameLine(root);
document_ += "\n";
return document_;
}
void StyledWriter::writeValue(const Value& value) {
switch (value.type()) {
case nullValue:
pushValue("null");
break;
case intValue:
pushValue(valueToString(value.asLargestInt()));
break;
case uintValue:
pushValue(valueToString(value.asLargestUInt()));
break;
case realValue:
pushValue(valueToString(value.asDouble()));
break;
case stringValue:
{
char const* str;
char const* end;
bool ok = value.getString(&str, &end);
if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
else pushValue("");
break;
}
case booleanValue:
pushValue(valueToString(value.asBool()));
break;
case arrayValue:
writeArrayValue(value);
break;
case objectValue: {
Value::Members members(value.getMemberNames());
if (members.empty())
pushValue("{}");
else {
writeWithIndent("{");
indent();
Value::Members::iterator it = members.begin();
for (;;) {
const std::string& name = *it;
const Value& childValue = value[name];
writeCommentBeforeValue(childValue);
writeWithIndent(valueToQuotedString(name.c_str()));
document_ += " : ";
writeValue(childValue);
if (++it == members.end()) {
writeCommentAfterValueOnSameLine(childValue);
break;
}
document_ += ',';
writeCommentAfterValueOnSameLine(childValue);
}
unindent();
writeWithIndent("}");
}
} break;
}
}
void StyledWriter::writeArrayValue(const Value& value) {
unsigned size = value.size();
if (size == 0)
pushValue("[]");
else {
bool isArrayMultiLine = isMultineArray(value);
if (isArrayMultiLine) {
writeWithIndent("[");
indent();
bool hasChildValue = !childValues_.empty();
unsigned index = 0;
for (;;) {
const Value& childValue = value[index];
writeCommentBeforeValue(childValue);
if (hasChildValue)
writeWithIndent(childValues_[index]);
else {
writeIndent();
writeValue(childValue);
}
if (++index == size) {
writeCommentAfterValueOnSameLine(childValue);
break;
}
document_ += ',';
writeCommentAfterValueOnSameLine(childValue);
}
unindent();
writeWithIndent("]");
} else {
assert(childValues_.size() == size);
document_ += "[ ";
for (unsigned index = 0; index < size; ++index) {
if (index > 0)
document_ += ", ";
document_ += childValues_[index];
}
document_ += " ]";
}
}
}
bool StyledWriter::isMultineArray(const Value& value) {
int size = value.size();
bool isMultiLine = size * 3 >= rightMargin_;
childValues_.clear();
for (int index = 0; index < size && !isMultiLine; ++index) {
const Value& childValue = value[index];
isMultiLine =
isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
childValue.size() > 0);
}
if (!isMultiLine) {
childValues_.reserve(size);
addChildValues_ = true;
int lineLength = 4 + (size - 1) * 2; for (int index = 0; index < size; ++index) {
if (hasCommentForValue(value[index])) {
isMultiLine = true;
}
writeValue(value[index]);
lineLength += int(childValues_[index].length());
}
addChildValues_ = false;
isMultiLine = isMultiLine || lineLength >= rightMargin_;
}
return isMultiLine;
}
void StyledWriter::pushValue(const std::string& value) {
if (addChildValues_)
childValues_.push_back(value);
else
document_ += value;
}
void StyledWriter::writeIndent() {
if (!document_.empty()) {
char last = document_[document_.length() - 1];
if (last == ' ') return;
if (last != '\n') document_ += '\n';
}
document_ += indentString_;
}
void StyledWriter::writeWithIndent(const std::string& value) {
writeIndent();
document_ += value;
}
void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); }
void StyledWriter::unindent() {
assert(int(indentString_.size()) >= indentSize_);
indentString_.resize(indentString_.size() - indentSize_);
}
void StyledWriter::writeCommentBeforeValue(const Value& root) {
if (!root.hasComment(commentBefore))
return;
document_ += "\n";
writeIndent();
const std::string& comment = root.getComment(commentBefore);
std::string::const_iterator iter = comment.begin();
while (iter != comment.end()) {
document_ += *iter;
if (*iter == '\n' &&
(iter != comment.end() && *(iter + 1) == '/'))
writeIndent();
++iter;
}
document_ += "\n";
}
void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
if (root.hasComment(commentAfterOnSameLine))
document_ += " " + root.getComment(commentAfterOnSameLine);
if (root.hasComment(commentAfter)) {
document_ += "\n";
document_ += root.getComment(commentAfter);
document_ += "\n";
}
}
bool StyledWriter::hasCommentForValue(const Value& value) {
return value.hasComment(commentBefore) ||
value.hasComment(commentAfterOnSameLine) ||
value.hasComment(commentAfter);
}
StyledStreamWriter::StyledStreamWriter(std::string indentation)
: document_(NULL), rightMargin_(74), indentation_(indentation),
addChildValues_() {}
void StyledStreamWriter::write(std::ostream& out, const Value& root) {
document_ = &out;
addChildValues_ = false;
indentString_ = "";
indented_ = true;
writeCommentBeforeValue(root);
if (!indented_) writeIndent();
indented_ = true;
writeValue(root);
writeCommentAfterValueOnSameLine(root);
*document_ << "\n";
document_ = NULL; }
void StyledStreamWriter::writeValue(const Value& value) {
switch (value.type()) {
case nullValue:
pushValue("null");
break;
case intValue:
pushValue(valueToString(value.asLargestInt()));
break;
case uintValue:
pushValue(valueToString(value.asLargestUInt()));
break;
case realValue:
pushValue(valueToString(value.asDouble()));
break;
case stringValue:
{
char const* str;
char const* end;
bool ok = value.getString(&str, &end);
if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
else pushValue("");
break;
}
case booleanValue:
pushValue(valueToString(value.asBool()));
break;
case arrayValue:
writeArrayValue(value);
break;
case objectValue: {
Value::Members members(value.getMemberNames());
if (members.empty())
pushValue("{}");
else {
writeWithIndent("{");
indent();
Value::Members::iterator it = members.begin();
for (;;) {
const std::string& name = *it;
const Value& childValue = value[name];
writeCommentBeforeValue(childValue);
writeWithIndent(valueToQuotedString(name.c_str()));
*document_ << " : ";
writeValue(childValue);
if (++it == members.end()) {
writeCommentAfterValueOnSameLine(childValue);
break;
}
*document_ << ",";
writeCommentAfterValueOnSameLine(childValue);
}
unindent();
writeWithIndent("}");
}
} break;
}
}
void StyledStreamWriter::writeArrayValue(const Value& value) {
unsigned size = value.size();
if (size == 0)
pushValue("[]");
else {
bool isArrayMultiLine = isMultineArray(value);
if (isArrayMultiLine) {
writeWithIndent("[");
indent();
bool hasChildValue = !childValues_.empty();
unsigned index = 0;
for (;;) {
const Value& childValue = value[index];
writeCommentBeforeValue(childValue);
if (hasChildValue)
writeWithIndent(childValues_[index]);
else {
if (!indented_) writeIndent();
indented_ = true;
writeValue(childValue);
indented_ = false;
}
if (++index == size) {
writeCommentAfterValueOnSameLine(childValue);
break;
}
*document_ << ",";
writeCommentAfterValueOnSameLine(childValue);
}
unindent();
writeWithIndent("]");
} else {
assert(childValues_.size() == size);
*document_ << "[ ";
for (unsigned index = 0; index < size; ++index) {
if (index > 0)
*document_ << ", ";
*document_ << childValues_[index];
}
*document_ << " ]";
}
}
}
bool StyledStreamWriter::isMultineArray(const Value& value) {
int size = value.size();
bool isMultiLine = size * 3 >= rightMargin_;
childValues_.clear();
for (int index = 0; index < size && !isMultiLine; ++index) {
const Value& childValue = value[index];
isMultiLine =
isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
childValue.size() > 0);
}
if (!isMultiLine) {
childValues_.reserve(size);
addChildValues_ = true;
int lineLength = 4 + (size - 1) * 2; for (int index = 0; index < size; ++index) {
if (hasCommentForValue(value[index])) {
isMultiLine = true;
}
writeValue(value[index]);
lineLength += int(childValues_[index].length());
}
addChildValues_ = false;
isMultiLine = isMultiLine || lineLength >= rightMargin_;
}
return isMultiLine;
}
void StyledStreamWriter::pushValue(const std::string& value) {
if (addChildValues_)
childValues_.push_back(value);
else
*document_ << value;
}
void StyledStreamWriter::writeIndent() {
*document_ << '\n' << indentString_;
}
void StyledStreamWriter::writeWithIndent(const std::string& value) {
if (!indented_) writeIndent();
*document_ << value;
indented_ = false;
}
void StyledStreamWriter::indent() { indentString_ += indentation_; }
void StyledStreamWriter::unindent() {
assert(indentString_.size() >= indentation_.size());
indentString_.resize(indentString_.size() - indentation_.size());
}
void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
if (!root.hasComment(commentBefore))
return;
if (!indented_) writeIndent();
const std::string& comment = root.getComment(commentBefore);
std::string::const_iterator iter = comment.begin();
while (iter != comment.end()) {
*document_ << *iter;
if (*iter == '\n' &&
(iter != comment.end() && *(iter + 1) == '/'))
*document_ << indentString_;
++iter;
}
indented_ = false;
}
void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
if (root.hasComment(commentAfterOnSameLine))
*document_ << ' ' << root.getComment(commentAfterOnSameLine);
if (root.hasComment(commentAfter)) {
writeIndent();
*document_ << root.getComment(commentAfter);
}
indented_ = false;
}
bool StyledStreamWriter::hasCommentForValue(const Value& value) {
return value.hasComment(commentBefore) ||
value.hasComment(commentAfterOnSameLine) ||
value.hasComment(commentAfter);
}
struct CommentStyle {
enum Enum {
None, Most, All };
};
struct BuiltStyledStreamWriter : public StreamWriter
{
BuiltStyledStreamWriter(
std::string const& indentation,
CommentStyle::Enum cs,
std::string const& colonSymbol,
std::string const& nullSymbol,
std::string const& endingLineFeedSymbol);
virtual int write(Value const& root, std::ostream* sout);
private:
void writeValue(Value const& value);
void writeArrayValue(Value const& value);
bool isMultineArray(Value const& value);
void pushValue(std::string const& value);
void writeIndent();
void writeWithIndent(std::string const& value);
void indent();
void unindent();
void writeCommentBeforeValue(Value const& root);
void writeCommentAfterValueOnSameLine(Value const& root);
static bool hasCommentForValue(const Value& value);
typedef std::vector<std::string> ChildValues;
ChildValues childValues_;
std::string indentString_;
int rightMargin_;
std::string indentation_;
CommentStyle::Enum cs_;
std::string colonSymbol_;
std::string nullSymbol_;
std::string endingLineFeedSymbol_;
bool addChildValues_ : 1;
bool indented_ : 1;
};
BuiltStyledStreamWriter::BuiltStyledStreamWriter(
std::string const& indentation,
CommentStyle::Enum cs,
std::string const& colonSymbol,
std::string const& nullSymbol,
std::string const& endingLineFeedSymbol)
: rightMargin_(74)
, indentation_(indentation)
, cs_(cs)
, colonSymbol_(colonSymbol)
, nullSymbol_(nullSymbol)
, endingLineFeedSymbol_(endingLineFeedSymbol)
, addChildValues_(false)
, indented_(false)
{
}
int BuiltStyledStreamWriter::write(Value const& root, std::ostream* sout)
{
sout_ = sout;
addChildValues_ = false;
indented_ = true;
indentString_ = "";
writeCommentBeforeValue(root);
if (!indented_) writeIndent();
indented_ = true;
writeValue(root);
writeCommentAfterValueOnSameLine(root);
*sout_ << endingLineFeedSymbol_;
sout_ = NULL;
return 0;
}
void BuiltStyledStreamWriter::writeValue(Value const& value) {
switch (value.type()) {
case nullValue:
pushValue(nullSymbol_);
break;
case intValue:
pushValue(valueToString(value.asLargestInt()));
break;
case uintValue:
pushValue(valueToString(value.asLargestUInt()));
break;
case realValue:
pushValue(valueToString(value.asDouble()));
break;
case stringValue:
{
char const* str;
char const* end;
bool ok = value.getString(&str, &end);
if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
else pushValue("");
break;
}
case booleanValue:
pushValue(valueToString(value.asBool()));
break;
case arrayValue:
writeArrayValue(value);
break;
case objectValue: {
Value::Members members(value.getMemberNames());
if (members.empty())
pushValue("{}");
else {
writeWithIndent("{");
indent();
Value::Members::iterator it = members.begin();
for (;;) {
std::string const& name = *it;
Value const& childValue = value[name];
writeCommentBeforeValue(childValue);
writeWithIndent(valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length())));
*sout_ << colonSymbol_;
writeValue(childValue);
if (++it == members.end()) {
writeCommentAfterValueOnSameLine(childValue);
break;
}
*sout_ << ",";
writeCommentAfterValueOnSameLine(childValue);
}
unindent();
writeWithIndent("}");
}
} break;
}
}
void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
unsigned size = value.size();
if (size == 0)
pushValue("[]");
else {
bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value);
if (isMultiLine) {
writeWithIndent("[");
indent();
bool hasChildValue = !childValues_.empty();
unsigned index = 0;
for (;;) {
Value const& childValue = value[index];
writeCommentBeforeValue(childValue);
if (hasChildValue)
writeWithIndent(childValues_[index]);
else {
if (!indented_) writeIndent();
indented_ = true;
writeValue(childValue);
indented_ = false;
}
if (++index == size) {
writeCommentAfterValueOnSameLine(childValue);
break;
}
*sout_ << ",";
writeCommentAfterValueOnSameLine(childValue);
}
unindent();
writeWithIndent("]");
} else {
assert(childValues_.size() == size);
*sout_ << "[";
if (!indentation_.empty()) *sout_ << " ";
for (unsigned index = 0; index < size; ++index) {
if (index > 0)
*sout_ << ", ";
*sout_ << childValues_[index];
}
if (!indentation_.empty()) *sout_ << " ";
*sout_ << "]";
}
}
}
bool BuiltStyledStreamWriter::isMultineArray(Value const& value) {
int size = value.size();
bool isMultiLine = size * 3 >= rightMargin_;
childValues_.clear();
for (int index = 0; index < size && !isMultiLine; ++index) {
Value const& childValue = value[index];
isMultiLine =
isMultiLine || ((childValue.isArray() || childValue.isObject()) &&
childValue.size() > 0);
}
if (!isMultiLine) {
childValues_.reserve(size);
addChildValues_ = true;
int lineLength = 4 + (size - 1) * 2; for (int index = 0; index < size; ++index) {
if (hasCommentForValue(value[index])) {
isMultiLine = true;
}
writeValue(value[index]);
lineLength += int(childValues_[index].length());
}
addChildValues_ = false;
isMultiLine = isMultiLine || lineLength >= rightMargin_;
}
return isMultiLine;
}
void BuiltStyledStreamWriter::pushValue(std::string const& value) {
if (addChildValues_)
childValues_.push_back(value);
else
*sout_ << value;
}
void BuiltStyledStreamWriter::writeIndent() {
if (!indentation_.empty()) {
*sout_ << '\n' << indentString_;
}
}
void BuiltStyledStreamWriter::writeWithIndent(std::string const& value) {
if (!indented_) writeIndent();
*sout_ << value;
indented_ = false;
}
void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
void BuiltStyledStreamWriter::unindent() {
assert(indentString_.size() >= indentation_.size());
indentString_.resize(indentString_.size() - indentation_.size());
}
void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
if (cs_ == CommentStyle::None) return;
if (!root.hasComment(commentBefore))
return;
if (!indented_) writeIndent();
const std::string& comment = root.getComment(commentBefore);
std::string::const_iterator iter = comment.begin();
while (iter != comment.end()) {
*sout_ << *iter;
if (*iter == '\n' &&
(iter != comment.end() && *(iter + 1) == '/'))
*sout_ << indentString_;
++iter;
}
indented_ = false;
}
void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) {
if (cs_ == CommentStyle::None) return;
if (root.hasComment(commentAfterOnSameLine))
*sout_ << " " + root.getComment(commentAfterOnSameLine);
if (root.hasComment(commentAfter)) {
writeIndent();
*sout_ << root.getComment(commentAfter);
}
}
bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
return value.hasComment(commentBefore) ||
value.hasComment(commentAfterOnSameLine) ||
value.hasComment(commentAfter);
}
StreamWriter::StreamWriter()
: sout_(NULL)
{
}
StreamWriter::~StreamWriter()
{
}
StreamWriter::Factory::~Factory()
{}
StreamWriterBuilder::StreamWriterBuilder()
{
setDefaults(&settings_);
}
StreamWriterBuilder::~StreamWriterBuilder()
{}
StreamWriter* StreamWriterBuilder::newStreamWriter() const
{
std::string indentation = settings_["indentation"].asString();
std::string cs_str = settings_["commentStyle"].asString();
bool eyc = settings_["enableYAMLCompatibility"].asBool();
bool dnp = settings_["dropNullPlaceholders"].asBool();
CommentStyle::Enum cs = CommentStyle::All;
if (cs_str == "All") {
cs = CommentStyle::All;
} else if (cs_str == "None") {
cs = CommentStyle::None;
} else {
throwRuntimeError("commentStyle must be 'All' or 'None'");
}
std::string colonSymbol = " : ";
if (eyc) {
colonSymbol = ": ";
} else if (indentation.empty()) {
colonSymbol = ":";
}
std::string nullSymbol = "null";
if (dnp) {
nullSymbol = "";
}
std::string endingLineFeedSymbol = "";
return new BuiltStyledStreamWriter(
indentation, cs,
colonSymbol, nullSymbol, endingLineFeedSymbol);
}
static void getValidWriterKeys(std::set<std::string>* valid_keys)
{
valid_keys->clear();
valid_keys->insert("indentation");
valid_keys->insert("commentStyle");
valid_keys->insert("enableYAMLCompatibility");
valid_keys->insert("dropNullPlaceholders");
}
bool StreamWriterBuilder::validate(Json::Value* invalid) const
{
Json::Value my_invalid;
if (!invalid) invalid = &my_invalid; Json::Value& inv = *invalid;
std::set<std::string> valid_keys;
getValidWriterKeys(&valid_keys);
Value::Members keys = settings_.getMemberNames();
size_t n = keys.size();
for (size_t i = 0; i < n; ++i) {
std::string const& key = keys[i];
if (valid_keys.find(key) == valid_keys.end()) {
inv[key] = settings_[key];
}
}
return 0u == inv.size();
}
Value& StreamWriterBuilder::operator[](std::string key)
{
return settings_[key];
}
void StreamWriterBuilder::setDefaults(Json::Value* settings)
{
(*settings)["commentStyle"] = "All";
(*settings)["indentation"] = "\t";
(*settings)["enableYAMLCompatibility"] = false;
(*settings)["dropNullPlaceholders"] = false;
}
std::string writeString(StreamWriter::Factory const& builder, Value const& root) {
std::ostringstream sout;
StreamWriterPtr const writer(builder.newStreamWriter());
writer->write(root, &sout);
return sout.str();
}
std::ostream& operator<<(std::ostream& sout, Value const& root) {
StreamWriterBuilder builder;
StreamWriterPtr const writer(builder.newStreamWriter());
writer->write(root, &sout);
return sout;
}
}