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