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