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