JsonCpp project page JsonCpp home page

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