~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
38
#include <config.h>
39
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 /
40
#include <plugin/json_server/json/reader.h>
41
#include <plugin/json_server/json/value.h>
2295.1.1 by Brian Aker
Fix up stewart JSON patch.
42
43
#include <cassert>
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 <cstdio>
45
#include <cstring>
46
#include <iostream>
47
#include <stdexcept>
2295.1.1 by Brian Aker
Fix up stewart JSON patch.
48
#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 /
49
50
namespace Json {
51
52
// Implementation of class Features
53
// ////////////////////////////////
54
55
Features::Features()
56
   : allowComments_( true )
57
   , strictRoot_( false )
58
{
59
}
60
61
62
Features 
63
Features::all()
64
{
65
   return Features();
66
}
67
68
69
Features 
70
Features::strictMode()
71
{
72
   Features features;
73
   features.allowComments_ = false;
74
   features.strictRoot_ = true;
75
   return features;
76
}
77
78
// Implementation of class Reader
79
// ////////////////////////////////
80
81
82
static inline bool 
83
in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 )
84
{
85
   return c == c1  ||  c == c2  ||  c == c3  ||  c == c4;
86
}
87
88
static inline bool 
89
in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 )
90
{
91
   return c == c1  ||  c == c2  ||  c == c3  ||  c == c4  ||  c == c5;
92
}
93
94
95
static bool 
96
containsNewLine( Reader::Location begin, 
97
                 Reader::Location end )
98
{
99
   for ( ;begin < end; ++begin )
100
      if ( *begin == '\n'  ||  *begin == '\r' )
101
         return true;
102
   return false;
103
}
104
105
static std::string codePointToUTF8(unsigned int cp)
106
{
107
   std::string result;
108
   
109
   // based on description from http://en.wikipedia.org/wiki/UTF-8
110
111
   if (cp <= 0x7f) 
112
   {
113
      result.resize(1);
114
      result[0] = static_cast<char>(cp);
115
   } 
116
   else if (cp <= 0x7FF) 
117
   {
118
      result.resize(2);
119
      result[1] = static_cast<char>(0x80 | (0x3f & cp));
120
      result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
121
   } 
122
   else if (cp <= 0xFFFF) 
123
   {
124
      result.resize(3);
125
      result[2] = static_cast<char>(0x80 | (0x3f & cp));
126
      result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6)));
127
      result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12)));
128
   }
129
   else if (cp <= 0x10FFFF) 
130
   {
131
      result.resize(4);
132
      result[3] = static_cast<char>(0x80 | (0x3f & cp));
133
      result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
134
      result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
135
      result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
136
   }
137
138
   return result;
139
}
140
141
142
// Class Reader
143
// //////////////////////////////////////////////////////////////////
144
145
Reader::Reader()
146
   : features_( Features::all() )
147
{
148
}
149
150
151
Reader::Reader( const Features &features )
152
   : features_( features )
153
{
154
}
155
156
157
bool
158
Reader::parse( const std::string &document, 
159
               Value &root,
160
               bool collectComments )
161
{
162
   document_ = document;
163
   const char *begin = document_.c_str();
164
   const char *end = begin + document_.length();
165
   return parse( begin, end, root, collectComments );
166
}
167
168
169
bool
170
Reader::parse( std::istream& sin,
171
               Value &root,
172
               bool collectComments )
173
{
174
   //std::istream_iterator<char> begin(sin);
175
   //std::istream_iterator<char> end;
176
   // Those would allow streamed input from a file, if parse() were a
177
   // template function.
178
179
   // Since std::string is reference-counted, this at least does not
180
   // create an extra copy.
181
   std::string doc;
182
   std::getline(sin, doc, (char)EOF);
183
   return parse( doc, root, collectComments );
184
}
185
186
bool 
187
Reader::parse( const char *beginDoc, const char *endDoc, 
188
               Value &root,
189
               bool collectComments )
190
{
191
   if ( !features_.allowComments_ )
192
   {
193
      collectComments = false;
194
   }
195
196
   begin_ = beginDoc;
197
   end_ = endDoc;
198
   collectComments_ = collectComments;
199
   current_ = begin_;
200
   lastValueEnd_ = 0;
201
   lastValue_ = 0;
202
   commentsBefore_ = "";
203
   errors_.clear();
204
   while ( !nodes_.empty() )
205
      nodes_.pop();
206
   nodes_.push( &root );
207
   
208
   bool successful = readValue();
209
   Token token;
210
   skipCommentTokens( token );
211
   if ( collectComments_  &&  !commentsBefore_.empty() )
212
      root.setComment( commentsBefore_, commentAfter );
213
   if ( features_.strictRoot_ )
214
   {
215
      if ( !root.isArray()  &&  !root.isObject() )
216
      {
217
         // Set error location to start of doc, ideally should be first token found in doc
218
         token.type_ = tokenError;
219
         token.start_ = beginDoc;
220
         token.end_ = endDoc;
221
         addError( "A valid JSON document must be either an array or an object value.",
222
                   token );
223
         return false;
224
      }
225
   }
226
   return successful;
227
}
228
229
230
bool
231
Reader::readValue()
232
{
233
   Token token;
234
   skipCommentTokens( token );
235
   bool successful = true;
236
237
   if ( collectComments_  &&  !commentsBefore_.empty() )
238
   {
239
      currentValue().setComment( commentsBefore_, commentBefore );
240
      commentsBefore_ = "";
241
   }
242
243
244
   switch ( token.type_ )
245
   {
246
   case tokenObjectBegin:
247
      successful = readObject( token );
248
      break;
249
   case tokenArrayBegin:
250
      successful = readArray( token );
251
      break;
252
   case tokenNumber:
253
      successful = decodeNumber( token );
254
      break;
255
   case tokenString:
256
      successful = decodeString( token );
257
      break;
258
   case tokenTrue:
259
      currentValue() = true;
260
      break;
261
   case tokenFalse:
262
      currentValue() = false;
263
      break;
264
   case tokenNull:
265
      currentValue() = Value();
266
      break;
267
   default:
268
      return addError( "Syntax error: value, object or array expected.", token );
269
   }
270
271
   if ( collectComments_ )
272
   {
273
      lastValueEnd_ = current_;
274
      lastValue_ = &currentValue();
275
   }
276
277
   return successful;
278
}
279
280
281
void 
282
Reader::skipCommentTokens( Token &token )
283
{
284
   if ( features_.allowComments_ )
285
   {
286
      do
287
      {
288
         readToken( token );
289
      }
290
      while ( token.type_ == tokenComment );
291
   }
292
   else
293
   {
294
      readToken( token );
295
   }
296
}
297
298
299
bool 
300
Reader::expectToken( TokenType type, Token &token, const char *message )
301
{
302
   readToken( token );
303
   if ( token.type_ != type )
304
      return addError( message, token );
305
   return true;
306
}
307
308
309
bool 
310
Reader::readToken( Token &token )
311
{
312
   skipSpaces();
313
   token.start_ = current_;
314
   Char c = getNextChar();
315
   bool ok = true;
316
   switch ( c )
317
   {
318
   case '{':
319
      token.type_ = tokenObjectBegin;
320
      break;
321
   case '}':
322
      token.type_ = tokenObjectEnd;
323
      break;
324
   case '[':
325
      token.type_ = tokenArrayBegin;
326
      break;
327
   case ']':
328
      token.type_ = tokenArrayEnd;
329
      break;
330
   case '"':
331
      token.type_ = tokenString;
332
      ok = readString();
333
      break;
334
   case '/':
335
      token.type_ = tokenComment;
336
      ok = readComment();
337
      break;
338
   case '0':
339
   case '1':
340
   case '2':
341
   case '3':
342
   case '4':
343
   case '5':
344
   case '6':
345
   case '7':
346
   case '8':
347
   case '9':
348
   case '-':
349
      token.type_ = tokenNumber;
350
      readNumber();
351
      break;
352
   case 't':
353
      token.type_ = tokenTrue;
354
      ok = match( "rue", 3 );
355
      break;
356
   case 'f':
357
      token.type_ = tokenFalse;
358
      ok = match( "alse", 4 );
359
      break;
360
   case 'n':
361
      token.type_ = tokenNull;
362
      ok = match( "ull", 3 );
363
      break;
364
   case ',':
365
      token.type_ = tokenArraySeparator;
366
      break;
367
   case ':':
368
      token.type_ = tokenMemberSeparator;
369
      break;
370
   case 0:
371
      token.type_ = tokenEndOfStream;
372
      break;
373
   default:
374
      ok = false;
375
      break;
376
   }
377
   if ( !ok )
378
      token.type_ = tokenError;
379
   token.end_ = current_;
380
   return true;
381
}
382
383
384
void 
385
Reader::skipSpaces()
386
{
387
   while ( current_ != end_ )
388
   {
389
      Char c = *current_;
390
      if ( c == ' '  ||  c == '\t'  ||  c == '\r'  ||  c == '\n' )
391
         ++current_;
392
      else
393
         break;
394
   }
395
}
396
397
398
bool 
399
Reader::match( Location pattern, 
400
               int patternLength )
401
{
402
   if ( end_ - current_ < patternLength )
403
      return false;
404
   int index = patternLength;
405
   while ( index-- )
406
      if ( current_[index] != pattern[index] )
407
         return false;
408
   current_ += patternLength;
409
   return true;
410
}
411
412
413
bool
414
Reader::readComment()
415
{
416
   Location commentBegin = current_ - 1;
417
   Char c = getNextChar();
418
   bool successful = false;
419
   if ( c == '*' )
420
      successful = readCStyleComment();
421
   else if ( c == '/' )
422
      successful = readCppStyleComment();
423
   if ( !successful )
424
      return false;
425
426
   if ( collectComments_ )
427
   {
428
      CommentPlacement placement = commentBefore;
429
      if ( lastValueEnd_  &&  !containsNewLine( lastValueEnd_, commentBegin ) )
430
      {
431
         if ( c != '*'  ||  !containsNewLine( commentBegin, current_ ) )
432
            placement = commentAfterOnSameLine;
433
      }
434
435
      addComment( commentBegin, current_, placement );
436
   }
437
   return true;
438
}
439
440
441
void 
442
Reader::addComment( Location begin, 
443
                    Location end, 
444
                    CommentPlacement placement )
445
{
446
   assert( collectComments_ );
447
   if ( placement == commentAfterOnSameLine )
448
   {
449
      assert( lastValue_ != 0 );
450
      lastValue_->setComment( std::string( begin, end ), placement );
451
   }
452
   else
453
   {
454
      if ( !commentsBefore_.empty() )
455
         commentsBefore_ += "\n";
456
      commentsBefore_ += std::string( begin, end );
457
   }
458
}
459
460
461
bool 
462
Reader::readCStyleComment()
463
{
464
   while ( current_ != end_ )
465
   {
466
      Char c = getNextChar();
467
      if ( c == '*'  &&  *current_ == '/' )
468
         break;
469
   }
470
   return getNextChar() == '/';
471
}
472
473
474
bool 
475
Reader::readCppStyleComment()
476
{
477
   while ( current_ != end_ )
478
   {
479
      Char c = getNextChar();
480
      if (  c == '\r'  ||  c == '\n' )
481
         break;
482
   }
483
   return true;
484
}
485
486
487
void 
488
Reader::readNumber()
489
{
490
   while ( current_ != end_ )
491
   {
492
      if ( !(*current_ >= '0'  &&  *current_ <= '9')  &&
493
           !in( *current_, '.', 'e', 'E', '+', '-' ) )
494
         break;
495
      ++current_;
496
   }
497
}
498
499
bool
500
Reader::readString()
501
{
502
   Char c = 0;
503
   while ( current_ != end_ )
504
   {
505
      c = getNextChar();
506
      if ( c == '\\' )
507
         getNextChar();
508
      else if ( c == '"' )
509
         break;
510
   }
511
   return c == '"';
512
}
513
514
515
bool 
516
Reader::readObject( Token & )
517
{
518
   Token tokenName;
519
   std::string name;
520
   currentValue() = Value( objectValue );
521
   while ( readToken( tokenName ) )
522
   {
523
      bool initialTokenOk = true;
524
      while ( tokenName.type_ == tokenComment  &&  initialTokenOk )
525
         initialTokenOk = readToken( tokenName );
526
      if  ( !initialTokenOk )
527
         break;
528
      if ( tokenName.type_ == tokenObjectEnd  &&  name.empty() )  // empty object
529
         return true;
530
      if ( tokenName.type_ != tokenString )
531
         break;
532
      
533
      name = "";
534
      if ( !decodeString( tokenName, name ) )
535
         return recoverFromError( tokenObjectEnd );
536
537
      Token colon;
538
      if ( !readToken( colon ) ||  colon.type_ != tokenMemberSeparator )
539
      {
540
         return addErrorAndRecover( "Missing ':' after object member name", 
541
                                    colon, 
542
                                    tokenObjectEnd );
543
      }
544
      Value &value = currentValue()[ name ];
545
      nodes_.push( &value );
546
      bool ok = readValue();
547
      nodes_.pop();
548
      if ( !ok ) // error already set
549
         return recoverFromError( tokenObjectEnd );
550
551
      Token comma;
552
      if ( !readToken( comma )
553
            ||  ( comma.type_ != tokenObjectEnd  &&  
554
                  comma.type_ != tokenArraySeparator &&
555
		  comma.type_ != tokenComment ) )
556
      {
557
         return addErrorAndRecover( "Missing ',' or '}' in object declaration", 
558
                                    comma, 
559
                                    tokenObjectEnd );
560
      }
561
      bool finalizeTokenOk = true;
562
      while ( comma.type_ == tokenComment &&
563
              finalizeTokenOk )
564
         finalizeTokenOk = readToken( comma );
565
      if ( comma.type_ == tokenObjectEnd )
566
         return true;
567
   }
568
   return addErrorAndRecover( "Missing '}' or object member name", 
569
                              tokenName, 
570
                              tokenObjectEnd );
571
}
572
573
574
bool 
575
Reader::readArray( Token & )
576
{
577
   currentValue() = Value( arrayValue );
578
   skipSpaces();
579
   if ( *current_ == ']' ) // empty array
580
   {
581
      Token endArray;
582
      readToken( endArray );
583
      return true;
584
   }
585
   int index = 0;
586
   while ( true )
587
   {
588
      Value &value = currentValue()[ index++ ];
589
      nodes_.push( &value );
590
      bool ok = readValue();
591
      nodes_.pop();
592
      if ( !ok ) // error already set
593
         return recoverFromError( tokenArrayEnd );
594
595
      Token token;
596
      // Accept Comment after last item in the array.
597
      ok = readToken( token );
598
      while ( token.type_ == tokenComment  &&  ok )
599
      {
600
         ok = readToken( token );
601
      }
602
      bool badTokenType = ( token.type_ == tokenArraySeparator  &&  
603
                            token.type_ == tokenArrayEnd );
604
      if ( !ok  ||  badTokenType )
605
      {
606
         return addErrorAndRecover( "Missing ',' or ']' in array declaration", 
607
                                    token, 
608
                                    tokenArrayEnd );
609
      }
610
      if ( token.type_ == tokenArrayEnd )
611
         break;
612
   }
613
   return true;
614
}
615
616
617
bool 
618
Reader::decodeNumber( Token &token )
619
{
620
   bool isDouble = false;
621
   for ( Location inspect = token.start_; inspect != token.end_; ++inspect )
622
   {
623
      isDouble = isDouble  
624
                 ||  in( *inspect, '.', 'e', 'E', '+' )  
625
                 ||  ( *inspect == '-'  &&  inspect != token.start_ );
626
   }
627
   if ( isDouble )
628
      return decodeDouble( token );
629
   Location current = token.start_;
630
   bool isNegative = *current == '-';
631
   if ( isNegative )
632
      ++current;
633
   Value::UInt threshold = (isNegative ? Value::UInt(-Value::minInt) 
634
                                       : Value::maxUInt) / 10;
635
   Value::UInt value = 0;
636
   while ( current < token.end_ )
637
   {
638
      Char c = *current++;
639
      if ( c < '0'  ||  c > '9' )
640
         return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
641
      if ( value >= threshold )
642
         return decodeDouble( token );
643
      value = value * 10 + Value::UInt(c - '0');
644
   }
645
   if ( isNegative )
646
      currentValue() = -Value::Int( value );
647
   else if ( value <= Value::UInt(Value::maxInt) )
648
      currentValue() = Value::Int( value );
649
   else
650
      currentValue() = value;
651
   return true;
652
}
653
654
655
bool 
656
Reader::decodeDouble( Token &token )
657
{
658
   double value = 0;
659
   const int bufferSize = 32;
660
   int count;
661
   int length = int(token.end_ - token.start_);
662
   if ( length <= bufferSize )
663
   {
664
      Char buffer[bufferSize];
665
      memcpy( buffer, token.start_, length );
666
      buffer[length] = 0;
667
      count = sscanf( buffer, "%lf", &value );
668
   }
669
   else
670
   {
671
      std::string buffer( token.start_, token.end_ );
672
      count = sscanf( buffer.c_str(), "%lf", &value );
673
   }
674
675
   if ( count != 1 )
676
      return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
677
   currentValue() = value;
678
   return true;
679
}
680
681
682
bool 
683
Reader::decodeString( Token &token )
684
{
685
   std::string decoded;
686
   if ( !decodeString( token, decoded ) )
687
      return false;
688
   currentValue() = decoded;
689
   return true;
690
}
691
692
693
bool 
694
Reader::decodeString( Token &token, std::string &decoded )
695
{
696
   decoded.reserve( token.end_ - token.start_ - 2 );
697
   Location current = token.start_ + 1; // skip '"'
698
   Location end = token.end_ - 1;      // do not include '"'
699
   while ( current != end )
700
   {
701
      Char c = *current++;
702
      if ( c == '"' )
703
         break;
704
      else if ( c == '\\' )
705
      {
706
         if ( current == end )
707
            return addError( "Empty escape sequence in string", token, current );
708
         Char escape = *current++;
709
         switch ( escape )
710
         {
711
         case '"': decoded += '"'; break;
712
         case '/': decoded += '/'; break;
713
         case '\\': decoded += '\\'; break;
714
         case 'b': decoded += '\b'; break;
715
         case 'f': decoded += '\f'; break;
716
         case 'n': decoded += '\n'; break;
717
         case 'r': decoded += '\r'; break;
718
         case 't': decoded += '\t'; break;
719
         case 'u':
720
            {
721
               unsigned int unicode;
722
               if ( !decodeUnicodeCodePoint( token, current, end, unicode ) )
723
                  return false;
724
               decoded += codePointToUTF8(unicode);
725
            }
726
            break;
727
         default:
728
            return addError( "Bad escape sequence in string", token, current );
729
         }
730
      }
731
      else
732
      {
733
         decoded += c;
734
      }
735
   }
736
   return true;
737
}
738
739
bool
740
Reader::decodeUnicodeCodePoint( Token &token, 
741
                                     Location &current, 
742
                                     Location end, 
743
                                     unsigned int &unicode )
744
{
745
746
   if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) )
747
      return false;
748
   if (unicode >= 0xD800 && unicode <= 0xDBFF)
749
   {
750
      // surrogate pairs
751
      if (end - current < 6)
752
         return addError( "additional six characters expected to parse unicode surrogate pair.", token, current );
753
      unsigned int surrogatePair;
754
      if (*(current++) == '\\' && *(current++)== 'u')
755
      {
756
         if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair ))
757
         {
758
            unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
759
         } 
760
         else
761
            return false;
762
      } 
763
      else
764
         return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current );
765
   }
766
   return true;
767
}
768
769
bool 
770
Reader::decodeUnicodeEscapeSequence( Token &token, 
771
                                     Location &current, 
772
                                     Location end, 
773
                                     unsigned int &unicode )
774
{
775
   if ( end - current < 4 )
776
      return addError( "Bad unicode escape sequence in string: four digits expected.", token, current );
777
   unicode = 0;
778
   for ( int index =0; index < 4; ++index )
779
   {
780
      Char c = *current++;
781
      unicode *= 16;
782
      if ( c >= '0'  &&  c <= '9' )
783
         unicode += c - '0';
784
      else if ( c >= 'a'  &&  c <= 'f' )
785
         unicode += c - 'a' + 10;
786
      else if ( c >= 'A'  &&  c <= 'F' )
787
         unicode += c - 'A' + 10;
788
      else
789
         return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current );
790
   }
791
   return true;
792
}
793
794
795
bool 
796
Reader::addError( const std::string &message, 
797
                  Token &token,
798
                  Location extra )
799
{
800
   ErrorInfo info;
801
   info.token_ = token;
802
   info.message_ = message;
803
   info.extra_ = extra;
804
   errors_.push_back( info );
805
   return false;
806
}
807
808
809
bool 
810
Reader::recoverFromError( TokenType skipUntilToken )
811
{
812
   int errorCount = int(errors_.size());
813
   Token skip;
814
   while ( true )
815
   {
816
      if ( !readToken(skip) )
817
         errors_.resize( errorCount ); // discard errors caused by recovery
818
      if ( skip.type_ == skipUntilToken  ||  skip.type_ == tokenEndOfStream )
819
         break;
820
   }
821
   errors_.resize( errorCount );
822
   return false;
823
}
824
825
826
bool 
827
Reader::addErrorAndRecover( const std::string &message, 
828
                            Token &token,
829
                            TokenType skipUntilToken )
830
{
831
   addError( message, token );
832
   return recoverFromError( skipUntilToken );
833
}
834
835
836
Value &
837
Reader::currentValue()
838
{
839
   return *(nodes_.top());
840
}
841
842
843
Reader::Char 
844
Reader::getNextChar()
845
{
846
   if ( current_ == end_ )
847
      return 0;
848
   return *current_++;
849
}
850
851
852
void 
853
Reader::getLocationLineAndColumn( Location location,
854
                                  int &line,
855
                                  int &column ) const
856
{
857
   Location current = begin_;
858
   Location lastLineStart = current;
859
   line = 0;
860
   while ( current < location  &&  current != end_ )
861
   {
862
      Char c = *current++;
863
      if ( c == '\r' )
864
      {
865
         if ( *current == '\n' )
866
            ++current;
867
         lastLineStart = current;
868
         ++line;
869
      }
870
      else if ( c == '\n' )
871
      {
872
         lastLineStart = current;
873
         ++line;
874
      }
875
   }
876
   // column & line start at 1
877
   column = int(location - lastLineStart) + 1;
878
   ++line;
879
}
880
881
882
std::string
883
Reader::getLocationLineAndColumn( Location location ) const
884
{
885
   int line, column;
886
   getLocationLineAndColumn( location, line, column );
887
   char buffer[18+16+16+1];
888
   sprintf( buffer, "Line %d, Column %d", line, column );
889
   return buffer;
890
}
891
892
893
std::string 
894
Reader::getFormatedErrorMessages() const
895
{
896
   std::string formattedMessage;
897
   for ( Errors::const_iterator itError = errors_.begin();
898
         itError != errors_.end();
899
         ++itError )
900
   {
901
      const ErrorInfo &error = *itError;
902
      formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n";
903
      formattedMessage += "  " + error.message_ + "\n";
904
      if ( error.extra_ )
905
         formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n";
906
   }
907
   return formattedMessage;
908
}
909
910
911
std::istream& operator>>( std::istream &sin, Value &root )
912
{
913
    Json::Reader reader;
914
    bool ok = reader.parse(sin, root, true);
915
    //JSON_ASSERT( ok );
916
    if (!ok) throw std::runtime_error(reader.getFormatedErrorMessages());
917
    return sin;
918
}
919
920
921
} // namespace Json