Coverage Report - org.apache.johnzon.core.JsonStreamParserImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
JsonStreamParserImpl
92 %
301/324
88 %
326/367
8,368
JsonStreamParserImpl$StructureElement
100 %
4/4
N/A
8,368
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements. See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership. The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License. You may obtain a copy of the License at
 9  
  *
 10  
  * http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied. See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.johnzon.core;
 20  
 
 21  
 import java.io.IOException;
 22  
 import java.io.InputStream;
 23  
 import java.io.InputStreamReader;
 24  
 import java.io.Reader;
 25  
 import java.math.BigDecimal;
 26  
 import java.nio.charset.Charset;
 27  
 import java.util.NoSuchElementException;
 28  
 
 29  
 import javax.json.JsonException;
 30  
 import javax.json.stream.JsonLocation;
 31  
 import javax.json.stream.JsonParser;
 32  
 import javax.json.stream.JsonParsingException;
 33  
 
 34  
 //This class represents either the Json tokenizer and the Json parser.
 35  
 public class JsonStreamParserImpl implements JsonChars, JsonParser{
 36  
 
 37  
     //the main buffer where the stream will be buffered
 38  
     private final char[] buffer;
 39  
 
 40  
     //current parser position within the buffer
 41  
     //Initial MIN_VALUE will trigger buffer refill, normally bufferPos is >= -1
 42  
     //-1 would cause a re-read of the first character in the buffer (which is at zero index)
 43  460
     private int bufferPos = Integer.MIN_VALUE;
 44  
 
 45  
     //available character in the buffer. It might be <= "buffer.length".
 46  
     private int availableCharsInBuffer;
 47  
 
 48  
     //start and end position of values in the buffer
 49  
     //may cross boundaries, then value is in fallBackCopyBuffer
 50  460
     private int startOfValueInBuffer = -1;
 51  460
     private int endOfValueInBuffer = -1;
 52  
 
 53  
     private final Reader in;
 54  
 
 55  
     //do we read from a character stream or a byte stream
 56  
     //not used at the moment but maybe relevant in future to calculate the JsonLocation offset
 57  
     @SuppressWarnings("unused")
 58  
     private final boolean readBytes;
 59  
     private final BufferStrategy.BufferProvider<char[]> bufferProvider;
 60  
     private final BufferStrategy.BufferProvider<char[]> valueProvider;
 61  
 
 62  
     //max length for strings and numbers (max count of characters)
 63  
     private final int maxValueLength;
 64  
 
 65  
     //we use a byte here, because comparing bytes
 66  
     //is more efficient than comparing enums
 67  
     //Additionally we handle internally two more event: COMMA_EVENT and KEY_SEPARATOR_EVENT
 68  
     private byte previousEvent;
 69  
 
 70  
     //this buffer is used to store current String or Number value in case that
 71  
     //within the value a buffer boundary is crossed or the string contains escaped characters
 72  
     private final char[] fallBackCopyBuffer;
 73  
     private int fallBackCopyBufferLength;
 74  
 
 75  
     // location (line, column, offset)
 76  
     // We try to calculate this efficiently so we do not just increment the values per char read
 77  
     // Instead we calculate the column and offset relative to the pastBufferReadCount and/or lastLineBreakPosition.
 78  460
     private long currentLine = 1;
 79  
     private long lastLineBreakPosition;
 80  
     private long pastBufferReadCount;
 81  
 
 82  
     //cache (if current value is a number) integral state and the number itself if its only one digit    
 83  460
     private boolean isCurrentNumberIntegral = true;
 84  460
     private int currentIntegralNumber = Integer.MIN_VALUE; //for number from 0 - 9
 85  
 
 86  
     //maybe we want also cache BigDecimals
 87  
     //private BigDecimal currentBigDecimalNumber = null;
 88  
 
 89  
     //We need a stack if we want detect bad formatted Json do determine if we are within an array or not
 90  
     //example
 91  
     //     Streamparser sees: ],1  <-- we look from here
 92  
     //the 1 is only allowed if we are within an array
 93  
     //This can only be determined by build up a stack which tracks the trail of Json objects and arrays
 94  
     //This stack here is only needed for validating the above mentioned case, if we want to be lenient we can skip suing the stack.
 95  
     //Stack can cause out of memory issues when the nesting depth of a Json stream is too deep.
 96  460
     private StructureElement currentStructureElement = null;
 97  
 
 98  
     //minimal stack implementation
 99  
     private static final class StructureElement {
 100  
         final StructureElement previous;
 101  
         final boolean isArray;
 102  
 
 103  
         StructureElement(final StructureElement previous, final boolean isArray) {
 104  7158
             super();
 105  7158
             this.previous = previous;
 106  7158
             this.isArray = isArray;
 107  7158
         }
 108  
     }
 109  
 
 110  
     //detect charset according to RFC 4627
 111  
     public JsonStreamParserImpl(final InputStream inputStream, final int maxStringLength,
 112  
             final BufferStrategy.BufferProvider<char[]> bufferProvider, final BufferStrategy.BufferProvider<char[]> valueBuffer) {
 113  
 
 114  279
         this(inputStream, null, null, maxStringLength, bufferProvider, valueBuffer);
 115  276
     }
 116  
 
 117  
     //use charset provided
 118  
     public JsonStreamParserImpl(final InputStream inputStream, final Charset encoding, final int maxStringLength,
 119  
             final BufferStrategy.BufferProvider<char[]> bufferProvider, final BufferStrategy.BufferProvider<char[]> valueBuffer) {
 120  
 
 121  55
         this(inputStream, null, encoding, maxStringLength, bufferProvider, valueBuffer);
 122  55
     }
 123  
 
 124  
     public JsonStreamParserImpl(final Reader reader, final int maxStringLength, final BufferStrategy.BufferProvider<char[]> bufferProvider,
 125  
             final BufferStrategy.BufferProvider<char[]> valueBuffer) {
 126  
 
 127  126
         this(null, reader, null, maxStringLength, bufferProvider, valueBuffer);
 128  126
     }
 129  
 
 130  
     private JsonStreamParserImpl(final InputStream inputStream, final Reader reader, final Charset encoding, final int maxStringLength,
 131  460
             final BufferStrategy.BufferProvider<char[]> bufferProvider, final BufferStrategy.BufferProvider<char[]> valueBuffer) {
 132  
 
 133  460
         this.maxValueLength = maxStringLength <= 0 ? 8192 : maxStringLength;
 134  460
         this.fallBackCopyBuffer = valueBuffer.newBuffer();
 135  460
         this.buffer = bufferProvider.newBuffer();
 136  460
         this.bufferProvider = bufferProvider;
 137  460
         this.valueProvider = valueBuffer;
 138  
 
 139  460
         if (fallBackCopyBuffer.length < maxStringLength) {
 140  0
             throw cust("Size of value buffer cannot be smaller than maximum string length");
 141  
         }
 142  
 
 143  460
         if (reader != null) {
 144  126
             this.in = reader;
 145  126
             readBytes = false;
 146  334
         } else if (encoding == null) {
 147  279
             this.in = new RFC4627AwareInputStreamReader(inputStream);
 148  276
             readBytes = true;
 149  
 
 150  
         } else {
 151  55
             this.in = new InputStreamReader(inputStream, encoding.newDecoder());
 152  55
             readBytes = true;
 153  
         }
 154  
 
 155  457
     }
 156  
 
 157  
     //append a single char to the value buffer
 158  
     private void appendToCopyBuffer(final char c) {
 159  23690
         fallBackCopyBuffer[fallBackCopyBufferLength++] = c;
 160  23690
     }
 161  
 
 162  
     //copy content between "start" and "end" from buffer to value buffer 
 163  
     private void copyCurrentValue() {
 164  
 
 165  10991
         if ((endOfValueInBuffer - startOfValueInBuffer) > 0) {
 166  
 
 167  10931
             if ((endOfValueInBuffer - startOfValueInBuffer) > maxValueLength) {
 168  1
                 throw tmc();
 169  
             }
 170  
 
 171  10930
             System.arraycopy(buffer, startOfValueInBuffer, fallBackCopyBuffer, fallBackCopyBufferLength,
 172  
                     (endOfValueInBuffer - startOfValueInBuffer));
 173  10930
             fallBackCopyBufferLength += (endOfValueInBuffer - startOfValueInBuffer);
 174  
 
 175  
         }
 176  
 
 177  10990
         startOfValueInBuffer = endOfValueInBuffer = -1;
 178  10990
     }
 179  
 
 180  
     @Override
 181  
     public final boolean hasNext() {
 182  
 
 183  324681
         if (currentStructureElement != null || (previousEvent != END_ARRAY && previousEvent != END_OBJECT) || previousEvent == 0) {
 184  324060
             return true;
 185  
         }
 186  
 
 187  
         //detect garbage at the end of the file after last object or array is closed
 188  621
         if (bufferPos < availableCharsInBuffer - 2) {
 189  
 
 190  5
             final char c = readNextNonWhitespaceChar(readNextChar());
 191  
 
 192  5
             if (c == EOF) {
 193  1
                 return false;
 194  
             }
 195  
 
 196  4
             if (bufferPos < availableCharsInBuffer) {
 197  4
                 throw uexc("EOF expected");
 198  
             }
 199  
 
 200  
         }
 201  
 
 202  616
         return false;
 203  
 
 204  
     }
 205  
 
 206  
     private static boolean isAsciiDigit(final char value) {
 207  201281
         return value <= NINE && value >= ZERO;
 208  
     }
 209  
 
 210  
     //check if value is a valid hex digit and return the numeric value
 211  
     private int parseHexDigit(final char value) {
 212  
 
 213  38437
         if (isAsciiDigit(value)) {
 214  26315
             return value - 48;
 215  12122
         } else if (value <= 'f' && value >= 'a') {
 216  12120
             return (value) - 87;
 217  2
         } else if ((value <= 'F' && value >= 'A')) {
 218  0
             return (value) - 55;
 219  
         } else {
 220  2
             throw uexc("Invalid hex character");
 221  
         }
 222  
     }
 223  
 
 224  
     private JsonLocation createLocation() {
 225  
 
 226  
         //we start with column = 1, so column is always >= 1
 227  
         //APi is not clear in this, but starting column with 1 is convenient
 228  1009
         long column = 1;
 229  1009
         long charOffset = 0;
 230  
 
 231  1009
         if (bufferPos >= -1) {
 232  
 
 233  907
             charOffset = pastBufferReadCount + bufferPos + 1;
 234  907
             column = lastLineBreakPosition == 0 ? charOffset + 1 : charOffset - lastLineBreakPosition;
 235  
         }
 236  
 
 237  
         //For now its unclear how to calculate offset for (byte) inputsream.
 238  
         //API says count bytes but thats dependent on encoding and not efficient
 239  
         //skip this for now, count always bytes and defer this until the JSR TCK arrives.
 240  
 
 241  1009
         return new JsonLocationImpl(currentLine, column, charOffset);
 242  
     }
 243  
 
 244  
     //read the next char from the stream and set/increment the bufferPos
 245  
     //will also refill buffer if necessary
 246  
     //if we are currently processing a value (string or number) and buffer 
 247  
     //refill is necessary copy the already read value part into the value buffer
 248  
     protected final char readNextChar() {
 249  
 
 250  2014684
         if ((availableCharsInBuffer - bufferPos) <= 1) {
 251  
             //fillbuffer
 252  
 
 253  
             //copy content from old buffer to valuebuffer
 254  
             //correct start end mark
 255  1187
             if (startOfValueInBuffer > -1 && endOfValueInBuffer == -1) {
 256  268
                 endOfValueInBuffer = availableCharsInBuffer;
 257  268
                 copyCurrentValue();
 258  
 
 259  267
                 startOfValueInBuffer = 0;
 260  
             }
 261  
 
 262  1186
             if (bufferPos >= -1) {
 263  729
                 pastBufferReadCount += availableCharsInBuffer;
 264  
             }
 265  
 
 266  
             try {
 267  1186
                 availableCharsInBuffer = in.read(buffer, 0, buffer.length);
 268  1186
                 if (availableCharsInBuffer <= 0) {
 269  5
                     return EOF;
 270  
                 }
 271  
 
 272  0
             } catch (final IOException e) {
 273  0
                 close();
 274  0
                 throw uexio(e);
 275  1181
             }
 276  
 
 277  1181
             bufferPos = 0;
 278  
             //end fillbuffer
 279  
         } else {
 280  
 
 281  
             //since JOHNZON-18 not longer necessary
 282  
             //prevent "bufferoverflow
 283  
             //if(bufferPos + 1 >= availableCharsInBuffer) {
 284  
             //    return EOF;
 285  
             //}
 286  
 
 287  2013497
             bufferPos++;
 288  
         }
 289  
 
 290  2014678
         return buffer[bufferPos];
 291  
     }
 292  
 
 293  
     //skip whitespaces
 294  
     //tracks location informations (line, column)
 295  
     //returns the first non whitespace character
 296  
     protected final char readNextNonWhitespaceChar(char c) {
 297  
 
 298  211842
         int dosCount = 0;
 299  
 
 300  682981
         while (c == SPACE || c == TAB || c == CR || c == EOL) {
 301  
 
 302  471141
             if (c == EOL) {
 303  71147
                 currentLine++;
 304  71147
                 lastLineBreakPosition = pastBufferReadCount + bufferPos;
 305  
             }
 306  
 
 307  
             //prevent DOS (denial of service) attack
 308  471141
             if (dosCount >= maxValueLength) {
 309  2
                 throw tmc();
 310  
             }
 311  471139
             dosCount++;
 312  
 
 313  
             //read next character
 314  471139
             c = readNextChar();
 315  
 
 316  
         }
 317  
 
 318  211840
         return c;
 319  
     }
 320  
 
 321  
     @Override
 322  
     public final Event next() {
 323  
         //main entry, make decision how to handle the current character in the stream
 324  
 
 325  211742
         if (!hasNext()) {
 326  1
             throw new NoSuchElementException();
 327  
         }
 328  
 
 329  211741
         if (previousEvent != 0 && currentStructureElement == null) {
 330  4
             throw uexc("Unexpected end of structure");
 331  
         }
 332  
 
 333  211737
         final char c = readNextNonWhitespaceChar(readNextChar());
 334  
 
 335  211735
         if (c == COMMA_CHAR) {
 336  
 
 337  
             //last event must one of the following-> " ] } LITERAL
 338  50121
             if (previousEvent == START_ARRAY || previousEvent == START_OBJECT || previousEvent == COMMA_EVENT || previousEvent == KEY_NAME) {
 339  8
                 throw uexc("Expected \" ] } LITERAL");
 340  
             }
 341  
 
 342  50113
             previousEvent = COMMA_EVENT;
 343  50113
             return next();
 344  
 
 345  
         }
 346  
 
 347  161614
         if (c == KEY_SEPARATOR) {
 348  
 
 349  48411
             if (previousEvent != KEY_NAME) {
 350  5
                 throw uexc("A : can only follow a key name");
 351  
             }
 352  
 
 353  48406
             previousEvent = KEY_SEPARATOR_EVENT;
 354  48406
             return next();
 355  
 
 356  
         }
 357  
 
 358  113203
         if (!isCurrentNumberIntegral) {
 359  7392
             isCurrentNumberIntegral = true;
 360  
         }
 361  
         //        if (currentBigDecimalNumber != null) {
 362  
         //            currentBigDecimalNumber = null;
 363  
         //        }
 364  113203
         if (currentIntegralNumber != Integer.MIN_VALUE) {
 365  4970
             currentIntegralNumber = Integer.MIN_VALUE;
 366  
         }
 367  
 
 368  113203
         if (fallBackCopyBufferLength != 0) {
 369  10382
             fallBackCopyBufferLength = 0;
 370  
         }
 371  
 
 372  113203
         startOfValueInBuffer = endOfValueInBuffer = -1;
 373  
 
 374  113203
         switch (c) {
 375  
 
 376  
             case START_OBJECT_CHAR:
 377  
 
 378  4863
                 return handleStartObject();
 379  
 
 380  
             case END_OBJECT_CHAR:
 381  
 
 382  4801
                 return handleEndObject();
 383  
 
 384  
             case START_ARRAY_CHAR:
 385  
 
 386  2296
                 return handleStartArray();
 387  
 
 388  
             case END_ARRAY_CHAR:
 389  
 
 390  2276
                 return handleEndArray();
 391  
 
 392  
             case QUOTE_CHAR:
 393  
 
 394  71471
                 return handleQuote();
 395  
 
 396  
             case '0':
 397  
             case '1':
 398  
             case '2':
 399  
             case '3':
 400  
             case '4':
 401  
             case '5':
 402  
             case '6':
 403  
             case '7':
 404  
             case '8':
 405  
             case '9':
 406  
             case MINUS:
 407  
             case FALSE_F: // false
 408  
             case TRUE_T: // true
 409  
             case NULL_N: // null
 410  
 
 411  27469
                 return handleLiteral();
 412  
 
 413  
             default:
 414  
 
 415  27
                 return defaultHandling(c);
 416  
         }
 417  
     }
 418  
 
 419  
     protected Event defaultHandling(char c) {
 420  20
         if (c == EOF) {
 421  6
             throw uexc("End of file hit too early");
 422  
         }
 423  14
         throw uexc("Expected structural character or digit or 't' or 'n' or 'f' or '-'");
 424  
     }
 425  
 
 426  
     private Event handleStartObject() {
 427  
 
 428  
         //last event must one of the following-> : , [
 429  4863
         if (previousEvent != 0 && previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_ARRAY && previousEvent != COMMA_EVENT) {
 430  1
             throw uexc("Expected : , [");
 431  
         }
 432  
 
 433  
         //push upon the stack
 434  4862
         if (currentStructureElement == null) {
 435  398
             currentStructureElement = new StructureElement(null, false);
 436  
         } else {
 437  4464
             final StructureElement localStructureElement = new StructureElement(currentStructureElement, false);
 438  4464
             currentStructureElement = localStructureElement;
 439  
         }
 440  
 
 441  4862
         return EVT_MAP[previousEvent = START_OBJECT];
 442  
 
 443  
     }
 444  
 
 445  
     private Event handleEndObject() {
 446  
 
 447  
         //last event must one of the following-> " ] { } LITERAL
 448  4801
         if (previousEvent == START_ARRAY || previousEvent == COMMA_EVENT || previousEvent == KEY_NAME
 449  
                 || previousEvent == KEY_SEPARATOR_EVENT || currentStructureElement == null) {
 450  5
             throw uexc("Expected \" ] { } LITERAL");
 451  
         }
 452  
 
 453  4796
         if (currentStructureElement.isArray) {
 454  5
             throw uexc("Expected : ]");
 455  
         }
 456  
 
 457  
         //pop from stack
 458  4791
         currentStructureElement = currentStructureElement.previous;
 459  
 
 460  4791
         return EVT_MAP[previousEvent = END_OBJECT];
 461  
     }
 462  
 
 463  
     private Event handleStartArray() {
 464  
 
 465  
         //last event must one of the following-> : , [
 466  2296
         if (previousEvent != 0 && previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_ARRAY && previousEvent != COMMA_EVENT) {
 467  0
             throw uexc("Expected : , [");
 468  
         }
 469  
 
 470  
         //push upon the stack
 471  2296
         if (currentStructureElement == null) {
 472  42
             currentStructureElement = new StructureElement(null, true);
 473  
         } else {
 474  2254
             final StructureElement localStructureElement = new StructureElement(currentStructureElement, true);
 475  2254
             currentStructureElement = localStructureElement;
 476  
         }
 477  
 
 478  2296
         return EVT_MAP[previousEvent = START_ARRAY];
 479  
     }
 480  
 
 481  
     private Event handleEndArray() {
 482  
 
 483  
         //last event must one of the following-> [ ] } " LITERAL
 484  2276
         if (previousEvent == START_OBJECT || previousEvent == COMMA_EVENT || previousEvent == KEY_SEPARATOR_EVENT
 485  
                 || currentStructureElement == null) {
 486  3
             throw uexc("Expected [ ] } \" LITERAL");
 487  
         }
 488  
 
 489  2273
         if (!currentStructureElement.isArray) {
 490  2
             throw uexc("Expected : }");
 491  
         }
 492  
 
 493  
         //pop from stack
 494  2271
         currentStructureElement = currentStructureElement.previous;
 495  
 
 496  2271
         return EVT_MAP[previousEvent = END_ARRAY];
 497  
     }
 498  
 
 499  
     //read a string, gets called recursively
 500  
     //Handles escape/d characters
 501  
     //if string contains escape chars and/or cross buffer boundary then copy in the value buffer
 502  
     //if not then denote string start and end in startOfValueInBuffer and endOfValueInBuffer and read directly from buffer
 503  
     private void readString() {
 504  
 
 505  
         do {
 506  102231
             char n = readNextChar();
 507  
             //when first called n its first char after the starting quote
 508  
             //after that its the next character after the while loop below
 509  
 
 510  102231
             if (n == QUOTE_CHAR) {
 511  6737
                 endOfValueInBuffer = startOfValueInBuffer = bufferPos; //->"" case
 512  6737
                 return;
 513  95494
             } else if (n == EOL) {
 514  0
                 throw uexc("Unexpected linebreak");
 515  
 
 516  95494
             } else if (/* n >= '\u0000' && */ n <= '\u001F') {
 517  3
                 throw uexc("Unescaped control character");
 518  
 
 519  95491
             } else if (n == ESCAPE_CHAR) {
 520  
 
 521  23693
                 n = readNextChar();
 522  
 
 523  
                 //  \ u XXXX -> unicode char
 524  23693
                 if (n == 'u') {
 525  9610
                     n = parseUnicodeHexChars();
 526  9608
                     appendToCopyBuffer(n);
 527  
 
 528  
                     // \\ -> \
 529  14083
                 } else if (n == ESCAPE_CHAR) {
 530  3197
                     appendToCopyBuffer(n);
 531  
 
 532  
                     //another escape chars, for example \t
 533  
                 } else {
 534  10886
                     appendToCopyBuffer(Strings.asEscapedChar(n));
 535  
 
 536  
                 }
 537  
 
 538  
             } else {
 539  
 
 540  71798
                 startOfValueInBuffer = bufferPos;
 541  71798
                 endOfValueInBuffer = -1;
 542  
 
 543  965523
                 while ((n = readNextChar()) > '\u001F' && n != ESCAPE_CHAR && n != EOL && n != QUOTE_CHAR) {
 544  
                     //read fast
 545  
                 }
 546  
 
 547  71797
                 endOfValueInBuffer = bufferPos;
 548  
 
 549  71797
                 if (n == QUOTE_CHAR) {
 550  
 
 551  64723
                     if (fallBackCopyBufferLength > 0) {
 552  3621
                         copyCurrentValue();
 553  
                     } else {
 554  61102
                         if ((endOfValueInBuffer - startOfValueInBuffer) > maxValueLength) {
 555  1
                             throw tmc();
 556  
                         }
 557  
 
 558  
                     }
 559  
 
 560  64722
                     return;
 561  7074
                 } else if (n == EOL) {
 562  2
                     throw uexc("Unexpected linebreak");
 563  
 
 564  7072
                 } else if (n >= '\u0000' && n <= '\u001F') {
 565  0
                     throw uexc("Unescaped control character");
 566  
                 }
 567  
 
 568  7072
                 copyCurrentValue();
 569  
 
 570  
                 //current n is one of < '\u001F' -OR- ESCAPE_CHAR -OR- EOL -OR- QUOTE
 571  
 
 572  7072
                 bufferPos--; //unread one char
 573  
 
 574  
             }
 575  30762
         }  while (true);
 576  
 
 577  
         // before this do while(true) it was:
 578  
         //
 579  
         //recurse until string is terminated by a non escaped quote
 580  
         //readString();
 581  
         //
 582  
         //
 583  
         // but recursive = can't read big strings
 584  
 
 585  
     }
 586  
 
 587  
     //maybe we want to check invalid utf encoding
 588  
     //not clear yet if the InputStreamReader is doing that
 589  
 
 590  
     /*
 591  
     private char checkSurrogates(char n, char highSurrogate) {
 592  
         //check for invalid surrogates
 593  
         //high followed by low       
 594  
         if (Character.isHighSurrogate(n)) {
 595  
 
 596  
             if (highSurrogate != 0) {
 597  
                 throw uexc("Unexpected high surrogate");
 598  
             }
 599  
             return n;
 600  
         } else if (Character.isLowSurrogate(n)) {
 601  
 
 602  
             if (highSurrogate == 0) {
 603  
                 throw uexc("Unexpected low surrogate");
 604  
             } else if (!Character.isSurrogatePair(highSurrogate, n)) {
 605  
                 throw uexc("Invalid surrogate pair");
 606  
             }
 607  
             return 0;
 608  
         } else if (highSurrogate != 0 && !Character.isLowSurrogate(n)) {
 609  
             throw uexc("Expected low surrogate");
 610  
         }
 611  
         
 612  
         return highSurrogate;
 613  
     }*/
 614  
 
 615  
     //read the next four chars, check them and treat them as an single unicode char
 616  
     private char parseUnicodeHexChars() {
 617  
         // \u08Ac etc       
 618  9610
         return (char) (((parseHexDigit(readNextChar())) * 4096) + ((parseHexDigit(readNextChar())) * 256)
 619  
                 + ((parseHexDigit(readNextChar())) * 16) + ((parseHexDigit(readNextChar()))));
 620  
 
 621  
     }
 622  
 
 623  
     private Event handleQuote() {
 624  
 
 625  
         //always the beginning quote of a key or value  
 626  
 
 627  
         //last event must one of the following-> : { [ ,
 628  71471
         if (previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_OBJECT && previousEvent != START_ARRAY
 629  
                 && previousEvent != COMMA_EVENT) {
 630  2
             throw uexc("Expected : { [ ,");
 631  
         }
 632  
         //starting quote already consumed
 633  71469
         readString();
 634  
         //end quote already consumed
 635  
 
 636  
         //make the decision if its an key or value
 637  71459
         if (previousEvent == KEY_SEPARATOR_EVENT) {
 638  
             //must be value
 639  
 
 640  18749
             if (currentStructureElement != null && currentStructureElement.isArray) {
 641  
                 //not in array, only allowed within array
 642  0
                 throw uexc("Key value pair not allowed in an array");
 643  
             }
 644  
 
 645  18749
             return EVT_MAP[previousEvent = VALUE_STRING];
 646  
 
 647  
         } else { //Event is  START_OBJECT  OR START_ARRAY OR COMMA_EVENT
 648  
             //must be a key if we are in an object, if not its a value 
 649  
 
 650  52710
             if (currentStructureElement != null && currentStructureElement.isArray) {
 651  4295
                 return EVT_MAP[previousEvent = VALUE_STRING];
 652  
             }
 653  
 
 654  48415
             return EVT_MAP[previousEvent = KEY_NAME];
 655  
         }
 656  
 
 657  
     }
 658  
 
 659  
     //read a number
 660  
     //if a number cross buffer boundary then copy in the value buffer
 661  
     //if not then denote string start and end in startOfValueInBuffer and endOfValueInBuffer and read directly from buffer
 662  
     private void readNumber() {
 663  
 
 664  16620
         char c = buffer[bufferPos];
 665  
 
 666  
         //start can change on any read() if we cross buffer boundary
 667  16620
         startOfValueInBuffer = bufferPos;
 668  16620
         endOfValueInBuffer = -1;
 669  
 
 670  16620
         char y = EOF;
 671  
 
 672  
         //sum up the digit values 
 673  16620
         int cumulatedDigitValue = 0;
 674  90117
         while (isAsciiDigit(y = readNextChar())) {
 675  
 
 676  73501
             if (c == ZERO) {
 677  1
                 throw uexc("Leading zeros not allowed");
 678  
             }
 679  
 
 680  73500
             if (c == MINUS && cumulatedDigitValue == 48) {
 681  3
                 throw uexc("Leading zeros after minus not allowed");
 682  
             }
 683  
 
 684  73497
             cumulatedDigitValue += y;
 685  
 
 686  
         }
 687  
 
 688  16616
         if (c == MINUS && cumulatedDigitValue == 0) {
 689  
 
 690  1
             throw uexc("Unexpected premature end of number");
 691  
         }
 692  
 
 693  16615
         if (y == DOT) {
 694  7401
             isCurrentNumberIntegral = false;
 695  7401
             cumulatedDigitValue = 0;
 696  57149
             while (isAsciiDigit(y = readNextChar())) {
 697  49748
                 cumulatedDigitValue++;
 698  
             }
 699  
 
 700  7401
             if (cumulatedDigitValue == 0) {
 701  
 
 702  3
                 throw uexc("Unexpected premature end of number");
 703  
             }
 704  
 
 705  
         }
 706  
 
 707  16612
         if (y == EXP_LOWERCASE || y == EXP_UPPERCASE) {
 708  3141
             isCurrentNumberIntegral = false;
 709  
 
 710  3141
             y = readNextChar(); //+ or - or digit
 711  
 
 712  3141
             if (!isAsciiDigit(y) && y != MINUS && y != PLUS) {
 713  2
                 throw uexc("Expected DIGIT or + or -");
 714  
             }
 715  
 
 716  3139
             if (y == MINUS || y == PLUS) {
 717  3137
                 y = readNextChar();
 718  3137
                 if (!isAsciiDigit(y)) {
 719  1
                     throw uexc("Unexpected premature end of number");
 720  
                 }
 721  
 
 722  
             }
 723  
 
 724  9300
             while (isAsciiDigit(y = readNextChar())) {
 725  
                 //no-op
 726  
             }
 727  
 
 728  
         }
 729  
 
 730  16609
         endOfValueInBuffer = bufferPos;
 731  
 
 732  16609
         if (y == COMMA_CHAR || y == END_ARRAY_CHAR || y == END_OBJECT_CHAR || y == EOL || y == SPACE || y == TAB || y == CR) {
 733  
 
 734  16603
             bufferPos--;//unread one char
 735  
 
 736  
             //['-', DIGIT]
 737  16603
             if (isCurrentNumberIntegral && c == MINUS && cumulatedDigitValue >= 48 && cumulatedDigitValue <= 57) {
 738  
 
 739  27
                 currentIntegralNumber = -(cumulatedDigitValue - 48); //optimize -0 till -9
 740  27
                 return;
 741  
             }
 742  
 
 743  
             //[DIGIT]
 744  16576
             if (isCurrentNumberIntegral && c != MINUS && cumulatedDigitValue == 0) {
 745  
 
 746  4943
                 currentIntegralNumber = (c - 48); //optimize 0 till 9
 747  4943
                 return;
 748  
             }
 749  
 
 750  11633
             if (fallBackCopyBufferLength > 0) {
 751  
 
 752  
                 //we crossed a buffer boundary, use value buffer
 753  30
                 copyCurrentValue();
 754  
 
 755  
             } else {
 756  11603
                 if ((endOfValueInBuffer - startOfValueInBuffer) >= maxValueLength) {
 757  1
                     throw tmc();
 758  
                 }
 759  
             }
 760  
 
 761  11632
             return;
 762  
 
 763  
         }
 764  
 
 765  6
         throw uexc("Unexpected premature end of number");
 766  
 
 767  
     }
 768  
 
 769  
     //handles false, true, null and numbers
 770  
     private Event handleLiteral() {
 771  
 
 772  
         //last event must one of the following-> : , [
 773  27469
         if (previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_ARRAY && previousEvent != COMMA_EVENT) {
 774  8
             throw uexc("Expected : , [");
 775  
         }
 776  
 
 777  27461
         if (previousEvent == COMMA_EVENT && !currentStructureElement.isArray) {
 778  
             //only allowed within array
 779  1
             throw uexc("Not in an array context");
 780  
         }
 781  
 
 782  27460
         char c = buffer[bufferPos];
 783  
 
 784  
         // probe literals
 785  27460
         switch (c) {
 786  
             case TRUE_T:
 787  
 
 788  1323
                 if (readNextChar() != TRUE_R || readNextChar() != TRUE_U || readNextChar() != TRUE_E) {
 789  0
                     throw uexc("Expected LITERAL: true");
 790  
                 }
 791  1323
                 return EVT_MAP[previousEvent = VALUE_TRUE];
 792  
             case FALSE_F:
 793  
 
 794  6368
                 if (readNextChar() != FALSE_A || readNextChar() != FALSE_L || readNextChar() != FALSE_S || readNextChar() != FALSE_E) {
 795  1
                     throw uexc("Expected LITERAL: false");
 796  
                 }
 797  
 
 798  6367
                 return EVT_MAP[previousEvent = VALUE_FALSE];
 799  
 
 800  
             case NULL_N:
 801  
 
 802  3149
                 if (readNextChar() != NULL_U || readNextChar() != NULL_L || readNextChar() != NULL_L) {
 803  2
                     throw uexc("Expected LITERAL: null");
 804  
                 }
 805  3147
                 return EVT_MAP[previousEvent = VALUE_NULL];
 806  
 
 807  
             default:
 808  16620
                 readNumber();
 809  16602
                 return EVT_MAP[previousEvent = VALUE_NUMBER];
 810  
         }
 811  
 
 812  
     }
 813  
 
 814  
     @Override
 815  
     public String getString() {
 816  70373
         if (previousEvent == KEY_NAME || previousEvent == VALUE_STRING || previousEvent == VALUE_NUMBER) {
 817  
 
 818  
             //if there a content in the value buffer read from them, if not use main buffer
 819  70373
             return fallBackCopyBufferLength > 0 ? new String(fallBackCopyBuffer, 0, fallBackCopyBufferLength) : new String(buffer,
 820  
                     startOfValueInBuffer, endOfValueInBuffer - startOfValueInBuffer);
 821  
         } else {
 822  0
             throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getString()");
 823  
         }
 824  
     }
 825  
 
 826  
     @Override
 827  
     public boolean isIntegralNumber() {
 828  
 
 829  16492
         if (previousEvent != VALUE_NUMBER) {
 830  1
             throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support isIntegralNumber()");
 831  
         } else {
 832  16491
             return isCurrentNumberIntegral;
 833  
         }
 834  
     }
 835  
 
 836  
     @Override
 837  
     public int getInt() {
 838  28
         if (previousEvent != VALUE_NUMBER) {
 839  0
             throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getInt()");
 840  28
         } else if (isCurrentNumberIntegral && currentIntegralNumber != Integer.MIN_VALUE) {
 841  20
             return currentIntegralNumber;
 842  8
         } else if (isCurrentNumberIntegral) {
 843  
             //if there a content in the value buffer read from them, if not use main buffer
 844  6
             final Integer retVal = fallBackCopyBufferLength > 0 ? parseIntegerFromChars(fallBackCopyBuffer, 0, fallBackCopyBufferLength)
 845  
                     : parseIntegerFromChars(buffer, startOfValueInBuffer, endOfValueInBuffer);
 846  6
             if (retVal == null) {
 847  0
                 return getBigDecimal().intValue();
 848  
             } else {
 849  6
                 return retVal.intValue();
 850  
             }
 851  
         } else {
 852  2
             return getBigDecimal().intValue();
 853  
         }
 854  
     }
 855  
 
 856  
     @Override
 857  
     public long getLong() {
 858  9196
         if (previousEvent != VALUE_NUMBER) {
 859  0
             throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getLong()");
 860  9196
         } else if (isCurrentNumberIntegral && currentIntegralNumber != Integer.MIN_VALUE) {
 861  4956
             return currentIntegralNumber;
 862  4240
         } else if (isCurrentNumberIntegral) {
 863  
             //if there a content in the value buffer read from them, if not use main buffer
 864  4238
             final Long retVal = fallBackCopyBufferLength > 0 ? parseLongFromChars(fallBackCopyBuffer, 0, fallBackCopyBufferLength)
 865  
                     : parseLongFromChars(buffer, startOfValueInBuffer, endOfValueInBuffer);
 866  4238
             if (retVal == null) {
 867  0
                 return getBigDecimal().longValue();
 868  
             } else {
 869  4238
                 return retVal.longValue();
 870  
             }
 871  
         } else {
 872  2
             return getBigDecimal().longValue();
 873  
         }
 874  
 
 875  
     }
 876  
 
 877  
     @Override
 878  
     public BigDecimal getBigDecimal() {
 879  7309
         if (previousEvent != VALUE_NUMBER) {
 880  0
             throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getBigDecimal()");
 881  
             //        } else if (currentBigDecimalNumber != null) {
 882  
             //            return currentBigDecimalNumber;
 883  7309
         } else if (isCurrentNumberIntegral && currentIntegralNumber != Integer.MIN_VALUE) {
 884  6
             return new BigDecimal(currentIntegralNumber);
 885  7303
         } else if (isCurrentNumberIntegral) {
 886  
             //if there a content in the value buffer read from them, if not use main buffer
 887  8
             final Long retVal = fallBackCopyBufferLength > 0 ? parseLongFromChars(fallBackCopyBuffer, 0, fallBackCopyBufferLength)
 888  
                     : parseLongFromChars(buffer, startOfValueInBuffer, endOfValueInBuffer);
 889  8
             if (retVal == null) {
 890  2
                 return (/*currentBigDecimalNumber = */fallBackCopyBufferLength > 0 ? new BigDecimal(fallBackCopyBuffer, 0,
 891  
                         fallBackCopyBufferLength) : new BigDecimal(buffer, startOfValueInBuffer,
 892  
                         (endOfValueInBuffer - startOfValueInBuffer)));
 893  
             } else {
 894  6
                 return (/*currentBigDecimalNumber = */new BigDecimal(retVal.longValue()));
 895  
             }
 896  
         } else {
 897  
             //if there a content in the value buffer read from them, if not use main buffer
 898  7295
             return (/*currentBigDecimalNumber = */fallBackCopyBufferLength > 0 ? new BigDecimal(fallBackCopyBuffer, 0,
 899  
                     fallBackCopyBufferLength) : new BigDecimal(buffer, startOfValueInBuffer, (endOfValueInBuffer - startOfValueInBuffer)));
 900  
         }
 901  
 
 902  
     }
 903  
 
 904  
     @Override
 905  
     public JsonLocation getLocation() {
 906  909
         return createLocation();
 907  
     }
 908  
 
 909  
     @Override
 910  
     public void close() {
 911  277
         bufferProvider.release(buffer);
 912  277
         valueProvider.release(fallBackCopyBuffer);
 913  
 
 914  
         try {
 915  277
             in.close();
 916  0
         } catch (final IOException e) {
 917  0
             throw new JsonException("Unexpected IO exception " + e.getMessage(), e);
 918  277
         }
 919  277
     }
 920  
 
 921  
     //parse a char[] to long while checking overflow
 922  
     //if overflowed return null
 923  
     //no additional checks since we are sure here that there are no non digits in the array
 924  
     private static Long parseLongFromChars(final char[] chars, final int start, final int end) {
 925  
 
 926  4246
         long retVal = 0;
 927  4246
         final boolean negative = chars[start] == MINUS;
 928  19247
         for (int i = negative ? start + 1 : start; i < end; i++) {
 929  15003
             final long tmp = retVal * 10 + (chars[i] - ZERO);
 930  15003
             if (tmp < retVal) { //check overflow
 931  2
                 return null;
 932  
             } else {
 933  15001
                 retVal = tmp;
 934  
             }
 935  
         }
 936  
 
 937  4244
         return negative ? -retVal : retVal;
 938  
     }
 939  
 
 940  
     //parse a char[] to int while checking overflow
 941  
     //if overflowed return null
 942  
     //no additional checks since we are sure here that there are no non digits in the array
 943  
     private static Integer parseIntegerFromChars(final char[] chars, final int start, final int end) {
 944  
 
 945  6
         int retVal = 0;
 946  6
         final boolean negative = chars[start] == MINUS;
 947  22
         for (int i = negative ? start + 1 : start; i < end; i++) {
 948  16
             final int tmp = retVal * 10 + (chars[i] - ZERO);
 949  16
             if (tmp < retVal) { //check overflow
 950  0
                 return null;
 951  
             } else {
 952  16
                 retVal = tmp;
 953  
             }
 954  
         }
 955  
 
 956  6
         return negative ? -retVal : retVal;
 957  
     }
 958  
 
 959  
     private JsonParsingException uexc(final char c, final String message) {
 960  95
         final JsonLocation location = createLocation();
 961  95
         return new JsonParsingException("Unexpected character '" + c + "' (Codepoint: " + String.valueOf(c).codePointAt(0) + ") on "
 962  
                 + location + ". Reason is [[" + message + "]]", location);
 963  
     }
 964  
 
 965  
     private JsonParsingException uexc(final String message) {
 966  95
         final char c = bufferPos < 0 ? 0 : buffer[bufferPos];
 967  95
         return uexc(c, message);
 968  
     }
 969  
 
 970  
     private JsonParsingException tmc() {
 971  5
         final JsonLocation location = createLocation();
 972  5
         return new JsonParsingException("Too many characters. Maximum string/number length of " + maxValueLength + " exceeded on "
 973  
                 + location, location);
 974  
     }
 975  
 
 976  
     private JsonParsingException uexio(final IOException e) {
 977  0
         final JsonLocation location = createLocation();
 978  0
         return new JsonParsingException("Unexpected IO exception on " + location, e, location);
 979  
     }
 980  
 
 981  
     private JsonParsingException cust(final String message) {
 982  0
         final JsonLocation location = createLocation();
 983  0
         return new JsonParsingException("General exception on " + location + ". Reason is [[" + message + "]]", location);
 984  
     }
 985  
 
 986  
 }