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