~drizzle-trunk/drizzle/development

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