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