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