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