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