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