1 // Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
2 // Distributed under MIT license, or public domain if desired and
3 // recognized in your jurisdiction.
4 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
5 
6 #if !defined(JSON_IS_AMALGAMATION)
7 #include <json/writer.h>
8 #include "json_tool.h"
9 #endif // if !defined(JSON_IS_AMALGAMATION)
10 #include <iomanip>
11 #include <memory>
12 #include <sstream>
13 #include <utility>
14 #include <set>
15 #include <cassert>
16 #include <cstring>
17 #include <cstdio>
18 
19 #if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0
20 #include <float.h>
21 #define isfinite _finite
22 #elif defined(__sun) && defined(__SVR4) //Solaris
23 #if !defined(isfinite)
24 #include <ieeefp.h>
25 #define isfinite finite
26 #endif
27 #elif defined(_AIX)
28 #if !defined(isfinite)
29 #include <math.h>
30 #define isfinite finite
31 #endif
32 #elif defined(__hpux)
33 #if !defined(isfinite)
34 #if defined(__ia64) && !defined(finite)
35 #define isfinite(x) ((sizeof(x) == sizeof(float) ? \
36                      _Isfinitef(x) : _IsFinite(x)))
37 #else
38 #include <math.h>
39 #define isfinite finite
40 #endif
41 #endif
42 #else
43 #include <cmath>
44 #if !(defined(__QNXNTO__)) // QNX already defines isfinite
45 #define isfinite std::isfinite
46 #endif
47 #endif
48 
49 #if defined(_MSC_VER)
50 #if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above
51 #define snprintf sprintf_s
52 #elif _MSC_VER >= 1900 // VC++ 14.0 and above
53 #define snprintf std::snprintf
54 #else
55 #define snprintf _snprintf
56 #endif
57 #elif defined(__ANDROID__) || defined(__QNXNTO__)
58 #define snprintf snprintf
59 #elif __cplusplus >= 201103L
60 #if !defined(__MINGW32__) && !defined(__CYGWIN__)
61 #define snprintf std::snprintf
62 #endif
63 #endif
64 
65 #if defined(__BORLANDC__)
66 #include <float.h>
67 #define isfinite _finite
68 #define snprintf _snprintf
69 #endif
70 
71 #if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
72 // Disable warning about strdup being deprecated.
73 #pragma warning(disable : 4996)
74 #endif
75 
76 namespace Json {
77 
78 #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
79 typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
80 #else
81 typedef std::auto_ptr<StreamWriter>   StreamWriterPtr;
82 #endif
83 
valueToString(LargestInt value)84 JSONCPP_STRING valueToString(LargestInt value) {
85   UIntToStringBuffer buffer;
86   char* current = buffer + sizeof(buffer);
87   if (value == Value::minLargestInt) {
88     uintToString(LargestUInt(Value::maxLargestInt) + 1, current);
89     *--current = '-';
90   } else if (value < 0) {
91     uintToString(LargestUInt(-value), current);
92     *--current = '-';
93   } else {
94     uintToString(LargestUInt(value), current);
95   }
96   assert(current >= buffer);
97   return current;
98 }
99 
valueToString(LargestUInt value)100 JSONCPP_STRING valueToString(LargestUInt value) {
101   UIntToStringBuffer buffer;
102   char* current = buffer + sizeof(buffer);
103   uintToString(value, current);
104   assert(current >= buffer);
105   return current;
106 }
107 
108 #if defined(JSON_HAS_INT64)
109 
valueToString(Int value)110 JSONCPP_STRING valueToString(Int value) {
111   return valueToString(LargestInt(value));
112 }
113 
valueToString(UInt value)114 JSONCPP_STRING valueToString(UInt value) {
115   return valueToString(LargestUInt(value));
116 }
117 
118 #endif // # if defined(JSON_HAS_INT64)
119 
120 namespace {
valueToString(double value,bool useSpecialFloats,unsigned int precision)121 JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision) {
122   // Allocate a buffer that is more than large enough to store the 16 digits of
123   // precision requested below.
124   char buffer[36];
125   int len = -1;
126 
127   char formatString[15];
128   snprintf(formatString, sizeof(formatString), "%%.%ug", precision);
129 
130   // Print into the buffer. We need not request the alternative representation
131   // that always has a decimal point because JSON doesn't distinguish the
132   // concepts of reals and integers.
133   if (isfinite(value)) {
134     len = snprintf(buffer, sizeof(buffer), formatString, value);
135     fixNumericLocale(buffer, buffer + len);
136 
137     // try to ensure we preserve the fact that this was given to us as a double on input
138     if (!strchr(buffer, '.') && !strchr(buffer, 'e')) {
139       strcat(buffer, ".0");
140     }
141 
142   } else {
143     // IEEE standard states that NaN values will not compare to themselves
144     if (value != value) {
145       len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null");
146     } else if (value < 0) {
147       len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999");
148     } else {
149       len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "1e+9999");
150     }
151   }
152   assert(len >= 0);
153   return buffer;
154 }
155 }
156 
valueToString(double value)157 JSONCPP_STRING valueToString(double value) { return valueToString(value, false, 17); }
158 
valueToString(bool value)159 JSONCPP_STRING valueToString(bool value) { return value ? "true" : "false"; }
160 
isAnyCharRequiredQuoting(char const * s,size_t n)161 static bool isAnyCharRequiredQuoting(char const* s, size_t n) {
162   assert(s || !n);
163 
164   char const* const end = s + n;
165   for (char const* cur = s; cur < end; ++cur) {
166     if (*cur == '\\' || *cur == '\"' || *cur < ' '
167       || static_cast<unsigned char>(*cur) < 0x80)
168       return true;
169   }
170   return false;
171 }
172 
utf8ToCodepoint(const char * & s,const char * e)173 static unsigned int utf8ToCodepoint(const char*& s, const char* e) {
174   const unsigned int REPLACEMENT_CHARACTER = 0xFFFD;
175 
176   unsigned int firstByte = static_cast<unsigned char>(*s);
177 
178   if (firstByte < 0x80)
179     return firstByte;
180 
181   if (firstByte < 0xE0) {
182     if (e - s < 2)
183       return REPLACEMENT_CHARACTER;
184 
185     unsigned int calculated = ((firstByte & 0x1F) << 6)
186       | (static_cast<unsigned int>(s[1]) & 0x3F);
187     s += 1;
188     // oversized encoded characters are invalid
189     return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated;
190   }
191 
192   if (firstByte < 0xF0) {
193     if (e - s < 3)
194       return REPLACEMENT_CHARACTER;
195 
196     unsigned int calculated = ((firstByte & 0x0F) << 12)
197       | ((static_cast<unsigned int>(s[1]) & 0x3F) << 6)
198       |  (static_cast<unsigned int>(s[2]) & 0x3F);
199     s += 2;
200     // surrogates aren't valid codepoints itself
201     // shouldn't be UTF-8 encoded
202     if (calculated >= 0xD800 && calculated <= 0xDFFF)
203       return REPLACEMENT_CHARACTER;
204     // oversized encoded characters are invalid
205     return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated;
206   }
207 
208   if (firstByte < 0xF8) {
209     if (e - s < 4)
210       return REPLACEMENT_CHARACTER;
211 
212     unsigned int calculated = ((firstByte & 0x07) << 24)
213       | ((static_cast<unsigned int>(s[1]) & 0x3F) << 12)
214       | ((static_cast<unsigned int>(s[2]) & 0x3F) << 6)
215       |  (static_cast<unsigned int>(s[3]) & 0x3F);
216     s += 3;
217     // oversized encoded characters are invalid
218     return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated;
219   }
220 
221   return REPLACEMENT_CHARACTER;
222 }
223 
224 static const char hex2[] =
225   "000102030405060708090a0b0c0d0e0f"
226   "101112131415161718191a1b1c1d1e1f"
227   "202122232425262728292a2b2c2d2e2f"
228   "303132333435363738393a3b3c3d3e3f"
229   "404142434445464748494a4b4c4d4e4f"
230   "505152535455565758595a5b5c5d5e5f"
231   "606162636465666768696a6b6c6d6e6f"
232   "707172737475767778797a7b7c7d7e7f"
233   "808182838485868788898a8b8c8d8e8f"
234   "909192939495969798999a9b9c9d9e9f"
235   "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
236   "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
237   "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
238   "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
239   "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
240   "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
241 
toHex16Bit(unsigned int x)242 static JSONCPP_STRING toHex16Bit(unsigned int x) {
243   const unsigned int hi = (x >> 8) & 0xff;
244   const unsigned int lo = x & 0xff;
245   JSONCPP_STRING result(4, ' ');
246   result[0] = hex2[2 * hi];
247   result[1] = hex2[2 * hi + 1];
248   result[2] = hex2[2 * lo];
249   result[3] = hex2[2 * lo + 1];
250   return result;
251 }
252 
valueToQuotedStringN(const char * value,unsigned length)253 static JSONCPP_STRING valueToQuotedStringN(const char* value, unsigned length) {
254   if (value == NULL)
255     return "";
256 
257   if (!isAnyCharRequiredQuoting(value, length))
258     return JSONCPP_STRING("\"") + value + "\"";
259   // We have to walk value and escape any special characters.
260   // Appending to JSONCPP_STRING is not efficient, but this should be rare.
261   // (Note: forward slashes are *not* rare, but I am not escaping them.)
262   JSONCPP_STRING::size_type maxsize =
263       length * 2 + 3; // allescaped+quotes+NULL
264   JSONCPP_STRING result;
265   result.reserve(maxsize); // to avoid lots of mallocs
266   result += "\"";
267   char const* end = value + length;
268   for (const char* c = value; c != end; ++c) {
269     switch (*c) {
270     case '\"':
271       result += "\\\"";
272       break;
273     case '\\':
274       result += "\\\\";
275       break;
276     case '\b':
277       result += "\\b";
278       break;
279     case '\f':
280       result += "\\f";
281       break;
282     case '\n':
283       result += "\\n";
284       break;
285     case '\r':
286       result += "\\r";
287       break;
288     case '\t':
289       result += "\\t";
290       break;
291     // case '/':
292     // Even though \/ is considered a legal escape in JSON, a bare
293     // slash is also legal, so I see no reason to escape it.
294     // (I hope I am not misunderstanding something.)
295     // blep notes: actually escaping \/ may be useful in javascript to avoid </
296     // sequence.
297     // Should add a flag to allow this compatibility mode and prevent this
298     // sequence from occurring.
299     default: {
300         unsigned int cp = utf8ToCodepoint(c, end);
301         // don't escape non-control characters
302         // (short escape sequence are applied above)
303         if (cp < 0x80 && cp >= 0x20)
304           result += static_cast<char>(cp);
305         else if (cp < 0x10000) { // codepoint is in Basic Multilingual Plane
306           result += "\\u";
307           result += toHex16Bit(cp);
308         }
309         else { // codepoint is not in Basic Multilingual Plane
310                // convert to surrogate pair first
311           cp -= 0x10000;
312           result += "\\u";
313           result += toHex16Bit((cp >> 10) + 0xD800);
314           result += "\\u";
315           result += toHex16Bit((cp & 0x3FF) + 0xDC00);
316         }
317       }
318       break;
319     }
320   }
321   result += "\"";
322   return result;
323 }
324 
valueToQuotedString(const char * value)325 JSONCPP_STRING valueToQuotedString(const char* value) {
326   return valueToQuotedStringN(value, static_cast<unsigned int>(strlen(value)));
327 }
328 
329 // Class Writer
330 // //////////////////////////////////////////////////////////////////
~Writer()331 Writer::~Writer() {}
332 
333 // Class FastWriter
334 // //////////////////////////////////////////////////////////////////
335 
FastWriter()336 FastWriter::FastWriter()
337     : yamlCompatibilityEnabled_(false), dropNullPlaceholders_(false),
338       omitEndingLineFeed_(false) {}
339 
enableYAMLCompatibility()340 void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; }
341 
dropNullPlaceholders()342 void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
343 
omitEndingLineFeed()344 void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
345 
write(const Value & root)346 JSONCPP_STRING FastWriter::write(const Value& root) {
347   document_.clear();
348   writeValue(root);
349   if (!omitEndingLineFeed_)
350     document_ += "\n";
351   return document_;
352 }
353 
writeValue(const Value & value)354 void FastWriter::writeValue(const Value& value) {
355   switch (value.type()) {
356   case nullValue:
357     if (!dropNullPlaceholders_)
358       document_ += "null";
359     break;
360   case intValue:
361     document_ += valueToString(value.asLargestInt());
362     break;
363   case uintValue:
364     document_ += valueToString(value.asLargestUInt());
365     break;
366   case realValue:
367     document_ += valueToString(value.asDouble());
368     break;
369   case stringValue:
370   {
371     // Is NULL possible for value.string_? No.
372     char const* str;
373     char const* end;
374     bool ok = value.getString(&str, &end);
375     if (ok) document_ += valueToQuotedStringN(str, static_cast<unsigned>(end-str));
376     break;
377   }
378   case booleanValue:
379     document_ += valueToString(value.asBool());
380     break;
381   case arrayValue: {
382     document_ += '[';
383     ArrayIndex size = value.size();
384     for (ArrayIndex index = 0; index < size; ++index) {
385       if (index > 0)
386         document_ += ',';
387       writeValue(value[index]);
388     }
389     document_ += ']';
390   } break;
391   case objectValue: {
392     Value::Members members(value.getMemberNames());
393     document_ += '{';
394     for (Value::Members::iterator it = members.begin(); it != members.end();
395          ++it) {
396       const JSONCPP_STRING& name = *it;
397       if (it != members.begin())
398         document_ += ',';
399       document_ += valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length()));
400       document_ += yamlCompatibilityEnabled_ ? ": " : ":";
401       writeValue(value[name]);
402     }
403     document_ += '}';
404   } break;
405   }
406 }
407 
408 // Class StyledWriter
409 // //////////////////////////////////////////////////////////////////
410 
StyledWriter()411 StyledWriter::StyledWriter()
412     : rightMargin_(74), indentSize_(3), addChildValues_() {}
413 
write(const Value & root)414 JSONCPP_STRING StyledWriter::write(const Value& root) {
415   document_.clear();
416   addChildValues_ = false;
417   indentString_.clear();
418   writeCommentBeforeValue(root);
419   writeValue(root);
420   writeCommentAfterValueOnSameLine(root);
421   document_ += "\n";
422   return document_;
423 }
424 
writeValue(const Value & value)425 void StyledWriter::writeValue(const Value& value) {
426   switch (value.type()) {
427   case nullValue:
428     pushValue("null");
429     break;
430   case intValue:
431     pushValue(valueToString(value.asLargestInt()));
432     break;
433   case uintValue:
434     pushValue(valueToString(value.asLargestUInt()));
435     break;
436   case realValue:
437     pushValue(valueToString(value.asDouble()));
438     break;
439   case stringValue:
440   {
441     // Is NULL possible for value.string_? No.
442     char const* str;
443     char const* end;
444     bool ok = value.getString(&str, &end);
445     if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
446     else pushValue("");
447     break;
448   }
449   case booleanValue:
450     pushValue(valueToString(value.asBool()));
451     break;
452   case arrayValue:
453     writeArrayValue(value);
454     break;
455   case objectValue: {
456     Value::Members members(value.getMemberNames());
457     if (members.empty())
458       pushValue("{}");
459     else {
460       writeWithIndent("{");
461       indent();
462       Value::Members::iterator it = members.begin();
463       for (;;) {
464         const JSONCPP_STRING& name = *it;
465         const Value& childValue = value[name];
466         writeCommentBeforeValue(childValue);
467         writeWithIndent(valueToQuotedString(name.c_str()));
468         document_ += " : ";
469         writeValue(childValue);
470         if (++it == members.end()) {
471           writeCommentAfterValueOnSameLine(childValue);
472           break;
473         }
474         document_ += ',';
475         writeCommentAfterValueOnSameLine(childValue);
476       }
477       unindent();
478       writeWithIndent("}");
479     }
480   } break;
481   }
482 }
483 
writeArrayValue(const Value & value)484 void StyledWriter::writeArrayValue(const Value& value) {
485   unsigned size = value.size();
486   if (size == 0)
487     pushValue("[]");
488   else {
489     bool isArrayMultiLine = isMultilineArray(value);
490     if (isArrayMultiLine) {
491       writeWithIndent("[");
492       indent();
493       bool hasChildValue = !childValues_.empty();
494       unsigned index = 0;
495       for (;;) {
496         const Value& childValue = value[index];
497         writeCommentBeforeValue(childValue);
498         if (hasChildValue)
499           writeWithIndent(childValues_[index]);
500         else {
501           writeIndent();
502           writeValue(childValue);
503         }
504         if (++index == size) {
505           writeCommentAfterValueOnSameLine(childValue);
506           break;
507         }
508         document_ += ',';
509         writeCommentAfterValueOnSameLine(childValue);
510       }
511       unindent();
512       writeWithIndent("]");
513     } else // output on a single line
514     {
515       assert(childValues_.size() == size);
516       document_ += "[ ";
517       for (unsigned index = 0; index < size; ++index) {
518         if (index > 0)
519           document_ += ", ";
520         document_ += childValues_[index];
521       }
522       document_ += " ]";
523     }
524   }
525 }
526 
isMultilineArray(const Value & value)527 bool StyledWriter::isMultilineArray(const Value& value) {
528   ArrayIndex const size = value.size();
529   bool isMultiLine = size * 3 >= rightMargin_;
530   childValues_.clear();
531   for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
532     const Value& childValue = value[index];
533     isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
534                         childValue.size() > 0);
535   }
536   if (!isMultiLine) // check if line length > max line length
537   {
538     childValues_.reserve(size);
539     addChildValues_ = true;
540     ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
541     for (ArrayIndex index = 0; index < size; ++index) {
542       if (hasCommentForValue(value[index])) {
543         isMultiLine = true;
544       }
545       writeValue(value[index]);
546       lineLength += static_cast<ArrayIndex>(childValues_[index].length());
547     }
548     addChildValues_ = false;
549     isMultiLine = isMultiLine || lineLength >= rightMargin_;
550   }
551   return isMultiLine;
552 }
553 
pushValue(const JSONCPP_STRING & value)554 void StyledWriter::pushValue(const JSONCPP_STRING& value) {
555   if (addChildValues_)
556     childValues_.push_back(value);
557   else
558     document_ += value;
559 }
560 
writeIndent()561 void StyledWriter::writeIndent() {
562   if (!document_.empty()) {
563     char last = document_[document_.length() - 1];
564     if (last == ' ') // already indented
565       return;
566     if (last != '\n') // Comments may add new-line
567       document_ += '\n';
568   }
569   document_ += indentString_;
570 }
571 
writeWithIndent(const JSONCPP_STRING & value)572 void StyledWriter::writeWithIndent(const JSONCPP_STRING& value) {
573   writeIndent();
574   document_ += value;
575 }
576 
indent()577 void StyledWriter::indent() { indentString_ += JSONCPP_STRING(indentSize_, ' '); }
578 
unindent()579 void StyledWriter::unindent() {
580   assert(indentString_.size() >= indentSize_);
581   indentString_.resize(indentString_.size() - indentSize_);
582 }
583 
writeCommentBeforeValue(const Value & root)584 void StyledWriter::writeCommentBeforeValue(const Value& root) {
585   if (!root.hasComment(commentBefore))
586     return;
587 
588   document_ += "\n";
589   writeIndent();
590   const JSONCPP_STRING& comment = root.getComment(commentBefore);
591   JSONCPP_STRING::const_iterator iter = comment.begin();
592   while (iter != comment.end()) {
593     document_ += *iter;
594     if (*iter == '\n' &&
595        ((iter+1) != comment.end() && *(iter + 1) == '/'))
596       writeIndent();
597     ++iter;
598   }
599 
600   // Comments are stripped of trailing newlines, so add one here
601   document_ += "\n";
602 }
603 
writeCommentAfterValueOnSameLine(const Value & root)604 void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
605   if (root.hasComment(commentAfterOnSameLine))
606     document_ += " " + root.getComment(commentAfterOnSameLine);
607 
608   if (root.hasComment(commentAfter)) {
609     document_ += "\n";
610     document_ += root.getComment(commentAfter);
611     document_ += "\n";
612   }
613 }
614 
hasCommentForValue(const Value & value)615 bool StyledWriter::hasCommentForValue(const Value& value) {
616   return value.hasComment(commentBefore) ||
617          value.hasComment(commentAfterOnSameLine) ||
618          value.hasComment(commentAfter);
619 }
620 
621 // Class StyledStreamWriter
622 // //////////////////////////////////////////////////////////////////
623 
StyledStreamWriter(JSONCPP_STRING indentation)624 StyledStreamWriter::StyledStreamWriter(JSONCPP_STRING indentation)
625     : document_(NULL), rightMargin_(74), indentation_(indentation),
626       addChildValues_() {}
627 
write(JSONCPP_OSTREAM & out,const Value & root)628 void StyledStreamWriter::write(JSONCPP_OSTREAM& out, const Value& root) {
629   document_ = &out;
630   addChildValues_ = false;
631   indentString_.clear();
632   indented_ = true;
633   writeCommentBeforeValue(root);
634   if (!indented_) writeIndent();
635   indented_ = true;
636   writeValue(root);
637   writeCommentAfterValueOnSameLine(root);
638   *document_ << "\n";
639   document_ = NULL; // Forget the stream, for safety.
640 }
641 
writeValue(const Value & value)642 void StyledStreamWriter::writeValue(const Value& value) {
643   switch (value.type()) {
644   case nullValue:
645     pushValue("null");
646     break;
647   case intValue:
648     pushValue(valueToString(value.asLargestInt()));
649     break;
650   case uintValue:
651     pushValue(valueToString(value.asLargestUInt()));
652     break;
653   case realValue:
654     pushValue(valueToString(value.asDouble()));
655     break;
656   case stringValue:
657   {
658     // Is NULL possible for value.string_? No.
659     char const* str;
660     char const* end;
661     bool ok = value.getString(&str, &end);
662     if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
663     else pushValue("");
664     break;
665   }
666   case booleanValue:
667     pushValue(valueToString(value.asBool()));
668     break;
669   case arrayValue:
670     writeArrayValue(value);
671     break;
672   case objectValue: {
673     Value::Members members(value.getMemberNames());
674     if (members.empty())
675       pushValue("{}");
676     else {
677       writeWithIndent("{");
678       indent();
679       Value::Members::iterator it = members.begin();
680       for (;;) {
681         const JSONCPP_STRING& name = *it;
682         const Value& childValue = value[name];
683         writeCommentBeforeValue(childValue);
684         writeWithIndent(valueToQuotedString(name.c_str()));
685         *document_ << " : ";
686         writeValue(childValue);
687         if (++it == members.end()) {
688           writeCommentAfterValueOnSameLine(childValue);
689           break;
690         }
691         *document_ << ",";
692         writeCommentAfterValueOnSameLine(childValue);
693       }
694       unindent();
695       writeWithIndent("}");
696     }
697   } break;
698   }
699 }
700 
writeArrayValue(const Value & value)701 void StyledStreamWriter::writeArrayValue(const Value& value) {
702   unsigned size = value.size();
703   if (size == 0)
704     pushValue("[]");
705   else {
706     bool isArrayMultiLine = isMultilineArray(value);
707     if (isArrayMultiLine) {
708       writeWithIndent("[");
709       indent();
710       bool hasChildValue = !childValues_.empty();
711       unsigned index = 0;
712       for (;;) {
713         const Value& childValue = value[index];
714         writeCommentBeforeValue(childValue);
715         if (hasChildValue)
716           writeWithIndent(childValues_[index]);
717         else {
718           if (!indented_) writeIndent();
719           indented_ = true;
720           writeValue(childValue);
721           indented_ = false;
722         }
723         if (++index == size) {
724           writeCommentAfterValueOnSameLine(childValue);
725           break;
726         }
727         *document_ << ",";
728         writeCommentAfterValueOnSameLine(childValue);
729       }
730       unindent();
731       writeWithIndent("]");
732     } else // output on a single line
733     {
734       assert(childValues_.size() == size);
735       *document_ << "[ ";
736       for (unsigned index = 0; index < size; ++index) {
737         if (index > 0)
738           *document_ << ", ";
739         *document_ << childValues_[index];
740       }
741       *document_ << " ]";
742     }
743   }
744 }
745 
isMultilineArray(const Value & value)746 bool StyledStreamWriter::isMultilineArray(const Value& value) {
747   ArrayIndex const size = value.size();
748   bool isMultiLine = size * 3 >= rightMargin_;
749   childValues_.clear();
750   for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
751     const Value& childValue = value[index];
752     isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
753                         childValue.size() > 0);
754   }
755   if (!isMultiLine) // check if line length > max line length
756   {
757     childValues_.reserve(size);
758     addChildValues_ = true;
759     ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
760     for (ArrayIndex index = 0; index < size; ++index) {
761       if (hasCommentForValue(value[index])) {
762         isMultiLine = true;
763       }
764       writeValue(value[index]);
765       lineLength += static_cast<ArrayIndex>(childValues_[index].length());
766     }
767     addChildValues_ = false;
768     isMultiLine = isMultiLine || lineLength >= rightMargin_;
769   }
770   return isMultiLine;
771 }
772 
pushValue(const JSONCPP_STRING & value)773 void StyledStreamWriter::pushValue(const JSONCPP_STRING& value) {
774   if (addChildValues_)
775     childValues_.push_back(value);
776   else
777     *document_ << value;
778 }
779 
writeIndent()780 void StyledStreamWriter::writeIndent() {
781   // blep intended this to look at the so-far-written string
782   // to determine whether we are already indented, but
783   // with a stream we cannot do that. So we rely on some saved state.
784   // The caller checks indented_.
785   *document_ << '\n' << indentString_;
786 }
787 
writeWithIndent(const JSONCPP_STRING & value)788 void StyledStreamWriter::writeWithIndent(const JSONCPP_STRING& value) {
789   if (!indented_) writeIndent();
790   *document_ << value;
791   indented_ = false;
792 }
793 
indent()794 void StyledStreamWriter::indent() { indentString_ += indentation_; }
795 
unindent()796 void StyledStreamWriter::unindent() {
797   assert(indentString_.size() >= indentation_.size());
798   indentString_.resize(indentString_.size() - indentation_.size());
799 }
800 
writeCommentBeforeValue(const Value & root)801 void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
802   if (!root.hasComment(commentBefore))
803     return;
804 
805   if (!indented_) writeIndent();
806   const JSONCPP_STRING& comment = root.getComment(commentBefore);
807   JSONCPP_STRING::const_iterator iter = comment.begin();
808   while (iter != comment.end()) {
809     *document_ << *iter;
810     if (*iter == '\n' &&
811        ((iter+1) != comment.end() && *(iter + 1) == '/'))
812       // writeIndent();  // would include newline
813       *document_ << indentString_;
814     ++iter;
815   }
816   indented_ = false;
817 }
818 
writeCommentAfterValueOnSameLine(const Value & root)819 void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
820   if (root.hasComment(commentAfterOnSameLine))
821     *document_ << ' ' << root.getComment(commentAfterOnSameLine);
822 
823   if (root.hasComment(commentAfter)) {
824     writeIndent();
825     *document_ << root.getComment(commentAfter);
826   }
827   indented_ = false;
828 }
829 
hasCommentForValue(const Value & value)830 bool StyledStreamWriter::hasCommentForValue(const Value& value) {
831   return value.hasComment(commentBefore) ||
832          value.hasComment(commentAfterOnSameLine) ||
833          value.hasComment(commentAfter);
834 }
835 
836 //////////////////////////
837 // BuiltStyledStreamWriter
838 
839 /// Scoped enums are not available until C++11.
840 struct CommentStyle {
841   /// Decide whether to write comments.
842   enum Enum {
843     None,  ///< Drop all comments.
844     Most,  ///< Recover odd behavior of previous versions (not implemented yet).
845     All  ///< Keep all comments.
846   };
847 };
848 
849 struct BuiltStyledStreamWriter : public StreamWriter
850 {
851   BuiltStyledStreamWriter(
852       JSONCPP_STRING const& indentation,
853       CommentStyle::Enum cs,
854       JSONCPP_STRING const& colonSymbol,
855       JSONCPP_STRING const& nullSymbol,
856       JSONCPP_STRING const& endingLineFeedSymbol,
857       bool useSpecialFloats,
858       unsigned int precision);
859   int write(Value const& root, JSONCPP_OSTREAM* sout) JSONCPP_OVERRIDE;
860 private:
861   void writeValue(Value const& value);
862   void writeArrayValue(Value const& value);
863   bool isMultilineArray(Value const& value);
864   void pushValue(JSONCPP_STRING const& value);
865   void writeIndent();
866   void writeWithIndent(JSONCPP_STRING const& value);
867   void indent();
868   void unindent();
869   void writeCommentBeforeValue(Value const& root);
870   void writeCommentAfterValueOnSameLine(Value const& root);
871   static bool hasCommentForValue(const Value& value);
872 
873   typedef std::vector<JSONCPP_STRING> ChildValues;
874 
875   ChildValues childValues_;
876   JSONCPP_STRING indentString_;
877   unsigned int rightMargin_;
878   JSONCPP_STRING indentation_;
879   CommentStyle::Enum cs_;
880   JSONCPP_STRING colonSymbol_;
881   JSONCPP_STRING nullSymbol_;
882   JSONCPP_STRING endingLineFeedSymbol_;
883   bool addChildValues_ : 1;
884   bool indented_ : 1;
885   bool useSpecialFloats_ : 1;
886   unsigned int precision_;
887 };
BuiltStyledStreamWriter(JSONCPP_STRING const & indentation,CommentStyle::Enum cs,JSONCPP_STRING const & colonSymbol,JSONCPP_STRING const & nullSymbol,JSONCPP_STRING const & endingLineFeedSymbol,bool useSpecialFloats,unsigned int precision)888 BuiltStyledStreamWriter::BuiltStyledStreamWriter(
889       JSONCPP_STRING const& indentation,
890       CommentStyle::Enum cs,
891       JSONCPP_STRING const& colonSymbol,
892       JSONCPP_STRING const& nullSymbol,
893       JSONCPP_STRING const& endingLineFeedSymbol,
894       bool useSpecialFloats,
895       unsigned int precision)
896   : rightMargin_(74)
897   , indentation_(indentation)
898   , cs_(cs)
899   , colonSymbol_(colonSymbol)
900   , nullSymbol_(nullSymbol)
901   , endingLineFeedSymbol_(endingLineFeedSymbol)
902   , addChildValues_(false)
903   , indented_(false)
904   , useSpecialFloats_(useSpecialFloats)
905   , precision_(precision)
906 {
907 }
write(Value const & root,JSONCPP_OSTREAM * sout)908 int BuiltStyledStreamWriter::write(Value const& root, JSONCPP_OSTREAM* sout)
909 {
910   sout_ = sout;
911   addChildValues_ = false;
912   indented_ = true;
913   indentString_.clear();
914   writeCommentBeforeValue(root);
915   if (!indented_) writeIndent();
916   indented_ = true;
917   writeValue(root);
918   writeCommentAfterValueOnSameLine(root);
919   *sout_ << endingLineFeedSymbol_;
920   sout_ = NULL;
921   return 0;
922 }
writeValue(Value const & value)923 void BuiltStyledStreamWriter::writeValue(Value const& value) {
924   switch (value.type()) {
925   case nullValue:
926     pushValue(nullSymbol_);
927     break;
928   case intValue:
929     pushValue(valueToString(value.asLargestInt()));
930     break;
931   case uintValue:
932     pushValue(valueToString(value.asLargestUInt()));
933     break;
934   case realValue:
935     pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_));
936     break;
937   case stringValue:
938   {
939     // Is NULL is possible for value.string_? No.
940     char const* str;
941     char const* end;
942     bool ok = value.getString(&str, &end);
943     if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
944     else pushValue("");
945     break;
946   }
947   case booleanValue:
948     pushValue(valueToString(value.asBool()));
949     break;
950   case arrayValue:
951     writeArrayValue(value);
952     break;
953   case objectValue: {
954     Value::Members members(value.getMemberNames());
955     if (members.empty())
956       pushValue("{}");
957     else {
958       writeWithIndent("{");
959       indent();
960       Value::Members::iterator it = members.begin();
961       for (;;) {
962         JSONCPP_STRING const& name = *it;
963         Value const& childValue = value[name];
964         writeCommentBeforeValue(childValue);
965         writeWithIndent(valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length())));
966         *sout_ << colonSymbol_;
967         writeValue(childValue);
968         if (++it == members.end()) {
969           writeCommentAfterValueOnSameLine(childValue);
970           break;
971         }
972         *sout_ << ",";
973         writeCommentAfterValueOnSameLine(childValue);
974       }
975       unindent();
976       writeWithIndent("}");
977     }
978   } break;
979   }
980 }
981 
writeArrayValue(Value const & value)982 void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
983   unsigned size = value.size();
984   if (size == 0)
985     pushValue("[]");
986   else {
987     bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value);
988     if (isMultiLine) {
989       writeWithIndent("[");
990       indent();
991       bool hasChildValue = !childValues_.empty();
992       unsigned index = 0;
993       for (;;) {
994         Value const& childValue = value[index];
995         writeCommentBeforeValue(childValue);
996         if (hasChildValue)
997           writeWithIndent(childValues_[index]);
998         else {
999           if (!indented_) writeIndent();
1000           indented_ = true;
1001           writeValue(childValue);
1002           indented_ = false;
1003         }
1004         if (++index == size) {
1005           writeCommentAfterValueOnSameLine(childValue);
1006           break;
1007         }
1008         *sout_ << ",";
1009         writeCommentAfterValueOnSameLine(childValue);
1010       }
1011       unindent();
1012       writeWithIndent("]");
1013     } else // output on a single line
1014     {
1015       assert(childValues_.size() == size);
1016       *sout_ << "[";
1017       if (!indentation_.empty()) *sout_ << " ";
1018       for (unsigned index = 0; index < size; ++index) {
1019         if (index > 0)
1020           *sout_ << ((!indentation_.empty()) ? ", " : ",");
1021         *sout_ << childValues_[index];
1022       }
1023       if (!indentation_.empty()) *sout_ << " ";
1024       *sout_ << "]";
1025     }
1026   }
1027 }
1028 
isMultilineArray(Value const & value)1029 bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) {
1030   ArrayIndex const size = value.size();
1031   bool isMultiLine = size * 3 >= rightMargin_;
1032   childValues_.clear();
1033   for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
1034     Value const& childValue = value[index];
1035     isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
1036                         childValue.size() > 0);
1037   }
1038   if (!isMultiLine) // check if line length > max line length
1039   {
1040     childValues_.reserve(size);
1041     addChildValues_ = true;
1042     ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
1043     for (ArrayIndex index = 0; index < size; ++index) {
1044       if (hasCommentForValue(value[index])) {
1045         isMultiLine = true;
1046       }
1047       writeValue(value[index]);
1048       lineLength += static_cast<ArrayIndex>(childValues_[index].length());
1049     }
1050     addChildValues_ = false;
1051     isMultiLine = isMultiLine || lineLength >= rightMargin_;
1052   }
1053   return isMultiLine;
1054 }
1055 
pushValue(JSONCPP_STRING const & value)1056 void BuiltStyledStreamWriter::pushValue(JSONCPP_STRING const& value) {
1057   if (addChildValues_)
1058     childValues_.push_back(value);
1059   else
1060     *sout_ << value;
1061 }
1062 
writeIndent()1063 void BuiltStyledStreamWriter::writeIndent() {
1064   // blep intended this to look at the so-far-written string
1065   // to determine whether we are already indented, but
1066   // with a stream we cannot do that. So we rely on some saved state.
1067   // The caller checks indented_.
1068 
1069   if (!indentation_.empty()) {
1070     // In this case, drop newlines too.
1071     *sout_ << '\n' << indentString_;
1072   }
1073 }
1074 
writeWithIndent(JSONCPP_STRING const & value)1075 void BuiltStyledStreamWriter::writeWithIndent(JSONCPP_STRING const& value) {
1076   if (!indented_) writeIndent();
1077   *sout_ << value;
1078   indented_ = false;
1079 }
1080 
indent()1081 void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
1082 
unindent()1083 void BuiltStyledStreamWriter::unindent() {
1084   assert(indentString_.size() >= indentation_.size());
1085   indentString_.resize(indentString_.size() - indentation_.size());
1086 }
1087 
writeCommentBeforeValue(Value const & root)1088 void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
1089   if (cs_ == CommentStyle::None) return;
1090   if (!root.hasComment(commentBefore))
1091     return;
1092 
1093   if (!indented_) writeIndent();
1094   const JSONCPP_STRING& comment = root.getComment(commentBefore);
1095   JSONCPP_STRING::const_iterator iter = comment.begin();
1096   while (iter != comment.end()) {
1097     *sout_ << *iter;
1098     if (*iter == '\n' &&
1099        ((iter+1) != comment.end() && *(iter + 1) == '/'))
1100       // writeIndent();  // would write extra newline
1101       *sout_ << indentString_;
1102     ++iter;
1103   }
1104   indented_ = false;
1105 }
1106 
writeCommentAfterValueOnSameLine(Value const & root)1107 void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) {
1108   if (cs_ == CommentStyle::None) return;
1109   if (root.hasComment(commentAfterOnSameLine))
1110     *sout_ << " " + root.getComment(commentAfterOnSameLine);
1111 
1112   if (root.hasComment(commentAfter)) {
1113     writeIndent();
1114     *sout_ << root.getComment(commentAfter);
1115   }
1116 }
1117 
1118 // static
hasCommentForValue(const Value & value)1119 bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
1120   return value.hasComment(commentBefore) ||
1121          value.hasComment(commentAfterOnSameLine) ||
1122          value.hasComment(commentAfter);
1123 }
1124 
1125 ///////////////
1126 // StreamWriter
1127 
StreamWriter()1128 StreamWriter::StreamWriter()
1129     : sout_(NULL)
1130 {
1131 }
~StreamWriter()1132 StreamWriter::~StreamWriter()
1133 {
1134 }
~Factory()1135 StreamWriter::Factory::~Factory()
1136 {}
StreamWriterBuilder()1137 StreamWriterBuilder::StreamWriterBuilder()
1138 {
1139   setDefaults(&settings_);
1140 }
~StreamWriterBuilder()1141 StreamWriterBuilder::~StreamWriterBuilder()
1142 {}
newStreamWriter() const1143 StreamWriter* StreamWriterBuilder::newStreamWriter() const
1144 {
1145   JSONCPP_STRING indentation = settings_["indentation"].asString();
1146   JSONCPP_STRING cs_str = settings_["commentStyle"].asString();
1147   bool eyc = settings_["enableYAMLCompatibility"].asBool();
1148   bool dnp = settings_["dropNullPlaceholders"].asBool();
1149   bool usf = settings_["useSpecialFloats"].asBool();
1150   unsigned int pre = settings_["precision"].asUInt();
1151   CommentStyle::Enum cs = CommentStyle::All;
1152   if (cs_str == "All") {
1153     cs = CommentStyle::All;
1154   } else if (cs_str == "None") {
1155     cs = CommentStyle::None;
1156   } else {
1157     throwRuntimeError("commentStyle must be 'All' or 'None'");
1158   }
1159   JSONCPP_STRING colonSymbol = " : ";
1160   if (eyc) {
1161     colonSymbol = ": ";
1162   } else if (indentation.empty()) {
1163     colonSymbol = ":";
1164   }
1165   JSONCPP_STRING nullSymbol = "null";
1166   if (dnp) {
1167     nullSymbol.clear();
1168   }
1169   if (pre > 17) pre = 17;
1170   JSONCPP_STRING endingLineFeedSymbol;
1171   return new BuiltStyledStreamWriter(
1172       indentation, cs,
1173       colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre);
1174 }
getValidWriterKeys(std::set<JSONCPP_STRING> * valid_keys)1175 static void getValidWriterKeys(std::set<JSONCPP_STRING>* valid_keys)
1176 {
1177   valid_keys->clear();
1178   valid_keys->insert("indentation");
1179   valid_keys->insert("commentStyle");
1180   valid_keys->insert("enableYAMLCompatibility");
1181   valid_keys->insert("dropNullPlaceholders");
1182   valid_keys->insert("useSpecialFloats");
1183   valid_keys->insert("precision");
1184 }
validate(Json::Value * invalid) const1185 bool StreamWriterBuilder::validate(Json::Value* invalid) const
1186 {
1187   Json::Value my_invalid;
1188   if (!invalid) invalid = &my_invalid;  // so we do not need to test for NULL
1189   Json::Value& inv = *invalid;
1190   std::set<JSONCPP_STRING> valid_keys;
1191   getValidWriterKeys(&valid_keys);
1192   Value::Members keys = settings_.getMemberNames();
1193   size_t n = keys.size();
1194   for (size_t i = 0; i < n; ++i) {
1195     JSONCPP_STRING const& key = keys[i];
1196     if (valid_keys.find(key) == valid_keys.end()) {
1197       inv[key] = settings_[key];
1198     }
1199   }
1200   return 0u == inv.size();
1201 }
operator [](JSONCPP_STRING key)1202 Value& StreamWriterBuilder::operator[](JSONCPP_STRING key)
1203 {
1204   return settings_[key];
1205 }
1206 // static
setDefaults(Json::Value * settings)1207 void StreamWriterBuilder::setDefaults(Json::Value* settings)
1208 {
1209   //! [StreamWriterBuilderDefaults]
1210   (*settings)["commentStyle"] = "All";
1211   (*settings)["indentation"] = "\t";
1212   (*settings)["enableYAMLCompatibility"] = false;
1213   (*settings)["dropNullPlaceholders"] = false;
1214   (*settings)["useSpecialFloats"] = false;
1215   (*settings)["precision"] = 17;
1216   //! [StreamWriterBuilderDefaults]
1217 }
1218 
writeString(StreamWriter::Factory const & builder,Value const & root)1219 JSONCPP_STRING writeString(StreamWriter::Factory const& builder, Value const& root) {
1220   JSONCPP_OSTRINGSTREAM sout;
1221   StreamWriterPtr const writer(builder.newStreamWriter());
1222   writer->write(root, &sout);
1223   return sout.str();
1224 }
1225 
operator <<(JSONCPP_OSTREAM & sout,Value const & root)1226 JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM& sout, Value const& root) {
1227   StreamWriterBuilder builder;
1228   StreamWriterPtr const writer(builder.newStreamWriter());
1229   writer->write(root, &sout);
1230   return sout;
1231 }
1232 
1233 } // namespace Json
1234