json test

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers json_writer.cpp Source File

json_writer.cpp

00001 // Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
00002 // Distributed under MIT license, or public domain if desired and
00003 // recognized in your jurisdiction.
00004 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
00005 
00006 #if !defined(JSON_IS_AMALGAMATION)
00007 #include <json/writer.h>
00008 #include "json_tool.h"
00009 #endif // if !defined(JSON_IS_AMALGAMATION)
00010 #include <iomanip>
00011 #include <memory>
00012 #include <sstream>
00013 #include <utility>
00014 #include <set>
00015 #include <cassert>
00016 #include <cstring>
00017 #include <cstdio>
00018 
00019 #if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0
00020 #include <float.h>
00021 #define isfinite _finite
00022 #elif defined(__sun) && defined(__SVR4) //Solaris
00023 #if !defined(isfinite)
00024 #include <ieeefp.h>
00025 #define isfinite finite
00026 #endif
00027 #elif defined(_AIX)
00028 #if !defined(isfinite)
00029 #include <math.h>
00030 #define isfinite finite
00031 #endif
00032 #elif defined(__hpux)
00033 #if !defined(isfinite)
00034 #if defined(__ia64) && !defined(finite)
00035 #define isfinite(x) ((sizeof(x) == sizeof(float) ? \
00036                      _Isfinitef(x) : _IsFinite(x)))
00037 #else
00038 #include <math.h>
00039 #define isfinite finite
00040 #endif
00041 #endif
00042 #else
00043 #include <cmath>
00044 #if !(defined(__QNXNTO__)) // QNX already defines isfinite
00045 #define isfinite std::isfinite
00046 #endif
00047 #endif
00048 
00049 #if defined(_MSC_VER)
00050 #if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above
00051 #define snprintf sprintf_s
00052 #elif _MSC_VER >= 1900 // VC++ 14.0 and above
00053 #define snprintf std::snprintf
00054 #else
00055 #define snprintf _snprintf
00056 #endif
00057 #elif defined(__ANDROID__) || defined(__QNXNTO__)
00058 #define snprintf snprintf
00059 #elif __cplusplus >= 201103L
00060 #if !defined(__MINGW32__) && !defined(__CYGWIN__)
00061 #define snprintf std::snprintf
00062 #endif
00063 #endif
00064 
00065 #if defined(__BORLANDC__)  
00066 #include <float.h>
00067 #define isfinite _finite
00068 #define snprintf _snprintf
00069 #endif
00070 
00071 #if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
00072 // Disable warning about strdup being deprecated.
00073 #pragma warning(disable : 4996)
00074 #endif
00075 using namespace std;
00076 namespace Json {
00077 
00078 #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
00079 typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
00080 #else
00081 typedef std::auto_ptr<StreamWriter>   StreamWriterPtr;
00082 #endif
00083 
00084 JSONCPP_STRING valueToString(LargestInt value) {
00085   UIntToStringBuffer buffer;
00086   char* current = buffer + sizeof(buffer);
00087   if (value == Value::minLargestInt) {
00088     uintToString(LargestUInt(Value::maxLargestInt) + 1, current);
00089     *--current = '-';
00090   } else if (value < 0) {
00091     uintToString(LargestUInt(-value), current);
00092     *--current = '-';
00093   } else {
00094     uintToString(LargestUInt(value), current);
00095   }
00096   assert(current >= buffer);
00097   return current;
00098 }
00099 
00100 JSONCPP_STRING valueToString(LargestUInt value) {
00101   UIntToStringBuffer buffer;
00102   char* current = buffer + sizeof(buffer);
00103   uintToString(value, current);
00104   assert(current >= buffer);
00105   return current;
00106 }
00107 
00108 #if defined(JSON_HAS_INT64)
00109 
00110 JSONCPP_STRING valueToString(Int value) {
00111   return valueToString(LargestInt(value));
00112 }
00113 
00114 JSONCPP_STRING valueToString(UInt value) {
00115   return valueToString(LargestUInt(value));
00116 }
00117 
00118 #endif // # if defined(JSON_HAS_INT64)
00119 
00120 namespace {
00121 JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision) {
00122   // Allocate a buffer that is more than large enough to store the 16 digits of
00123   // precision requested below.
00124   char buffer[36];
00125   int len = -1;
00126 
00127   char formatString[15];
00128   snprintf(formatString, sizeof(formatString), "%%.%ug", precision);
00129 
00130   // Print into the buffer. We need not request the alternative representation
00131   // that always has a decimal point because JSON doesn't distinguish the
00132   // concepts of reals and integers.
00133   if (isfinite(value)) {
00134     len = snprintf(buffer, sizeof(buffer), formatString, value);
00135     fixNumericLocale(buffer, buffer + len);
00136 
00137     // try to ensure we preserve the fact that this was given to us as a double on input
00138     if (!strchr(buffer, '.') && !strchr(buffer, 'e')) {
00139       strcat(buffer, ".0");
00140     }
00141 
00142   } else {
00143     // IEEE standard states that NaN values will not compare to themselves
00144     if (value != value) {
00145       len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null");
00146     } else if (value < 0) {
00147       len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999");
00148     } else {
00149       len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "1e+9999");
00150     }
00151   }
00152   assert(len >= 0);
00153   return buffer;
00154 }
00155 }
00156 
00157 JSONCPP_STRING valueToString(double value) { return valueToString(value, false, 17); }
00158 
00159 JSONCPP_STRING valueToString(bool value) { return value ? "true" : "false"; }
00160 
00161 static bool isAnyCharRequiredQuoting(char const* s, size_t n) {
00162   assert(s || !n);
00163 
00164   char const* const end = s + n;
00165   for (char const* cur = s; cur < end; ++cur) {
00166     if (*cur == '\\' || *cur == '\"' || *cur < ' '
00167       || static_cast<unsigned char>(*cur) < 0x80)
00168       return true;
00169   }
00170   return false;
00171 }
00172 
00173 static unsigned int utf8ToCodepoint(const char*& s, const char* e) {
00174   const unsigned int REPLACEMENT_CHARACTER = 0xFFFD;
00175 
00176   unsigned int firstByte = static_cast<unsigned char>(*s);
00177 
00178   if (firstByte < 0x80)
00179     return firstByte;
00180 
00181   if (firstByte < 0xE0) {
00182     if (e - s < 2)
00183       return REPLACEMENT_CHARACTER;
00184 
00185     unsigned int calculated = ((firstByte & 0x1F) << 6)
00186       | (static_cast<unsigned int>(s[1]) & 0x3F);
00187     s += 1;
00188     // oversized encoded characters are invalid
00189     return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated;
00190   }
00191 
00192   if (firstByte < 0xF0) {
00193     if (e - s < 3)
00194       return REPLACEMENT_CHARACTER;
00195 
00196     unsigned int calculated = ((firstByte & 0x0F) << 12)
00197       | ((static_cast<unsigned int>(s[1]) & 0x3F) << 6)
00198       |  (static_cast<unsigned int>(s[2]) & 0x3F);
00199     s += 2;
00200     // surrogates aren't valid codepoints itself
00201     // shouldn't be UTF-8 encoded
00202     if (calculated >= 0xD800 && calculated <= 0xDFFF)
00203       return REPLACEMENT_CHARACTER;
00204     // oversized encoded characters are invalid
00205     return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated;
00206   }
00207 
00208   if (firstByte < 0xF8) {
00209     if (e - s < 4)
00210       return REPLACEMENT_CHARACTER;
00211 
00212     unsigned int calculated = ((firstByte & 0x07) << 24)
00213       | ((static_cast<unsigned int>(s[1]) & 0x3F) << 12)
00214       | ((static_cast<unsigned int>(s[2]) & 0x3F) << 6)
00215       |  (static_cast<unsigned int>(s[3]) & 0x3F);
00216     s += 3;
00217     // oversized encoded characters are invalid
00218     return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated;
00219   }
00220 
00221   return REPLACEMENT_CHARACTER;
00222 }
00223 
00224 static const char hex2[] =
00225   "000102030405060708090a0b0c0d0e0f"
00226   "101112131415161718191a1b1c1d1e1f"
00227   "202122232425262728292a2b2c2d2e2f"
00228   "303132333435363738393a3b3c3d3e3f"
00229   "404142434445464748494a4b4c4d4e4f"
00230   "505152535455565758595a5b5c5d5e5f"
00231   "606162636465666768696a6b6c6d6e6f"
00232   "707172737475767778797a7b7c7d7e7f"
00233   "808182838485868788898a8b8c8d8e8f"
00234   "909192939495969798999a9b9c9d9e9f"
00235   "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
00236   "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
00237   "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
00238   "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
00239   "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
00240   "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
00241 
00242 static JSONCPP_STRING toHex16Bit(unsigned int x) {
00243   const unsigned int hi = (x >> 8) & 0xff;
00244   const unsigned int lo = x & 0xff;
00245   JSONCPP_STRING result(4, ' ');
00246   result[0] = hex2[2 * hi];
00247   result[1] = hex2[2 * hi + 1];
00248   result[2] = hex2[2 * lo];
00249   result[3] = hex2[2 * lo + 1];
00250   return result;
00251 }
00252 
00253 static JSONCPP_STRING valueToQuotedStringN(const char* value, unsigned length) {
00254   if (value == NULL)
00255     return "";
00256 
00257   if (!isAnyCharRequiredQuoting(value, length))
00258     return JSONCPP_STRING("\"") + value + "\"";
00259   // We have to walk value and escape any special characters.
00260   // Appending to JSONCPP_STRING is not efficient, but this should be rare.
00261   // (Note: forward slashes are *not* rare, but I am not escaping them.)
00262   JSONCPP_STRING::size_type maxsize =
00263       length * 2 + 3; // allescaped+quotes+NULL
00264   JSONCPP_STRING result;
00265   result.reserve(maxsize); // to avoid lots of mallocs
00266   result += "\"";
00267   char const* end = value + length;
00268   for (const char* c = value; c != end; ++c) {
00269     switch (*c) {
00270     case '\"':
00271       result += "\\\"";
00272       break;
00273     case '\\':
00274       result += "\\\\";
00275       break;
00276     case '\b':
00277       result += "\\b";
00278       break;
00279     case '\f':
00280       result += "\\f";
00281       break;
00282     case '\n':
00283       result += "\\n";
00284       break;
00285     case '\r':
00286       result += "\\r";
00287       break;
00288     case '\t':
00289       result += "\\t";
00290       break;
00291     // case '/':
00292     // Even though \/ is considered a legal escape in JSON, a bare
00293     // slash is also legal, so I see no reason to escape it.
00294     // (I hope I am not misunderstanding something.)
00295     // blep notes: actually escaping \/ may be useful in javascript to avoid </
00296     // sequence.
00297     // Should add a flag to allow this compatibility mode and prevent this
00298     // sequence from occurring.
00299     default: {
00300         unsigned int cp = utf8ToCodepoint(c, end);
00301         // don't escape non-control characters
00302         // (short escape sequence are applied above)
00303         if (cp < 0x80 && cp >= 0x20)
00304           result += static_cast<char>(cp);
00305         else if (cp < 0x10000) { // codepoint is in Basic Multilingual Plane
00306           result += "\\u";
00307           result += toHex16Bit(cp);
00308         }
00309         else { // codepoint is not in Basic Multilingual Plane
00310                // convert to surrogate pair first
00311           cp -= 0x10000;
00312           result += "\\u";
00313           result += toHex16Bit((cp >> 10) + 0xD800);
00314           result += "\\u";
00315           result += toHex16Bit((cp & 0x3FF) + 0xDC00);
00316         }
00317       }
00318       break;
00319     }
00320   }
00321   result += "\"";
00322   return result;
00323 }
00324 
00325 JSONCPP_STRING valueToQuotedString(const char* value) {
00326   return valueToQuotedStringN(value, static_cast<unsigned int>(strlen(value)));
00327 }
00328 
00329 // Class Writer
00330 // //////////////////////////////////////////////////////////////////
00331 Writer::~Writer() {}
00332 
00333 // Class FastWriter
00334 // //////////////////////////////////////////////////////////////////
00335 
00336 FastWriter::FastWriter()
00337     : yamlCompatibilityEnabled_(false), dropNullPlaceholders_(false),
00338       omitEndingLineFeed_(false) {}
00339 
00340 void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; }
00341 
00342 void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
00343 
00344 void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
00345 
00346 JSONCPP_STRING FastWriter::write(const Value& root) {
00347   document_.clear();
00348   writeValue(root);
00349   if (!omitEndingLineFeed_)
00350     document_ += "\n";
00351   return document_;
00352 }
00353 
00354 void FastWriter::writeValue(const Value& value) {
00355   switch (value.type()) {
00356   case nullValue:
00357     if (!dropNullPlaceholders_)
00358       document_ += "null";
00359     break;
00360   case intValue:
00361     document_ += valueToString(value.asLargestInt());
00362     break;
00363   case uintValue:
00364     document_ += valueToString(value.asLargestUInt());
00365     break;
00366   case realValue:
00367     document_ += valueToString(value.asDouble());
00368     break;
00369   case stringValue:
00370   {
00371     // Is NULL possible for value.string_? No.
00372     char const* str;
00373     char const* end;
00374     bool ok = value.getString(&str, &end);
00375     if (ok) document_ += valueToQuotedStringN(str, static_cast<unsigned>(end-str));
00376     break;
00377   }
00378   case booleanValue:
00379     document_ += valueToString(value.asBool());
00380     break;
00381   case arrayValue: {
00382     document_ += '[';
00383     ArrayIndex size = value.size();
00384     for (ArrayIndex index = 0; index < size; ++index) {
00385       if (index > 0)
00386         document_ += ',';
00387       writeValue(value[index]);
00388     }
00389     document_ += ']';
00390   } break;
00391   case objectValue: {
00392     Value::Members members(value.getMemberNames());
00393     document_ += '{';
00394     for (Value::Members::iterator it = members.begin(); it != members.end();
00395          ++it) {
00396       const JSONCPP_STRING& name = *it;
00397       if (it != members.begin())
00398         document_ += ',';
00399       document_ += valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length()));
00400       document_ += yamlCompatibilityEnabled_ ? ": " : ":";
00401       writeValue(value[name]);
00402     }
00403     document_ += '}';
00404   } break;
00405   }
00406 }
00407 
00408 // Class StyledWriter
00409 // //////////////////////////////////////////////////////////////////
00410 
00411 StyledWriter::StyledWriter()
00412     : rightMargin_(74), indentSize_(3), addChildValues_() {}
00413 
00414 JSONCPP_STRING StyledWriter::write(const Value& root) {
00415   document_.clear();
00416   addChildValues_ = false;
00417   indentString_.clear();
00418   writeCommentBeforeValue(root);
00419   writeValue(root);
00420   writeCommentAfterValueOnSameLine(root);
00421   document_ += "\n";
00422   return document_;
00423 }
00424 
00425 void StyledWriter::writeValue(const Value& value) {
00426   switch (value.type()) {
00427   case nullValue:
00428     pushValue("null");
00429     break;
00430   case intValue:
00431     pushValue(valueToString(value.asLargestInt()));
00432     break;
00433   case uintValue:
00434     pushValue(valueToString(value.asLargestUInt()));
00435     break;
00436   case realValue:
00437     pushValue(valueToString(value.asDouble()));
00438     break;
00439   case stringValue:
00440   {
00441     // Is NULL possible for value.string_? No.
00442     char const* str;
00443     char const* end;
00444     bool ok = value.getString(&str, &end);
00445     if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
00446     else pushValue("");
00447     break;
00448   }
00449   case booleanValue:
00450     pushValue(valueToString(value.asBool()));
00451     break;
00452   case arrayValue:
00453     writeArrayValue(value);
00454     break;
00455   case objectValue: {
00456     Value::Members members(value.getMemberNames());
00457     if (members.empty())
00458       pushValue("{}");
00459     else {
00460       writeWithIndent("{");
00461       indent();
00462       Value::Members::iterator it = members.begin();
00463       for (;;) {
00464         const JSONCPP_STRING& name = *it;
00465         const Value& childValue = value[name];
00466         writeCommentBeforeValue(childValue);
00467         writeWithIndent(valueToQuotedString(name.c_str()));
00468         document_ += " : ";
00469         writeValue(childValue);
00470         if (++it == members.end()) {
00471           writeCommentAfterValueOnSameLine(childValue);
00472           break;
00473         }
00474         document_ += ',';
00475         writeCommentAfterValueOnSameLine(childValue);
00476       }
00477       unindent();
00478       writeWithIndent("}");
00479     }
00480   } break;
00481   }
00482 }
00483 
00484 void StyledWriter::writeArrayValue(const Value& value) {
00485   unsigned size = value.size();
00486   if (size == 0)
00487     pushValue("[]");
00488   else {
00489     bool isArrayMultiLine = isMultilineArray(value);
00490     if (isArrayMultiLine) {
00491       writeWithIndent("[");
00492       indent();
00493       bool hasChildValue = !childValues_.empty();
00494       unsigned index = 0;
00495       for (;;) {
00496         const Value& childValue = value[index];
00497         writeCommentBeforeValue(childValue);
00498         if (hasChildValue)
00499           writeWithIndent(childValues_[index]);
00500         else {
00501           writeIndent();
00502           writeValue(childValue);
00503         }
00504         if (++index == size) {
00505           writeCommentAfterValueOnSameLine(childValue);
00506           break;
00507         }
00508         document_ += ',';
00509         writeCommentAfterValueOnSameLine(childValue);
00510       }
00511       unindent();
00512       writeWithIndent("]");
00513     } else // output on a single line
00514     {
00515       assert(childValues_.size() == size);
00516       document_ += "[ ";
00517       for (unsigned index = 0; index < size; ++index) {
00518         if (index > 0)
00519           document_ += ", ";
00520         document_ += childValues_[index];
00521       }
00522       document_ += " ]";
00523     }
00524   }
00525 }
00526 
00527 bool StyledWriter::isMultilineArray(const Value& value) {
00528   ArrayIndex const size = value.size();
00529   bool isMultiLine = size * 3 >= rightMargin_;
00530   childValues_.clear();
00531   for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
00532     const Value& childValue = value[index];
00533     isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
00534                         childValue.size() > 0);
00535   }
00536   if (!isMultiLine) // check if line length > max line length
00537   {
00538     childValues_.reserve(size);
00539     addChildValues_ = true;
00540     ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
00541     for (ArrayIndex index = 0; index < size; ++index) {
00542       if (hasCommentForValue(value[index])) {
00543         isMultiLine = true;
00544       }
00545       writeValue(value[index]);
00546       lineLength += static_cast<ArrayIndex>(childValues_[index].length());
00547     }
00548     addChildValues_ = false;
00549     isMultiLine = isMultiLine || lineLength >= rightMargin_;
00550   }
00551   return isMultiLine;
00552 }
00553 
00554 void StyledWriter::pushValue(const JSONCPP_STRING& value) {
00555   if (addChildValues_)
00556     childValues_.push_back(value);
00557   else
00558     document_ += value;
00559 }
00560 
00561 void StyledWriter::writeIndent() {
00562   if (!document_.empty()) {
00563     char last = document_[document_.length() - 1];
00564     if (last == ' ') // already indented
00565       return;
00566     if (last != '\n') // Comments may add new-line
00567       document_ += '\n';
00568   }
00569   document_ += indentString_;
00570 }
00571 
00572 void StyledWriter::writeWithIndent(const JSONCPP_STRING& value) {
00573   writeIndent();
00574   document_ += value;
00575 }
00576 
00577 void StyledWriter::indent() { indentString_ += JSONCPP_STRING(indentSize_, ' '); }
00578 
00579 void StyledWriter::unindent() {
00580   assert(indentString_.size() >= indentSize_);
00581   indentString_.resize(indentString_.size() - indentSize_);
00582 }
00583 
00584 void StyledWriter::writeCommentBeforeValue(const Value& root) {
00585   if (!root.hasComment(commentBefore))
00586     return;
00587 
00588   document_ += "\n";
00589   writeIndent();
00590   const JSONCPP_STRING& comment = root.getComment(commentBefore);
00591   JSONCPP_STRING::const_iterator iter = comment.begin();
00592   while (iter != comment.end()) {
00593     document_ += *iter;
00594     if (*iter == '\n' &&
00595        ((iter+1) != comment.end() && *(iter + 1) == '/'))
00596       writeIndent();
00597     ++iter;
00598   }
00599 
00600   // Comments are stripped of trailing newlines, so add one here
00601   document_ += "\n";
00602 }
00603 
00604 void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
00605   if (root.hasComment(commentAfterOnSameLine))
00606     document_ += " " + root.getComment(commentAfterOnSameLine);
00607 
00608   if (root.hasComment(commentAfter)) {
00609     document_ += "\n";
00610     document_ += root.getComment(commentAfter);
00611     document_ += "\n";
00612   }
00613 }
00614 
00615 bool StyledWriter::hasCommentForValue(const Value& value) {
00616   return value.hasComment(commentBefore) ||
00617          value.hasComment(commentAfterOnSameLine) ||
00618          value.hasComment(commentAfter);
00619 }
00620 
00621 // Class StyledStreamWriter
00622 // //////////////////////////////////////////////////////////////////
00623 
00624 StyledStreamWriter::StyledStreamWriter(JSONCPP_STRING indentation)
00625     : document_(NULL), rightMargin_(74), indentation_(indentation),
00626       addChildValues_() {}
00627 
00628 void StyledStreamWriter::write(JSONCPP_OSTREAM& out, const Value& root) {
00629   document_ = &out;
00630   addChildValues_ = false;
00631   indentString_.clear();
00632   indented_ = true;
00633   writeCommentBeforeValue(root);
00634   if (!indented_) writeIndent();
00635   indented_ = true;
00636   writeValue(root);
00637   writeCommentAfterValueOnSameLine(root);
00638   *document_ << "\n";
00639   document_ = NULL; // Forget the stream, for safety.
00640 }
00641 
00642 void StyledStreamWriter::writeValue(const Value& value) {
00643   switch (value.type()) {
00644   case nullValue:
00645     pushValue("null");
00646     break;
00647   case intValue:
00648     pushValue(valueToString(value.asLargestInt()));
00649     break;
00650   case uintValue:
00651     pushValue(valueToString(value.asLargestUInt()));
00652     break;
00653   case realValue:
00654     pushValue(valueToString(value.asDouble()));
00655     break;
00656   case stringValue:
00657   {
00658     // Is NULL possible for value.string_? No.
00659     char const* str;
00660     char const* end;
00661     bool ok = value.getString(&str, &end);
00662     if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
00663     else pushValue("");
00664     break;
00665   }
00666   case booleanValue:
00667     pushValue(valueToString(value.asBool()));
00668     break;
00669   case arrayValue:
00670     writeArrayValue(value);
00671     break;
00672   case objectValue: {
00673     Value::Members members(value.getMemberNames());
00674     if (members.empty())
00675       pushValue("{}");
00676     else {
00677       writeWithIndent("{");
00678       indent();
00679       Value::Members::iterator it = members.begin();
00680       for (;;) {
00681         const JSONCPP_STRING& name = *it;
00682         const Value& childValue = value[name];
00683         writeCommentBeforeValue(childValue);
00684         writeWithIndent(valueToQuotedString(name.c_str()));
00685         *document_ << " : ";
00686         writeValue(childValue);
00687         if (++it == members.end()) {
00688           writeCommentAfterValueOnSameLine(childValue);
00689           break;
00690         }
00691         *document_ << ",";
00692         writeCommentAfterValueOnSameLine(childValue);
00693       }
00694       unindent();
00695       writeWithIndent("}");
00696     }
00697   } break;
00698   }
00699 }
00700 
00701 void StyledStreamWriter::writeArrayValue(const Value& value) {
00702   unsigned size = value.size();
00703   if (size == 0)
00704     pushValue("[]");
00705   else {
00706     bool isArrayMultiLine = isMultilineArray(value);
00707     if (isArrayMultiLine) {
00708       writeWithIndent("[");
00709       indent();
00710       bool hasChildValue = !childValues_.empty();
00711       unsigned index = 0;
00712       for (;;) {
00713         const Value& childValue = value[index];
00714         writeCommentBeforeValue(childValue);
00715         if (hasChildValue)
00716           writeWithIndent(childValues_[index]);
00717         else {
00718           if (!indented_) writeIndent();
00719           indented_ = true;
00720           writeValue(childValue);
00721           indented_ = false;
00722         }
00723         if (++index == size) {
00724           writeCommentAfterValueOnSameLine(childValue);
00725           break;
00726         }
00727         *document_ << ",";
00728         writeCommentAfterValueOnSameLine(childValue);
00729       }
00730       unindent();
00731       writeWithIndent("]");
00732     } else // output on a single line
00733     {
00734       assert(childValues_.size() == size);
00735       *document_ << "[ ";
00736       for (unsigned index = 0; index < size; ++index) {
00737         if (index > 0)
00738           *document_ << ", ";
00739         *document_ << childValues_[index];
00740       }
00741       *document_ << " ]";
00742     }
00743   }
00744 }
00745 
00746 bool StyledStreamWriter::isMultilineArray(const Value& value) {
00747   ArrayIndex const size = value.size();
00748   bool isMultiLine = size * 3 >= rightMargin_;
00749   childValues_.clear();
00750   for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
00751     const Value& childValue = value[index];
00752     isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
00753                         childValue.size() > 0);
00754   }
00755   if (!isMultiLine) // check if line length > max line length
00756   {
00757     childValues_.reserve(size);
00758     addChildValues_ = true;
00759     ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
00760     for (ArrayIndex index = 0; index < size; ++index) {
00761       if (hasCommentForValue(value[index])) {
00762         isMultiLine = true;
00763       }
00764       writeValue(value[index]);
00765       lineLength += static_cast<ArrayIndex>(childValues_[index].length());
00766     }
00767     addChildValues_ = false;
00768     isMultiLine = isMultiLine || lineLength >= rightMargin_;
00769   }
00770   return isMultiLine;
00771 }
00772 
00773 void StyledStreamWriter::pushValue(const JSONCPP_STRING& value) {
00774   if (addChildValues_)
00775     childValues_.push_back(value);
00776   else
00777     *document_ << value;
00778 }
00779 
00780 void StyledStreamWriter::writeIndent() {
00781   // blep intended this to look at the so-far-written string
00782   // to determine whether we are already indented, but
00783   // with a stream we cannot do that. So we rely on some saved state.
00784   // The caller checks indented_.
00785   *document_ << '\n' << indentString_;
00786 }
00787 
00788 void StyledStreamWriter::writeWithIndent(const JSONCPP_STRING& value) {
00789   if (!indented_) writeIndent();
00790   *document_ << value;
00791   indented_ = false;
00792 }
00793 
00794 void StyledStreamWriter::indent() { indentString_ += indentation_; }
00795 
00796 void StyledStreamWriter::unindent() {
00797   assert(indentString_.size() >= indentation_.size());
00798   indentString_.resize(indentString_.size() - indentation_.size());
00799 }
00800 
00801 void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
00802   if (!root.hasComment(commentBefore))
00803     return;
00804 
00805   if (!indented_) writeIndent();
00806   const JSONCPP_STRING& comment = root.getComment(commentBefore);
00807   JSONCPP_STRING::const_iterator iter = comment.begin();
00808   while (iter != comment.end()) {
00809     *document_ << *iter;
00810     if (*iter == '\n' &&
00811        ((iter+1) != comment.end() && *(iter + 1) == '/'))
00812       // writeIndent();  // would include newline
00813       *document_ << indentString_;
00814     ++iter;
00815   }
00816   indented_ = false;
00817 }
00818 
00819 void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
00820   if (root.hasComment(commentAfterOnSameLine))
00821     *document_ << ' ' << root.getComment(commentAfterOnSameLine);
00822 
00823   if (root.hasComment(commentAfter)) {
00824     writeIndent();
00825     *document_ << root.getComment(commentAfter);
00826   }
00827   indented_ = false;
00828 }
00829 
00830 bool StyledStreamWriter::hasCommentForValue(const Value& value) {
00831   return value.hasComment(commentBefore) ||
00832          value.hasComment(commentAfterOnSameLine) ||
00833          value.hasComment(commentAfter);
00834 }
00835 
00836 //////////////////////////
00837 // BuiltStyledStreamWriter
00838 
00839 /// Scoped enums are not available until C++11.
00840 struct CommentStyle {
00841   /// Decide whether to write comments.
00842   enum Enum {
00843     None,  ///< Drop all comments.
00844     Most,  ///< Recover odd behavior of previous versions (not implemented yet).
00845     All  ///< Keep all comments.
00846   };
00847 };
00848 
00849 struct BuiltStyledStreamWriter : public StreamWriter
00850 {
00851   BuiltStyledStreamWriter(
00852       JSONCPP_STRING const& indentation,
00853       CommentStyle::Enum cs,
00854       JSONCPP_STRING const& colonSymbol,
00855       JSONCPP_STRING const& nullSymbol,
00856       JSONCPP_STRING const& endingLineFeedSymbol,
00857       bool useSpecialFloats,
00858       unsigned int precision);
00859   int write(Value const& root, JSONCPP_OSTREAM* sout) JSONCPP_OVERRIDE;
00860 private:
00861   void writeValue(Value const& value);
00862   void writeArrayValue(Value const& value);
00863   bool isMultilineArray(Value const& value);
00864   void pushValue(JSONCPP_STRING const& value);
00865   void writeIndent();
00866   void writeWithIndent(JSONCPP_STRING const& value);
00867   void indent();
00868   void unindent();
00869   void writeCommentBeforeValue(Value const& root);
00870   void writeCommentAfterValueOnSameLine(Value const& root);
00871   static bool hasCommentForValue(const Value& value);
00872 
00873   typedef std::vector<JSONCPP_STRING> ChildValues;
00874 
00875   ChildValues childValues_;
00876   JSONCPP_STRING indentString_;
00877   unsigned int rightMargin_;
00878   JSONCPP_STRING indentation_;
00879   CommentStyle::Enum cs_;
00880   JSONCPP_STRING colonSymbol_;
00881   JSONCPP_STRING nullSymbol_;
00882   JSONCPP_STRING endingLineFeedSymbol_;
00883   bool addChildValues_ : 1;
00884   bool indented_ : 1;
00885   bool useSpecialFloats_ : 1;
00886   unsigned int precision_;
00887 };
00888 BuiltStyledStreamWriter::BuiltStyledStreamWriter(
00889       JSONCPP_STRING const& indentation,
00890       CommentStyle::Enum cs,
00891       JSONCPP_STRING const& colonSymbol,
00892       JSONCPP_STRING const& nullSymbol,
00893       JSONCPP_STRING const& endingLineFeedSymbol,
00894       bool useSpecialFloats,
00895       unsigned int precision)
00896   : rightMargin_(74)
00897   , indentation_(indentation)
00898   , cs_(cs)
00899   , colonSymbol_(colonSymbol)
00900   , nullSymbol_(nullSymbol)
00901   , endingLineFeedSymbol_(endingLineFeedSymbol)
00902   , addChildValues_(false)
00903   , indented_(false)
00904   , useSpecialFloats_(useSpecialFloats)
00905   , precision_(precision)
00906 {
00907 }
00908 int BuiltStyledStreamWriter::write(Value const& root, JSONCPP_OSTREAM* sout)
00909 {
00910   sout_ = sout;
00911   addChildValues_ = false;
00912   indented_ = true;
00913   indentString_.clear();
00914   writeCommentBeforeValue(root);
00915   if (!indented_) writeIndent();
00916   indented_ = true;
00917   writeValue(root);
00918   writeCommentAfterValueOnSameLine(root);
00919   *sout_ << endingLineFeedSymbol_;
00920   sout_ = NULL;
00921   return 0;
00922 }
00923 void BuiltStyledStreamWriter::writeValue(Value const& value) {
00924   switch (value.type()) {
00925   case nullValue:
00926     pushValue(nullSymbol_);
00927     break;
00928   case intValue:
00929     pushValue(valueToString(value.asLargestInt()));
00930     break;
00931   case uintValue:
00932     pushValue(valueToString(value.asLargestUInt()));
00933     break;
00934   case realValue:
00935     pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_));
00936     break;
00937   case stringValue:
00938   {
00939     // Is NULL is possible for value.string_? No.
00940     char const* str;
00941     char const* end;
00942     bool ok = value.getString(&str, &end);
00943     if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
00944     else pushValue("");
00945     break;
00946   }
00947   case booleanValue:
00948     pushValue(valueToString(value.asBool()));
00949     break;
00950   case arrayValue:
00951     writeArrayValue(value);
00952     break;
00953   case objectValue: {
00954     Value::Members members(value.getMemberNames());
00955     if (members.empty())
00956       pushValue("{}");
00957     else {
00958       writeWithIndent("{");
00959       indent();
00960       Value::Members::iterator it = members.begin();
00961       for (;;) {
00962         JSONCPP_STRING const& name = *it;
00963         Value const& childValue = value[name];
00964         writeCommentBeforeValue(childValue);
00965         writeWithIndent(valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length())));
00966         *sout_ << colonSymbol_;
00967         writeValue(childValue);
00968         if (++it == members.end()) {
00969           writeCommentAfterValueOnSameLine(childValue);
00970           break;
00971         }
00972         *sout_ << ",";
00973         writeCommentAfterValueOnSameLine(childValue);
00974       }
00975       unindent();
00976       writeWithIndent("}");
00977     }
00978   } break;
00979   }
00980 }
00981 
00982 void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
00983   unsigned size = value.size();
00984   if (size == 0)
00985     pushValue("[]");
00986   else {
00987     bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value);
00988     if (isMultiLine) {
00989       writeWithIndent("[");
00990       indent();
00991       bool hasChildValue = !childValues_.empty();
00992       unsigned index = 0;
00993       for (;;) {
00994         Value const& childValue = value[index];
00995         writeCommentBeforeValue(childValue);
00996         if (hasChildValue)
00997           writeWithIndent(childValues_[index]);
00998         else {
00999           if (!indented_) writeIndent();
01000           indented_ = true;
01001           writeValue(childValue);
01002           indented_ = false;
01003         }
01004         if (++index == size) {
01005           writeCommentAfterValueOnSameLine(childValue);
01006           break;
01007         }
01008         *sout_ << ",";
01009         writeCommentAfterValueOnSameLine(childValue);
01010       }
01011       unindent();
01012       writeWithIndent("]");
01013     } else // output on a single line
01014     {
01015       assert(childValues_.size() == size);
01016       *sout_ << "[";
01017       if (!indentation_.empty()) *sout_ << " ";
01018       for (unsigned index = 0; index < size; ++index) {
01019         if (index > 0)
01020           *sout_ << ((!indentation_.empty()) ? ", " : ",");
01021         *sout_ << childValues_[index];
01022       }
01023       if (!indentation_.empty()) *sout_ << " ";
01024       *sout_ << "]";
01025     }
01026   }
01027 }
01028 
01029 bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) {
01030   ArrayIndex const size = value.size();
01031   bool isMultiLine = size * 3 >= rightMargin_;
01032   childValues_.clear();
01033   for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
01034     Value const& childValue = value[index];
01035     isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
01036                         childValue.size() > 0);
01037   }
01038   if (!isMultiLine) // check if line length > max line length
01039   {
01040     childValues_.reserve(size);
01041     addChildValues_ = true;
01042     ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
01043     for (ArrayIndex index = 0; index < size; ++index) {
01044       if (hasCommentForValue(value[index])) {
01045         isMultiLine = true;
01046       }
01047       writeValue(value[index]);
01048       lineLength += static_cast<ArrayIndex>(childValues_[index].length());
01049     }
01050     addChildValues_ = false;
01051     isMultiLine = isMultiLine || lineLength >= rightMargin_;
01052   }
01053   return isMultiLine;
01054 }
01055 
01056 void BuiltStyledStreamWriter::pushValue(JSONCPP_STRING const& value) {
01057   if (addChildValues_)
01058     childValues_.push_back(value);
01059   else
01060     *sout_ << value;
01061 }
01062 
01063 void BuiltStyledStreamWriter::writeIndent() {
01064   // blep intended this to look at the so-far-written string
01065   // to determine whether we are already indented, but
01066   // with a stream we cannot do that. So we rely on some saved state.
01067   // The caller checks indented_.
01068 
01069   if (!indentation_.empty()) {
01070     // In this case, drop newlines too.
01071     *sout_ << '\n' << indentString_;
01072   }
01073 }
01074 
01075 void BuiltStyledStreamWriter::writeWithIndent(JSONCPP_STRING const& value) {
01076   if (!indented_) writeIndent();
01077   *sout_ << value;
01078   indented_ = false;
01079 }
01080 
01081 void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
01082 
01083 void BuiltStyledStreamWriter::unindent() {
01084   assert(indentString_.size() >= indentation_.size());
01085   indentString_.resize(indentString_.size() - indentation_.size());
01086 }
01087 
01088 void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
01089   if (cs_ == CommentStyle::None) return;
01090   if (!root.hasComment(commentBefore))
01091     return;
01092 
01093   if (!indented_) writeIndent();
01094   const JSONCPP_STRING& comment = root.getComment(commentBefore);
01095   JSONCPP_STRING::const_iterator iter = comment.begin();
01096   while (iter != comment.end()) {
01097     *sout_ << *iter;
01098     if (*iter == '\n' &&
01099        ((iter+1) != comment.end() && *(iter + 1) == '/'))
01100       // writeIndent();  // would write extra newline
01101       *sout_ << indentString_;
01102     ++iter;
01103   }
01104   indented_ = false;
01105 }
01106 
01107 void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) {
01108   if (cs_ == CommentStyle::None) return;
01109   if (root.hasComment(commentAfterOnSameLine))
01110     *sout_ << " " + root.getComment(commentAfterOnSameLine);
01111 
01112   if (root.hasComment(commentAfter)) {
01113     writeIndent();
01114     *sout_ << root.getComment(commentAfter);
01115   }
01116 }
01117 
01118 // static
01119 bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
01120   return value.hasComment(commentBefore) ||
01121          value.hasComment(commentAfterOnSameLine) ||
01122          value.hasComment(commentAfter);
01123 }
01124 
01125 ///////////////
01126 // StreamWriter
01127 
01128 StreamWriter::StreamWriter()
01129     : sout_(NULL)
01130 {
01131 }
01132 StreamWriter::~StreamWriter()
01133 {
01134 }
01135 StreamWriter::Factory::~Factory()
01136 {}
01137 StreamWriterBuilder::StreamWriterBuilder()
01138 {
01139   setDefaults(&settings_);
01140 }
01141 StreamWriterBuilder::~StreamWriterBuilder()
01142 {}
01143 StreamWriter* StreamWriterBuilder::newStreamWriter () const
01144 {
01145   JSONCPP_STRING indentation = settings_["indentation"].asString();
01146   JSONCPP_STRING cs_str = settings_["commentStyle"].asString();
01147   bool eyc = settings_["enableYAMLCompatibility"].asBool();
01148   bool dnp = settings_["dropNullPlaceholders"].asBool();
01149   bool usf = settings_["useSpecialFloats"].asBool(); 
01150   unsigned int pre = settings_["precision"].asUInt();
01151   CommentStyle::Enum cs = CommentStyle::All;
01152   if (cs_str == "All") {
01153     cs = CommentStyle::All;
01154   } else if (cs_str == "None") {
01155     cs = CommentStyle::None;
01156   } else {
01157     throwRuntimeError("commentStyle must be 'All' or 'None'");
01158   }
01159   JSONCPP_STRING colonSymbol = " : ";
01160   if (eyc) {
01161     colonSymbol = ": ";
01162   } else if (indentation.empty()) {
01163     colonSymbol = ":";
01164   }
01165   JSONCPP_STRING nullSymbol = "null";
01166   if (dnp) {
01167     nullSymbol.clear();
01168   }
01169   if (pre > 17) pre = 17;
01170   JSONCPP_STRING endingLineFeedSymbol;
01171   return new BuiltStyledStreamWriter(
01172       indentation, cs,
01173       colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre);
01174 }
01175 static void getValidWriterKeys(std::set<JSONCPP_STRING>* valid_keys)
01176 {
01177   valid_keys->clear();
01178   valid_keys->insert("indentation");
01179   valid_keys->insert("commentStyle");
01180   valid_keys->insert("enableYAMLCompatibility");
01181   valid_keys->insert("dropNullPlaceholders");
01182   valid_keys->insert("useSpecialFloats");
01183   valid_keys->insert("precision");
01184 }
01185 bool StreamWriterBuilder::validate (Json::Value* invalid) const
01186 {
01187   Json::Value my_invalid;
01188   if (!invalid) invalid = &my_invalid;  // so we do not need to test for NULL
01189   Json::Value& inv = *invalid;
01190   std::set<JSONCPP_STRING> valid_keys;
01191   getValidWriterKeys(&valid_keys);
01192   Value::Members keys = settings_.getMemberNames();
01193   size_t n = keys.size();
01194   for (size_t i = 0; i < n; ++i) {
01195     JSONCPP_STRING const& key = keys[i];
01196     if (valid_keys.find(key) == valid_keys.end()) {
01197       inv[key] = settings_[key];
01198     }
01199   }
01200   return 0u == inv.size();
01201 }
01202 Value& StreamWriterBuilder::operator[](JSONCPP_STRING key)
01203 {
01204   return settings_[key];
01205 }
01206 // static
01207 void StreamWriterBuilder::setDefaults(Json::Value* settings)
01208 {
01209   //! [StreamWriterBuilderDefaults]
01210   (*settings)["commentStyle"] = "All";
01211   (*settings)["indentation"] = "\t";
01212   (*settings)["enableYAMLCompatibility"] = false;
01213   (*settings)["dropNullPlaceholders"] = false;
01214   (*settings)["useSpecialFloats"] = false;
01215   (*settings)["precision"] = 17;
01216   //! [StreamWriterBuilderDefaults]
01217 }
01218 
01219 JSONCPP_STRING writeString(StreamWriter::Factory const& builder, Value const& root) {
01220   JSONCPP_OSTRINGSTREAM sout;
01221   StreamWriterPtr const writer(builder.newStreamWriter());
01222   writer->write(root, &sout);
01223   return sout.str();
01224 }
01225 
01226 JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM& sout, Value const& root) {
01227   StreamWriterBuilder builder;
01228   StreamWriterPtr const writer(builder.newStreamWriter ());
01229   writer->write(root, &sout);
01230   return sout;
01231 }
01232 
01233 } // namespace Json