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