ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/cf.schmorp.de/dclient/src/backend/json/json_writer.cpp
Revision: 1.1
Committed: Sun Oct 17 08:15:06 2010 UTC (15 years, 11 months ago) by sf-pippijn
Branch: MAIN
CVS Tags: HEAD
Log Message:
initial import

File Contents

# Content
1 #include <json/writer.h>
2 #include <utility>
3 #include <assert.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <iostream>
7 #include <sstream>
8 #include <iomanip>
9
10 #if _MSC_VER >= 1400 // VC++ 8.0
11 #pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
12 #endif
13
14 namespace Json
15 {
16
17 static bool
18 isControlCharacter (char ch)
19 {
20 return ch > 0 && ch <= 0x1F;
21 }
22
23 static bool
24 containsControlCharacter (const char *str)
25 {
26 while (*str)
27 if (isControlCharacter (*(str++)))
28 return true;
29 return false;
30 }
31
32 static void
33 uintToString (unsigned int value, char * &current)
34 {
35 *--current = 0;
36 do
37 {
38 *--current = (value % 10) + '0';
39 value /= 10;
40 }
41 while (value != 0);
42 }
43
44 std::string
45 valueToString (Int value)
46 {
47 char buffer[32];
48 char *current = buffer + sizeof (buffer);
49 bool isNegative = value < 0;
50
51 if (isNegative)
52 value = -value;
53 uintToString (UInt (value), current);
54 if (isNegative)
55 *--current = '-';
56 assert (current >= buffer);
57 return current;
58 }
59
60 std::string
61 valueToString (UInt value)
62 {
63 char buffer[32];
64 char *current = buffer + sizeof (buffer);
65
66 uintToString (value, current);
67 assert (current >= buffer);
68 return current;
69 }
70
71 std::string
72 valueToString (double value)
73 {
74 char buffer[32];
75
76 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) /* Use secure version with visual studio 2005 to avoid warning. */
77 sprintf_s (buffer, sizeof (buffer), "%#.16g", value);
78 #else
79 sprintf (buffer, "%#.16g", value);
80 #endif
81 char *ch = buffer + strlen (buffer) - 1;
82 if (*ch != '0')
83 return buffer; /* nothing to truncate, so save time */
84 while (ch > buffer && *ch == '0')
85 --ch;
86 char *last_nonzero = ch;
87 while (ch >= buffer)
88 {
89 switch (*ch)
90 {
91 case '0':
92 case '1':
93 case '2':
94 case '3':
95 case '4':
96 case '5':
97 case '6':
98 case '7':
99 case '8':
100 case '9':
101 --ch;
102 continue;
103 case '.':
104 /* Truncate zeroes to save bytes in output, but keep one. */
105 *(last_nonzero + 2) = '\0';
106 return buffer;
107 default:
108 return buffer;
109 }
110 }
111 return buffer;
112 }
113
114 std::string
115 valueToString (bool value)
116 {
117 return value ? "true" : "false";
118 }
119
120 std::string
121 valueToQuotedString (const char *value)
122 {
123 /* Not sure how to handle unicode... */
124 if (strpbrk (value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter (value))
125 return std::string ("\"") + value + "\"";
126 /*
127 * We have to walk value and escape any special characters.
128 * Appending to std::string is not efficient, but this should be rare.
129 * (Note: forward slashes are *not* rare, but I am not escaping them.)
130 */
131 unsigned maxsize = strlen (value) * 2 + 3; /* allescaped+quotes+NULL */
132 std::string result;
133 result.reserve (maxsize); /* to avoid lots of mallocs */
134 result += "\"";
135 for (const char *c = value; *c != 0; ++c)
136 {
137 switch (*c)
138 {
139 case '\"':
140 result += "\\\"";
141 break;
142 case '\\':
143 result += "\\\\";
144 break;
145 case '\b':
146 result += "\\b";
147 break;
148 case '\f':
149 result += "\\f";
150 break;
151 case '\n':
152 result += "\\n";
153 break;
154 case '\r':
155 result += "\\r";
156 break;
157 case '\t':
158 result += "\\t";
159 break;
160 /*
161 * case '/':
162 * Even though \/ is considered a legal escape in JSON, a bare
163 * slash is also legal, so I see no reason to escape it.
164 * (I hope I am not misunderstanding something.
165 * blep notes: actually escaping \/ may be useful in javascript to avoid </
166 * sequence.
167 * Should add a flag to allow this compatibility mode and prevent this
168 * sequence from occurring.
169 */
170 default:
171 if (isControlCharacter (*c))
172 {
173 std::ostringstream oss;
174 oss << "\\u" << std::hex << std::uppercase << std::setfill ('0') << std::setw (4) << static_cast<int> (*c);
175 result += oss.str ();
176 }
177 else
178 result += *c;
179 break;
180 }
181 }
182 result += "\"";
183 return result;
184 }
185
186 /*
187 * Class Writer
188 * //////////////////////////////////////////////////////////////////
189 */
190 Writer::~Writer ()
191 {
192 }
193
194 /*
195 * Class FastWriter
196 * //////////////////////////////////////////////////////////////////
197 */
198
199 FastWriter::FastWriter ()
200 : yamlCompatiblityEnabled_ (false)
201 {
202 }
203
204 void
205 FastWriter::enableYAMLCompatibility ()
206 {
207 yamlCompatiblityEnabled_ = true;
208 }
209
210 std::string
211 FastWriter::write (const Value &root)
212 {
213 document_ = "";
214 writeValue (root);
215 return document_;
216 }
217
218 void
219 FastWriter::writeValue (const Value &value)
220 {
221 switch (value.type ())
222 {
223 case nullValue:
224 document_ += "null";
225 break;
226 case intValue:
227 document_ += valueToString (value.asInt ());
228 break;
229 case uintValue:
230 document_ += valueToString (value.asUInt ());
231 break;
232 case realValue:
233 document_ += valueToString (value.asDouble ());
234 break;
235 case stringValue:
236 document_ += valueToQuotedString (value.asCString ());
237 break;
238 case booleanValue:
239 document_ += valueToString (value.asBool ());
240 break;
241 case arrayValue:
242 {
243 document_ += "[";
244 int size = value.size ();
245 for (int index = 0; index < size; ++index)
246 {
247 if (index > 0)
248 document_ += ",";
249 writeValue (value[index]);
250 }
251 document_ += "]";
252 break;
253 }
254 case objectValue:
255 {
256 Value::Members members (value.getMemberNames ());
257 document_ += "{";
258 for (Value::Members::iterator it = members.begin ();
259 it != members.end ();
260 ++it)
261 {
262 const std::string &name = *it;
263 if (it != members.begin ())
264 document_ += ",";
265 document_ += valueToQuotedString (name.c_str ());
266 document_ += yamlCompatiblityEnabled_ ? ": "
267 : ":";
268 writeValue (value[name]);
269 }
270 document_ += "}";
271 break;
272 }
273 }
274 }
275
276 /*
277 * Class StyledWriter
278 * //////////////////////////////////////////////////////////////////
279 */
280
281 StyledWriter::StyledWriter ()
282 : rightMargin_ (74)
283 , indentSize_ (3)
284 {
285 }
286
287 std::string
288 StyledWriter::write (const Value &root)
289 {
290 document_ = "";
291 addChildValues_ = false;
292 indentString_ = "";
293 writeCommentBeforeValue (root);
294 writeValue (root);
295 writeCommentAfterValueOnSameLine (root);
296 document_ += "\n";
297 return document_;
298 }
299
300 void
301 StyledWriter::writeValue (const Value &value)
302 {
303 switch (value.type ())
304 {
305 case nullValue:
306 pushValue ("null");
307 break;
308 case intValue:
309 pushValue (valueToString (value.asInt ()));
310 break;
311 case uintValue:
312 pushValue (valueToString (value.asUInt ()));
313 break;
314 case realValue:
315 pushValue (valueToString (value.asDouble ()));
316 break;
317 case stringValue:
318 pushValue (valueToQuotedString (value.asCString ()));
319 break;
320 case booleanValue:
321 pushValue (valueToString (value.asBool ()));
322 break;
323 case arrayValue:
324 writeArrayValue (value);
325 break;
326 case objectValue:
327 {
328 Value::Members members (value.getMemberNames ());
329 if (members.empty ())
330 pushValue ("{}");
331 else
332 {
333 writeWithIndent ("{");
334 indent ();
335 Value::Members::iterator it = members.begin ();
336 while (true)
337 {
338 const std::string &name = *it;
339 const Value &childValue = value[name];
340 writeCommentBeforeValue (childValue);
341 writeWithIndent (valueToQuotedString (name.c_str ()));
342 document_ += " : ";
343 writeValue (childValue);
344 if (++it == members.end ())
345 {
346 writeCommentAfterValueOnSameLine (childValue);
347 break;
348 }
349 document_ += ",";
350 writeCommentAfterValueOnSameLine (childValue);
351 }
352 unindent ();
353 writeWithIndent ("}");
354 }
355 break;
356 }
357 }
358 }
359
360 void
361 StyledWriter::writeArrayValue (const Value &value)
362 {
363 unsigned size = value.size ();
364
365 if (size == 0)
366 pushValue ("[]");
367 else
368 {
369 bool isArrayMultiLine = isMultineArray (value);
370 if (isArrayMultiLine)
371 {
372 writeWithIndent ("[");
373 indent ();
374 bool hasChildValue = !childValues_.empty ();
375 unsigned index = 0;
376 while (true)
377 {
378 const Value &childValue = value[index];
379 writeCommentBeforeValue (childValue);
380 if (hasChildValue)
381 writeWithIndent (childValues_[index]);
382 else
383 {
384 writeIndent ();
385 writeValue (childValue);
386 }
387 if (++index == size)
388 {
389 writeCommentAfterValueOnSameLine (childValue);
390 break;
391 }
392 document_ += ",";
393 writeCommentAfterValueOnSameLine (childValue);
394 }
395 unindent ();
396 writeWithIndent ("]");
397 }
398 else /* output on a single line */
399 {
400 assert (childValues_.size () == size);
401 document_ += "[ ";
402 for (unsigned index = 0; index < size; ++index)
403 {
404 if (index > 0)
405 document_ += ", ";
406 document_ += childValues_[index];
407 }
408 document_ += " ]";
409 }
410 }
411 }
412
413 bool
414 StyledWriter::isMultineArray (const Value &value)
415 {
416 int size = value.size ();
417 bool isMultiLine = size * 3 >= rightMargin_;
418
419 childValues_.clear ();
420 for (int index = 0; index < size && !isMultiLine; ++index)
421 {
422 const Value &childValue = value[index];
423 isMultiLine = isMultiLine ||
424 ((childValue.isArray () || childValue.isObject ()) &&
425 childValue.size () > 0);
426 }
427 if (!isMultiLine) /* check if line length > max line length */
428 {
429 childValues_.reserve (size);
430 addChildValues_ = true;
431 int lineLength = 4 + (size - 1) * 2; /* '[ ' + ', '*n + ' ]' */
432 for (int index = 0; index < size && !isMultiLine; ++index)
433 {
434 writeValue (value[index]);
435 lineLength += int(childValues_[index].length ());
436 isMultiLine = isMultiLine && hasCommentForValue (value[index]);
437 }
438 addChildValues_ = false;
439 isMultiLine = isMultiLine || lineLength >= rightMargin_;
440 }
441 return isMultiLine;
442 }
443
444 void
445 StyledWriter::pushValue (const std::string &value)
446 {
447 if (addChildValues_)
448 childValues_.push_back (value);
449 else
450 document_ += value;
451 }
452
453 void
454 StyledWriter::writeIndent ()
455 {
456 if (!document_.empty ())
457 {
458 char last = document_[document_.length () - 1];
459 if (last == ' ') /* already indented */
460 return;
461 if (last != '\n') /* Comments may add new-line */
462 document_ += '\n';
463 }
464 document_ += indentString_;
465 }
466
467 void
468 StyledWriter::writeWithIndent (const std::string &value)
469 {
470 writeIndent ();
471 document_ += value;
472 }
473
474 void
475 StyledWriter::indent ()
476 {
477 indentString_ += std::string (indentSize_, ' ');
478 }
479
480 void
481 StyledWriter::unindent ()
482 {
483 assert (int(indentString_.size ()) >= indentSize_);
484 indentString_.resize (indentString_.size () - indentSize_);
485 }
486
487 void
488 StyledWriter::writeCommentBeforeValue (const Value &root)
489 {
490 if (!root.hasComment (commentBefore))
491 return;
492 document_ += normalizeEOL (root.getComment (commentBefore));
493 document_ += "\n";
494 }
495
496 void
497 StyledWriter::writeCommentAfterValueOnSameLine (const Value &root)
498 {
499 if (root.hasComment (commentAfterOnSameLine))
500 document_ += " " + normalizeEOL (root.getComment (commentAfterOnSameLine));
501
502 if (root.hasComment (commentAfter))
503 {
504 document_ += "\n";
505 document_ += normalizeEOL (root.getComment (commentAfter));
506 document_ += "\n";
507 }
508 }
509
510 bool
511 StyledWriter::hasCommentForValue (const Value &value)
512 {
513 return value.hasComment (commentBefore)
514 || value.hasComment (commentAfterOnSameLine)
515 || value.hasComment (commentAfter);
516 }
517
518 std::string
519 StyledWriter::normalizeEOL (const std::string &text)
520 {
521 std::string normalized;
522
523 normalized.reserve (text.length ());
524 const char *begin = text.c_str ();
525 const char *end = begin + text.length ();
526 const char *current = begin;
527 while (current != end)
528 {
529 char c = *current++;
530 if (c == '\r') /* mac or dos EOL */
531 {
532 if (*current == '\n') /* convert dos EOL */
533 ++current;
534 normalized += '\n';
535 }
536 else /* handle unix EOL & other char */
537 normalized += c;
538 }
539 return normalized;
540 }
541
542 /*
543 * Class StyledStreamWriter
544 * //////////////////////////////////////////////////////////////////
545 */
546
547 StyledStreamWriter::StyledStreamWriter (std::string indentation)
548 : document_ (NULL)
549 , indentation_ (indentation)
550 , rightMargin_ (74)
551 {
552 }
553
554 void
555 StyledStreamWriter::write (std::ostream &out, const Value &root)
556 {
557 document_ = &out;
558 addChildValues_ = false;
559 indentString_ = "";
560 writeCommentBeforeValue (root);
561 writeValue (root);
562 writeCommentAfterValueOnSameLine (root);
563 *document_ << "\n";
564 document_ = NULL; /* Forget the stream, for safety. */
565 }
566
567 void
568 StyledStreamWriter::writeValue (const Value &value)
569 {
570 switch (value.type ())
571 {
572 case nullValue:
573 pushValue ("null");
574 break;
575 case intValue:
576 pushValue (valueToString (value.asInt ()));
577 break;
578 case uintValue:
579 pushValue (valueToString (value.asUInt ()));
580 break;
581 case realValue:
582 pushValue (valueToString (value.asDouble ()));
583 break;
584 case stringValue:
585 pushValue (valueToQuotedString (value.asCString ()));
586 break;
587 case booleanValue:
588 pushValue (valueToString (value.asBool ()));
589 break;
590 case arrayValue:
591 writeArrayValue (value);
592 break;
593 case objectValue:
594 {
595 Value::Members members (value.getMemberNames ());
596 if (members.empty ())
597 pushValue ("{}");
598 else
599 {
600 writeWithIndent ("{");
601 indent ();
602 Value::Members::iterator it = members.begin ();
603 while (true)
604 {
605 const std::string &name = *it;
606 const Value &childValue = value[name];
607 writeCommentBeforeValue (childValue);
608 writeWithIndent (valueToQuotedString (name.c_str ()));
609 *document_ << " : ";
610 writeValue (childValue);
611 if (++it == members.end ())
612 {
613 writeCommentAfterValueOnSameLine (childValue);
614 break;
615 }
616 *document_ << ",";
617 writeCommentAfterValueOnSameLine (childValue);
618 }
619 unindent ();
620 writeWithIndent ("}");
621 }
622 break;
623 }
624 }
625 }
626
627 void
628 StyledStreamWriter::writeArrayValue (const Value &value)
629 {
630 unsigned size = value.size ();
631
632 if (size == 0)
633 pushValue ("[]");
634 else
635 {
636 bool isArrayMultiLine = isMultineArray (value);
637 if (isArrayMultiLine)
638 {
639 writeWithIndent ("[");
640 indent ();
641 bool hasChildValue = !childValues_.empty ();
642 unsigned index = 0;
643 while (true)
644 {
645 const Value &childValue = value[index];
646 writeCommentBeforeValue (childValue);
647 if (hasChildValue)
648 writeWithIndent (childValues_[index]);
649 else
650 {
651 writeIndent ();
652 writeValue (childValue);
653 }
654 if (++index == size)
655 {
656 writeCommentAfterValueOnSameLine (childValue);
657 break;
658 }
659 *document_ << ",";
660 writeCommentAfterValueOnSameLine (childValue);
661 }
662 unindent ();
663 writeWithIndent ("]");
664 }
665 else /* output on a single line */
666 {
667 assert (childValues_.size () == size);
668 *document_ << "[ ";
669 for (unsigned index = 0; index < size; ++index)
670 {
671 if (index > 0)
672 *document_ << ", ";
673 *document_ << childValues_[index];
674 }
675 *document_ << " ]";
676 }
677 }
678 }
679
680 bool
681 StyledStreamWriter::isMultineArray (const Value &value)
682 {
683 int size = value.size ();
684 bool isMultiLine = size * 3 >= rightMargin_;
685
686 childValues_.clear ();
687 for (int index = 0; index < size && !isMultiLine; ++index)
688 {
689 const Value &childValue = value[index];
690 isMultiLine = isMultiLine ||
691 ((childValue.isArray () || childValue.isObject ()) &&
692 childValue.size () > 0);
693 }
694 if (!isMultiLine) /* check if line length > max line length */
695 {
696 childValues_.reserve (size);
697 addChildValues_ = true;
698 int lineLength = 4 + (size - 1) * 2; /* '[ ' + ', '*n + ' ]' */
699 for (int index = 0; index < size && !isMultiLine; ++index)
700 {
701 writeValue (value[index]);
702 lineLength += int(childValues_[index].length ());
703 isMultiLine = isMultiLine && hasCommentForValue (value[index]);
704 }
705 addChildValues_ = false;
706 isMultiLine = isMultiLine || lineLength >= rightMargin_;
707 }
708 return isMultiLine;
709 }
710
711 void
712 StyledStreamWriter::pushValue (const std::string &value)
713 {
714 if (addChildValues_)
715 childValues_.push_back (value);
716 else
717 *document_ << value;
718 }
719
720 void
721 StyledStreamWriter::writeIndent ()
722 {
723 /*
724 * Some comments in this method would have been nice. ;-)
725 *
726 * if ( !document_.empty() )
727 * {
728 * char last = document_[document_.length()-1];
729 * if ( last == ' ' ) // already indented
730 * return;
731 * if ( last != '\n' ) // Comments may add new-line
732 * *document_ << '\n';
733 * }
734 */
735 *document_ << '\n' << indentString_;
736 }
737
738 void
739 StyledStreamWriter::writeWithIndent (const std::string &value)
740 {
741 writeIndent ();
742 *document_ << value;
743 }
744
745 void
746 StyledStreamWriter::indent ()
747 {
748 indentString_ += indentation_;
749 }
750
751 void
752 StyledStreamWriter::unindent ()
753 {
754 assert (indentString_.size () >= indentation_.size ());
755 indentString_.resize (indentString_.size () - indentation_.size ());
756 }
757
758 void
759 StyledStreamWriter::writeCommentBeforeValue (const Value &root)
760 {
761 if (!root.hasComment (commentBefore))
762 return;
763 *document_ << normalizeEOL (root.getComment (commentBefore));
764 *document_ << "\n";
765 }
766
767 void
768 StyledStreamWriter::writeCommentAfterValueOnSameLine (const Value &root)
769 {
770 if (root.hasComment (commentAfterOnSameLine))
771 *document_ << " " + normalizeEOL (root.getComment (commentAfterOnSameLine));
772
773 if (root.hasComment (commentAfter))
774 {
775 *document_ << "\n";
776 *document_ << normalizeEOL (root.getComment (commentAfter));
777 *document_ << "\n";
778 }
779 }
780
781 bool
782 StyledStreamWriter::hasCommentForValue (const Value &value)
783 {
784 return value.hasComment (commentBefore)
785 || value.hasComment (commentAfterOnSameLine)
786 || value.hasComment (commentAfter);
787 }
788
789 std::string
790 StyledStreamWriter::normalizeEOL (const std::string &text)
791 {
792 std::string normalized;
793
794 normalized.reserve (text.length ());
795 const char *begin = text.c_str ();
796 const char *end = begin + text.length ();
797 const char *current = begin;
798 while (current != end)
799 {
800 char c = *current++;
801 if (c == '\r') /* mac or dos EOL */
802 {
803 if (*current == '\n') /* convert dos EOL */
804 ++current;
805 normalized += '\n';
806 }
807 else /* handle unix EOL & other char */
808 normalized += c;
809 }
810 return normalized;
811 }
812
813 std::ostream &
814 operator << (std::ostream &sout, const Value &root)
815 {
816 Json::FastWriter writer;
817
818 return sout << writer.write (root);
819 }
820
821 } // namespace Json