Coverage Report - org.apache.johnzon.core.JsonStreamParserImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
JsonStreamParserImpl
92%
301/325
88%
326/369
8,568
JsonStreamParserImpl$StructureElement
100%
4/4
N/A
8,568
 
 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  459
     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  459
     private int startOfValueInBuffer = -1;
 51  459
     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  459
     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  459
     private boolean isCurrentNumberIntegral = true;
 84  
     private Integer currentIntegralNumber; //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  459
     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  7156
             super();
 105  7156
             this.previous = previous;
 106  7156
             this.isArray = isArray;
 107  7156
         }
 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  278
         this(inputStream, null, null, maxStringLength, bufferProvider, valueBuffer);
 115  275
     }
 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  459
             final BufferStrategy.BufferProvider<char[]> bufferProvider, final BufferStrategy.BufferProvider<char[]> valueBuffer) {
 132  
 
 133  459
         this.maxValueLength = maxStringLength <= 0 ? 8192 : maxStringLength;
 134  459
         this.fallBackCopyBuffer = valueBuffer.newBuffer();
 135  459
         this.buffer = bufferProvider.newBuffer();
 136  459
         this.bufferProvider = bufferProvider;
 137  459
         this.valueProvider = valueBuffer;
 138  
 
 139  459
         if (fallBackCopyBuffer.length < maxStringLength) {
 140  0
             throw cust("Size of value buffer cannot be smaller than maximum string length");
 141  
         }
 142  
 
 143  459
         if (reader != null) {
 144  126
             this.in = reader;
 145  126
             readBytes = false;
 146  333
         } else if (encoding == null) {
 147  278
             this.in = new RFC4627AwareInputStreamReader(inputStream);
 148  275
             readBytes = true;
 149  
 
 150  
         } else {
 151  55
             this.in = new InputStreamReader(inputStream, encoding.newDecoder());
 152  55
             readBytes = true;
 153  
         }
 154  
 
 155  456
     }
 156  
 
 157  
     //append a single char to the value buffer
 158  
     private void appendToCopyBuffer(final char c) {
 159  23690
         fallBackCopyBuffer[fallBackCopyBufferLength] = c;
 160  23690
         fallBackCopyBufferLength++;
 161  23690
     }
 162  
 
 163  
     //copy content between "start" and "end" from buffer to value buffer 
 164  
     private void copyCurrentValue() {
 165  
 
 166  10967
         if ((endOfValueInBuffer - startOfValueInBuffer) > 0) {
 167  
 
 168  10908
             if ((endOfValueInBuffer - startOfValueInBuffer) > maxValueLength) {
 169  1
                 throw tmc();
 170  
             }
 171  
 
 172  10907
             System.arraycopy(buffer, startOfValueInBuffer, fallBackCopyBuffer, fallBackCopyBufferLength,
 173  
                     (endOfValueInBuffer - startOfValueInBuffer));
 174  10907
             fallBackCopyBufferLength += (endOfValueInBuffer - startOfValueInBuffer);
 175  
 
 176  
         }
 177  
 
 178  10966
         startOfValueInBuffer = endOfValueInBuffer = -1;
 179  10966
     }
 180  
 
 181  
     @Override
 182  
     public final boolean hasNext() {
 183  
 
 184  324645
         if (currentStructureElement != null || (previousEvent != END_ARRAY && previousEvent != END_OBJECT) || previousEvent == 0) {
 185  324025
             return true;
 186  
         }
 187  
 
 188  
         //detect garbage at the end of the file after last object or array is closed
 189  620
         if (bufferPos < availableCharsInBuffer - 2) {
 190  
 
 191  5
             final char c = readNextNonWhitespaceChar();
 192  
 
 193  5
             if (c == EOF) {
 194  1
                 return false;
 195  
             }
 196  
 
 197  4
             if (bufferPos < availableCharsInBuffer) {
 198  4
                 throw uexc("EOF expected");
 199  
             }
 200  
 
 201  
         }
 202  
 
 203  615
         return false;
 204  
 
 205  
     }
 206  
 
 207  
     private static boolean isAsciiDigit(final char value) {
 208  201280
         return value <= NINE && value >= ZERO;
 209  
     }
 210  
 
 211  
     //check if value is a valid hex digit and return the numeric value
 212  
     private int parseHexDigit(final char value) {
 213  
 
 214  38437
         if (isAsciiDigit(value)) {
 215  26315
             return value - 48;
 216  12122
         } else if (value <= 'f' && value >= 'a') {
 217  12120
             return (value) - 87;
 218  2
         } else if ((value <= 'F' && value >= 'A')) {
 219  0
             return (value) - 55;
 220  
         } else {
 221  2
             throw uexc("Invalid hex character");
 222  
         }
 223  
     }
 224  
 
 225  
     private JsonLocation createLocation() {
 226  
 
 227  
         //we start with column = 1, so column is always >= 1
 228  
         //APi is not clear in this, but starting column with 1 is convenient
 229  1009
         long column = 1;
 230  1009
         long charOffset = 0;
 231  
 
 232  1009
         if (bufferPos >= -1) {
 233  
 
 234  907
             charOffset = pastBufferReadCount + bufferPos + 1;
 235  907
             column = lastLineBreakPosition == 0 ? charOffset + 1 : charOffset - lastLineBreakPosition;
 236  
         }
 237  
 
 238  
         //For now its unclear how to calculate offset for (byte) inputsream.
 239  
         //API says count bytes but thats dependent on encoding and not efficient
 240  
         //skip this for now, count always bytes and defer this until the JSR TCK arrives.
 241  
 
 242  1009
         return new JsonLocationImpl(currentLine, column, charOffset);
 243  
     }
 244  
 
 245  
     //read the next char from the stream and set/increment the bufferPos
 246  
     //will also refill buffer if necessary
 247  
     //if we are currently processing a value (string or number) and buffer 
 248  
     //refill is necessary copy the already read value part into the value buffer
 249  
     private char readNextChar() {
 250  
 
 251  2014340
         if ((availableCharsInBuffer - bufferPos) <= 1) {
 252  
             //fillbuffer
 253  
 
 254  
             //copy content from old buffer to valuebuffer
 255  
             //correct start end mark
 256  1165
             if (startOfValueInBuffer > -1 && endOfValueInBuffer == -1) {
 257  254
                 endOfValueInBuffer = availableCharsInBuffer;
 258  254
                 copyCurrentValue();
 259  
 
 260  253
                 startOfValueInBuffer = 0;
 261  
             }
 262  
 
 263  1164
             if (bufferPos >= -1) {
 264  708
                 pastBufferReadCount += availableCharsInBuffer;
 265  
             }
 266  
 
 267  
             try {
 268  1164
                 availableCharsInBuffer = in.read(buffer, 0, buffer.length);
 269  1164
                 if (availableCharsInBuffer <= 0) {
 270  5
                     return EOF;
 271  
                 }
 272  
 
 273  0
             } catch (final IOException e) {
 274  0
                 close();
 275  0
                 throw uexio(e);
 276  1159
             }
 277  
 
 278  1159
             bufferPos = 0;
 279  
             //end fillbuffer
 280  
         } else {
 281  
 
 282  
             //prevent "bufferoverflow
 283  2013175
             if(bufferPos + 1 >= availableCharsInBuffer) {
 284  0
                 return EOF;
 285  
             }
 286  
 
 287  2013175
             bufferPos++;
 288  
         }
 289  
 
 290  2014334
         return buffer[bufferPos];
 291  
     }
 292  
 
 293  
     //skip whitespaces
 294  
     //tracks location informations (line, column)
 295  
     //returns the first non whitespace character
 296  
     private char readNextNonWhitespaceChar() {
 297  
 
 298  211718
         int dosCount = 0;
 299  211718
         char c = readNextChar();
 300  
 
 301  682784
         while (c == SPACE || c == TAB || c == CR || c == EOL) {
 302  
 
 303  471068
             if (c == EOL) {
 304  71135
                 currentLine++;
 305  71135
                 lastLineBreakPosition = pastBufferReadCount + bufferPos;
 306  
             }
 307  
 
 308  
             //prevent DOS (denial of service) attack
 309  471068
             if (dosCount >= maxValueLength) {
 310  2
                 throw tmc();
 311  
             }
 312  471066
             dosCount++;
 313  
 
 314  
             //read next character
 315  471066
             c = readNextChar();
 316  
 
 317  
         }
 318  
 
 319  211716
         return c;
 320  
     }
 321  
 
 322  
     @Override
 323  
     public final Event next() {
 324  
         //main entry, make decision how to handle the current character in the stream
 325  
 
 326  211718
         if (!hasNext()) {
 327  1
             throw new NoSuchElementException();
 328  
         }
 329  
 
 330  211717
         if (previousEvent != 0 && currentStructureElement == null) {
 331  4
             throw uexc("Unexpected end of structure");
 332  
         }
 333  
 
 334  211713
         final char c = readNextNonWhitespaceChar();
 335  
 
 336  211711
         if (c == COMMA_CHAR) {
 337  
 
 338  
             //last event must one of the following-> " ] } LITERAL
 339  50119
             if (previousEvent == START_ARRAY || previousEvent == START_OBJECT || previousEvent == COMMA_EVENT || previousEvent == KEY_NAME) {
 340  8
                 throw uexc("Expected \" ] } LITERAL");
 341  
             }
 342  
 
 343  50111
             previousEvent = COMMA_EVENT;
 344  50111
             return next();
 345  
 
 346  
         }
 347  
 
 348  161592
         if (c == KEY_SEPARATOR) {
 349  
 
 350  48407
             if (previousEvent != KEY_NAME) {
 351  5
                 throw uexc("A : can only follow a key name");
 352  
             }
 353  
 
 354  48402
             previousEvent = KEY_SEPARATOR_EVENT;
 355  48402
             return next();
 356  
 
 357  
         }
 358  
 
 359  113185
         if (!isCurrentNumberIntegral) {
 360  7392
             isCurrentNumberIntegral = true;
 361  
         }
 362  
         //        if (currentBigDecimalNumber != null) {
 363  
         //            currentBigDecimalNumber = null;
 364  
         //        }
 365  113185
         if (currentIntegralNumber != null) {
 366  4969
             currentIntegralNumber = null;
 367  
         }
 368  
 
 369  113185
         if (fallBackCopyBufferLength != 0) {
 370  10371
             fallBackCopyBufferLength = 0;
 371  
         }
 372  
 
 373  113185
         startOfValueInBuffer = endOfValueInBuffer = -1;
 374  
 
 375  113185
         switch (c) {
 376  
 
 377  
             case START_OBJECT_CHAR:
 378  
 
 379  4861
                 return handleStartObject();
 380  
 
 381  
             case END_OBJECT_CHAR:
 382  
 
 383  4799
                 return handleEndObject();
 384  
 
 385  
             case START_ARRAY_CHAR:
 386  
 
 387  2296
                 return handleStartArray();
 388  
 
 389  
             case END_ARRAY_CHAR:
 390  
 
 391  2276
                 return handleEndArray();
 392  
 
 393  
             case QUOTE_CHAR:
 394  
 
 395  71465
                 return handleQuote();
 396  
 
 397  
             case '0':
 398  
             case '1':
 399  
             case '2':
 400  
             case '3':
 401  
             case '4':
 402  
             case '5':
 403  
             case '6':
 404  
             case '7':
 405  
             case '8':
 406  
             case '9':
 407  
             case MINUS:
 408  
             case FALSE_F: // false
 409  
             case TRUE_T: // true
 410  
             case NULL_N: // null
 411  
 
 412  27468
                 return handleLiteral();
 413  
             default:
 414  20
                 throw uexc("Expected structural character or digit or 't' or 'n' or 'f' or '-'");
 415  
 
 416  
         }
 417  
 
 418  
     }
 419  
 
 420  
     private Event handleStartObject() {
 421  
 
 422  
         //last event must one of the following-> : , [
 423  4861
         if (previousEvent != 0 && previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_ARRAY && previousEvent != COMMA_EVENT) {
 424  1
             throw uexc("Expected : , [");
 425  
         }
 426  
 
 427  
         //push upon the stack
 428  4860
         if (currentStructureElement == null) {
 429  397
             currentStructureElement = new StructureElement(null, false);
 430  
         } else {
 431  4463
             final StructureElement localStructureElement = new StructureElement(currentStructureElement, false);
 432  4463
             currentStructureElement = localStructureElement;
 433  
         }
 434  
 
 435  4860
         return EVT_MAP[previousEvent = START_OBJECT];
 436  
 
 437  
     }
 438  
 
 439  
     private Event handleEndObject() {
 440  
 
 441  
         //last event must one of the following-> " ] { } LITERAL
 442  4799
         if (previousEvent == START_ARRAY || previousEvent == COMMA_EVENT || previousEvent == KEY_NAME
 443  
                 || previousEvent == KEY_SEPARATOR_EVENT || currentStructureElement == null) {
 444  5
             throw uexc("Expected \" ] { } LITERAL");
 445  
         }
 446  
 
 447  4794
         if (currentStructureElement.isArray) {
 448  5
             throw uexc("Expected : ]");
 449  
         }
 450  
 
 451  
         //pop from stack
 452  4789
         currentStructureElement = currentStructureElement.previous;
 453  
 
 454  4789
         return EVT_MAP[previousEvent = END_OBJECT];
 455  
     }
 456  
 
 457  
     private Event handleStartArray() {
 458  
 
 459  
         //last event must one of the following-> : , [
 460  2296
         if (previousEvent != 0 && previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_ARRAY && previousEvent != COMMA_EVENT) {
 461  0
             throw uexc("Expected : , [");
 462  
         }
 463  
 
 464  
         //push upon the stack
 465  2296
         if (currentStructureElement == null) {
 466  42
             currentStructureElement = new StructureElement(null, true);
 467  
         } else {
 468  2254
             final StructureElement localStructureElement = new StructureElement(currentStructureElement, true);
 469  2254
             currentStructureElement = localStructureElement;
 470  
         }
 471  
 
 472  2296
         return EVT_MAP[previousEvent = START_ARRAY];
 473  
     }
 474  
 
 475  
     private Event handleEndArray() {
 476  
 
 477  
         //last event must one of the following-> [ ] } " LITERAL
 478  2276
         if (previousEvent == START_OBJECT || previousEvent == COMMA_EVENT || previousEvent == KEY_SEPARATOR_EVENT
 479  
                 || currentStructureElement == null) {
 480  3
             throw uexc("Expected [ ] } \" LITERAL");
 481  
         }
 482  
 
 483  2273
         if (!currentStructureElement.isArray) {
 484  2
             throw uexc("Expected : }");
 485  
         }
 486  
 
 487  
         //pop from stack
 488  2271
         currentStructureElement = currentStructureElement.previous;
 489  
 
 490  2271
         return EVT_MAP[previousEvent = END_ARRAY];
 491  
     }
 492  
 
 493  
     //read a string, gets called recursively
 494  
     //Handles escape/d characters
 495  
     //if string contains escape chars and/or cross buffer boundary then copy in the value buffer
 496  
     //if not then denote string start and end in startOfValueInBuffer and endOfValueInBuffer and read directly from buffer
 497  
     private void readString() {
 498  
 
 499  102225
         char n = readNextChar();
 500  
         //when first called n its first char after the starting quote
 501  
         //after that its the next character after the while loop below
 502  
 
 503  102225
         if (n == QUOTE_CHAR) {
 504  6737
             endOfValueInBuffer = startOfValueInBuffer = bufferPos; //->"" case
 505  6737
             return;
 506  95488
         } else if (n == EOL) {
 507  0
             throw uexc("Unexpected linebreak");
 508  
 
 509  95488
         } else if (n >= '\u0000' && n <= '\u001F') {
 510  3
             throw uexc("Unescaped control character");
 511  
 
 512  95485
         } else if (n == ESCAPE_CHAR) {
 513  
 
 514  23693
             n = readNextChar();
 515  
 
 516  
             //  \ u XXXX -> unicode char
 517  23693
             if (n == 'u') {
 518  9610
                 n = parseUnicodeHexChars();
 519  9608
                 appendToCopyBuffer(n);
 520  
 
 521  
                 // \\ -> \
 522  14083
             } else if (n == ESCAPE_CHAR) {
 523  3197
                 appendToCopyBuffer(n);
 524  
 
 525  
                 //another escape chars, for example \t
 526  
             } else {
 527  10886
                 appendToCopyBuffer(Strings.asEscapedChar(n));
 528  
 
 529  
             }
 530  
 
 531  
         } else {
 532  
 
 533  71792
             startOfValueInBuffer = bufferPos;
 534  71792
             endOfValueInBuffer = -1;
 535  
 
 536  965471
             while ((n = readNextChar()) > '\u001F' && n != ESCAPE_CHAR && n != EOL && n != QUOTE_CHAR) {
 537  
                 //read fast
 538  
             }
 539  
 
 540  71791
             endOfValueInBuffer = bufferPos;
 541  
 
 542  71791
             if (n == QUOTE_CHAR) {
 543  
 
 544  64717
                 if (fallBackCopyBufferLength > 0) {
 545  3612
                     copyCurrentValue();
 546  
                 } else {
 547  61105
                     if ((endOfValueInBuffer - startOfValueInBuffer) > maxValueLength) {
 548  1
                         throw tmc();
 549  
                     }
 550  
 
 551  
                 }
 552  
 
 553  64716
                 return;
 554  7074
             } else if (n == EOL) {
 555  2
                 throw uexc("Unexpected linebreak");
 556  
 
 557  7072
             } else if (n >= '\u0000' && n <= '\u001F') {
 558  0
                 throw uexc("Unescaped control character");
 559  
             }
 560  
 
 561  7072
             copyCurrentValue();
 562  
 
 563  
             //current n is one of < '\u001F' -OR- ESCAPE_CHAR -OR- EOL -OR- QUOTE
 564  
 
 565  7072
             bufferPos--; //unread one char
 566  
 
 567  
         }
 568  
 
 569  
         //recurse until string is terminated by a non escaped quote
 570  30762
         readString();
 571  
 
 572  30762
     }
 573  
 
 574  
     //maybe we want to check invalid utf encoding
 575  
     //not clear yet if the InputStreamReader is doing that
 576  
 
 577  
     /*
 578  
     private char checkSurrogates(char n, char highSurrogate) {
 579  
         //check for invalid surrogates
 580  
         //high followed by low       
 581  
         if (Character.isHighSurrogate(n)) {
 582  
 
 583  
             if (highSurrogate != 0) {
 584  
                 throw uexc("Unexpected high surrogate");
 585  
             }
 586  
             return n;
 587  
         } else if (Character.isLowSurrogate(n)) {
 588  
 
 589  
             if (highSurrogate == 0) {
 590  
                 throw uexc("Unexpected low surrogate");
 591  
             } else if (!Character.isSurrogatePair(highSurrogate, n)) {
 592  
                 throw uexc("Invalid surrogate pair");
 593  
             }
 594  
             return 0;
 595  
         } else if (highSurrogate != 0 && !Character.isLowSurrogate(n)) {
 596  
             throw uexc("Expected low surrogate");
 597  
         }
 598  
         
 599  
         return highSurrogate;
 600  
     }*/
 601  
 
 602  
     //read the next four chars, check them and treat them as an single unicode char
 603  
     private char parseUnicodeHexChars() {
 604  
         // \u08Ac etc       
 605  9610
         return (char) (((parseHexDigit(readNextChar())) * 4096) + ((parseHexDigit(readNextChar())) * 256)
 606  
                 + ((parseHexDigit(readNextChar())) * 16) + ((parseHexDigit(readNextChar()))));
 607  
 
 608  
     }
 609  
 
 610  
     private Event handleQuote() {
 611  
 
 612  
         //always the beginning quote of a key or value  
 613  
 
 614  
         //last event must one of the following-> : { [ ,
 615  71465
         if (previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_OBJECT && previousEvent != START_ARRAY
 616  
                 && previousEvent != COMMA_EVENT) {
 617  2
             throw uexc("Expected : { [ ,");
 618  
         }
 619  
         //starting quote already consumed
 620  71463
         readString();
 621  
         //end quote already consumed
 622  
 
 623  
         //make the decision if its an key or value
 624  71453
         if (previousEvent == KEY_SEPARATOR_EVENT) {
 625  
             //must be value
 626  
 
 627  18747
             if (currentStructureElement != null && currentStructureElement.isArray) {
 628  
                 //not in array, only allowed within array
 629  0
                 throw uexc("Key value pair not allowed in an array");
 630  
             }
 631  
 
 632  18747
             return EVT_MAP[previousEvent = VALUE_STRING];
 633  
 
 634  
         } else { //Event is  START_OBJECT  OR START_ARRAY OR COMMA_EVENT
 635  
             //must be a key if we are in an object, if not its a value 
 636  
 
 637  52706
             if (currentStructureElement != null && currentStructureElement.isArray) {
 638  4295
                 return EVT_MAP[previousEvent = VALUE_STRING];
 639  
             }
 640  
 
 641  48411
             return EVT_MAP[previousEvent = KEY_NAME];
 642  
         }
 643  
 
 644  
     }
 645  
 
 646  
     //read a number
 647  
     //if a number cross buffer boundary then copy in the value buffer
 648  
     //if not then denote string start and end in startOfValueInBuffer and endOfValueInBuffer and read directly from buffer
 649  
     private void readNumber() {
 650  
 
 651  16619
         char c = buffer[bufferPos];
 652  
 
 653  
         //start can change on any read() if we cross buffer boundary
 654  16619
         startOfValueInBuffer = bufferPos;
 655  16619
         endOfValueInBuffer = -1;
 656  
 
 657  16619
         char y = EOF;
 658  
 
 659  
         //sum up the digit values 
 660  16619
         int cumulatedDigitValue = 0;
 661  90116
         while (isAsciiDigit(y = readNextChar())) {
 662  
 
 663  73501
             if (c == ZERO) {
 664  1
                 throw uexc("Leading zeros not allowed");
 665  
             }
 666  
 
 667  73500
             if (c == MINUS && cumulatedDigitValue == 48) {
 668  3
                 throw uexc("Leading zeros after minus not allowed");
 669  
             }
 670  
 
 671  73497
             cumulatedDigitValue += y;
 672  
 
 673  
         }
 674  
 
 675  16615
         if (c == MINUS && cumulatedDigitValue == 0) {
 676  
 
 677  1
             throw uexc("Unexpected premature end of number");
 678  
         }
 679  
 
 680  16614
         if (y == DOT) {
 681  7401
             isCurrentNumberIntegral = false;
 682  7401
             cumulatedDigitValue = 0;
 683  57149
             while (isAsciiDigit(y = readNextChar())) {
 684  49748
                 cumulatedDigitValue++;
 685  
             }
 686  
 
 687  7401
             if (cumulatedDigitValue == 0) {
 688  
 
 689  3
                 throw uexc("Unexpected premature end of number");
 690  
             }
 691  
 
 692  
         }
 693  
 
 694  16611
         if (y == EXP_LOWERCASE || y == EXP_UPPERCASE) {
 695  3141
             isCurrentNumberIntegral = false;
 696  
 
 697  3141
             y = readNextChar(); //+ or - or digit
 698  
 
 699  3141
             if (!isAsciiDigit(y) && y != MINUS && y != PLUS) {
 700  2
                 throw uexc("Expected DIGIT or + or -");
 701  
             }
 702  
 
 703  3139
             if (y == MINUS || y == PLUS) {
 704  3137
                 y = readNextChar();
 705  3137
                 if (!isAsciiDigit(y)) {
 706  1
                     throw uexc("Unexpected premature end of number");
 707  
                 }
 708  
 
 709  
             }
 710  
 
 711  9300
             while (isAsciiDigit(y = readNextChar())) {
 712  
                 //no-op
 713  
             }
 714  
 
 715  
         }
 716  
 
 717  16608
         endOfValueInBuffer = bufferPos;
 718  
 
 719  16608
         if (y == COMMA_CHAR || y == END_ARRAY_CHAR || y == END_OBJECT_CHAR || y == EOL || y == SPACE || y == TAB || y == CR) {
 720  
 
 721  16602
             bufferPos--;//unread one char
 722  
 
 723  
             //['-', DIGIT]
 724  16602
             if (isCurrentNumberIntegral && c == MINUS && cumulatedDigitValue >= 48 && cumulatedDigitValue <= 57) {
 725  
 
 726  27
                 currentIntegralNumber = -(cumulatedDigitValue - 48); //optimize -0 till -9
 727  27
                 return;
 728  
             }
 729  
 
 730  
             //[DIGIT]
 731  16575
             if (isCurrentNumberIntegral && c != MINUS && cumulatedDigitValue == 0) {
 732  
 
 733  4942
                 currentIntegralNumber = (c - 48); //optimize 0 till 9
 734  4942
                 return;
 735  
             }
 736  
 
 737  11633
             if (fallBackCopyBufferLength > 0) {
 738  
 
 739  
                 //we crossed a buffer boundary, use value buffer
 740  29
                 copyCurrentValue();
 741  
 
 742  
             } else {
 743  11604
                 if ((endOfValueInBuffer - startOfValueInBuffer) >= maxValueLength) {
 744  1
                     throw tmc();
 745  
                 }
 746  
             }
 747  
 
 748  11632
             return;
 749  
 
 750  
         }
 751  
 
 752  6
         throw uexc("Unexpected premature end of number");
 753  
 
 754  
     }
 755  
 
 756  
     //handles false, true, null and numbers
 757  
     private Event handleLiteral() {
 758  
 
 759  
         //last event must one of the following-> : , [
 760  27468
         if (previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_ARRAY && previousEvent != COMMA_EVENT) {
 761  8
             throw uexc("Expected : , [");
 762  
         }
 763  
 
 764  27460
         if (previousEvent == COMMA_EVENT && !currentStructureElement.isArray) {
 765  
             //only allowed within array
 766  1
             throw uexc("Not in an array context");
 767  
         }
 768  
 
 769  27459
         char c = buffer[bufferPos];
 770  
 
 771  
         // probe literals
 772  27459
         switch (c) {
 773  
             case TRUE_T:
 774  
 
 775  1323
                 if (readNextChar() != TRUE_R || readNextChar() != TRUE_U || readNextChar() != TRUE_E) {
 776  0
                     throw uexc("Expected LITERAL: true");
 777  
                 }
 778  1323
                 return EVT_MAP[previousEvent = VALUE_TRUE];
 779  
             case FALSE_F:
 780  
 
 781  6368
                 if (readNextChar() != FALSE_A || readNextChar() != FALSE_L || readNextChar() != FALSE_S || readNextChar() != FALSE_E) {
 782  1
                     throw uexc("Expected LITERAL: false");
 783  
                 }
 784  
 
 785  6367
                 return EVT_MAP[previousEvent = VALUE_FALSE];
 786  
 
 787  
             case NULL_N:
 788  
 
 789  3149
                 if (readNextChar() != NULL_U || readNextChar() != NULL_L || readNextChar() != NULL_L) {
 790  2
                     throw uexc("Expected LITERAL: null");
 791  
                 }
 792  3147
                 return EVT_MAP[previousEvent = VALUE_NULL];
 793  
 
 794  
             default:
 795  16619
                 readNumber();
 796  16601
                 return EVT_MAP[previousEvent = VALUE_NUMBER];
 797  
         }
 798  
 
 799  
     }
 800  
 
 801  
     @Override
 802  
     public String getString() {
 803  70367
         if (previousEvent == KEY_NAME || previousEvent == VALUE_STRING || previousEvent == VALUE_NUMBER) {
 804  
 
 805  
             //if there a content in the value buffer read from them, if not use main buffer
 806  70367
             return fallBackCopyBufferLength > 0 ? new String(fallBackCopyBuffer, 0, fallBackCopyBufferLength) : new String(buffer,
 807  
                     startOfValueInBuffer, endOfValueInBuffer - startOfValueInBuffer);
 808  
         } else {
 809  0
             throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getString()");
 810  
         }
 811  
     }
 812  
 
 813  
     @Override
 814  
     public boolean isIntegralNumber() {
 815  
 
 816  16491
         if (previousEvent != VALUE_NUMBER) {
 817  1
             throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support isIntegralNumber()");
 818  
         } else {
 819  16490
             return isCurrentNumberIntegral;
 820  
         }
 821  
     }
 822  
 
 823  
     @Override
 824  
     public int getInt() {
 825  28
         if (previousEvent != VALUE_NUMBER) {
 826  0
             throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getInt()");
 827  28
         } else if (isCurrentNumberIntegral && currentIntegralNumber != null) {
 828  20
             return currentIntegralNumber;
 829  8
         } else if (isCurrentNumberIntegral) {
 830  
             //if there a content in the value buffer read from them, if not use main buffer
 831  6
             final Integer retVal = fallBackCopyBufferLength > 0 ? parseIntegerFromChars(fallBackCopyBuffer, 0, fallBackCopyBufferLength)
 832  
                     : parseIntegerFromChars(buffer, startOfValueInBuffer, endOfValueInBuffer);
 833  6
             if (retVal == null) {
 834  0
                 return getBigDecimal().intValue();
 835  
             } else {
 836  6
                 return retVal.intValue();
 837  
             }
 838  
         } else {
 839  2
             return getBigDecimal().intValue();
 840  
         }
 841  
     }
 842  
 
 843  
     @Override
 844  
     public long getLong() {
 845  9195
         if (previousEvent != VALUE_NUMBER) {
 846  0
             throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getLong()");
 847  9195
         } else if (isCurrentNumberIntegral && currentIntegralNumber != null) {
 848  4955
             return currentIntegralNumber;
 849  4240
         } else if (isCurrentNumberIntegral) {
 850  
             //if there a content in the value buffer read from them, if not use main buffer
 851  4238
             final Long retVal = fallBackCopyBufferLength > 0 ? parseLongFromChars(fallBackCopyBuffer, 0, fallBackCopyBufferLength)
 852  
                     : parseLongFromChars(buffer, startOfValueInBuffer, endOfValueInBuffer);
 853  4238
             if (retVal == null) {
 854  0
                 return getBigDecimal().longValue();
 855  
             } else {
 856  4238
                 return retVal.longValue();
 857  
             }
 858  
         } else {
 859  2
             return getBigDecimal().longValue();
 860  
         }
 861  
 
 862  
     }
 863  
 
 864  
     @Override
 865  
     public BigDecimal getBigDecimal() {
 866  7309
         if (previousEvent != VALUE_NUMBER) {
 867  0
             throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getBigDecimal()");
 868  
             //        } else if (currentBigDecimalNumber != null) {
 869  
             //            return currentBigDecimalNumber;
 870  7309
         } else if (isCurrentNumberIntegral && currentIntegralNumber != null) {
 871  6
             return new BigDecimal(currentIntegralNumber);
 872  7303
         } else if (isCurrentNumberIntegral) {
 873  
             //if there a content in the value buffer read from them, if not use main buffer
 874  8
             final Long retVal = fallBackCopyBufferLength > 0 ? parseLongFromChars(fallBackCopyBuffer, 0, fallBackCopyBufferLength)
 875  
                     : parseLongFromChars(buffer, startOfValueInBuffer, endOfValueInBuffer);
 876  8
             if (retVal == null) {
 877  2
                 return (/*currentBigDecimalNumber = */fallBackCopyBufferLength > 0 ? new BigDecimal(fallBackCopyBuffer, 0,
 878  
                         fallBackCopyBufferLength) : new BigDecimal(buffer, startOfValueInBuffer,
 879  
                         (endOfValueInBuffer - startOfValueInBuffer)));
 880  
             } else {
 881  6
                 return (/*currentBigDecimalNumber = */new BigDecimal(retVal.longValue()));
 882  
             }
 883  
         } else {
 884  
             //if there a content in the value buffer read from them, if not use main buffer
 885  7295
             return (/*currentBigDecimalNumber = */fallBackCopyBufferLength > 0 ? new BigDecimal(fallBackCopyBuffer, 0,
 886  
                     fallBackCopyBufferLength) : new BigDecimal(buffer, startOfValueInBuffer, (endOfValueInBuffer - startOfValueInBuffer)));
 887  
         }
 888  
 
 889  
     }
 890  
 
 891  
     @Override
 892  
     public JsonLocation getLocation() {
 893  909
         return createLocation();
 894  
     }
 895  
 
 896  
     @Override
 897  
     public void close() {
 898  276
         bufferProvider.release(buffer);
 899  276
         valueProvider.release(fallBackCopyBuffer);
 900  
 
 901  
         try {
 902  276
             in.close();
 903  0
         } catch (final IOException e) {
 904  0
             throw new JsonException("Unexpected IO exception " + e.getMessage(), e);
 905  276
         }
 906  276
     }
 907  
 
 908  
     //parse a char[] to long while checking overflow
 909  
     //if overflowed return null
 910  
     //no additional checks since we are sure here that there are no non digits in the array
 911  
     private static Long parseLongFromChars(final char[] chars, final int start, final int end) {
 912  
 
 913  4246
         long retVal = 0;
 914  4246
         final boolean negative = chars[start] == MINUS;
 915  19247
         for (int i = negative ? start + 1 : start; i < end; i++) {
 916  15003
             final long tmp = retVal * 10 + (chars[i] - ZERO);
 917  15003
             if (tmp < retVal) { //check overflow
 918  2
                 return null;
 919  
             } else {
 920  15001
                 retVal = tmp;
 921  
             }
 922  
         }
 923  
 
 924  4244
         return negative ? -retVal : retVal;
 925  
     }
 926  
 
 927  
     //parse a char[] to int while checking overflow
 928  
     //if overflowed return null
 929  
     //no additional checks since we are sure here that there are no non digits in the array
 930  
     private static Integer parseIntegerFromChars(final char[] chars, final int start, final int end) {
 931  
 
 932  6
         int retVal = 0;
 933  6
         final boolean negative = chars[start] == MINUS;
 934  22
         for (int i = negative ? start + 1 : start; i < end; i++) {
 935  16
             final int tmp = retVal * 10 + (chars[i] - ZERO);
 936  16
             if (tmp < retVal) { //check overflow
 937  0
                 return null;
 938  
             } else {
 939  16
                 retVal = tmp;
 940  
             }
 941  
         }
 942  
 
 943  6
         return negative ? -retVal : retVal;
 944  
     }
 945  
 
 946  
     private JsonParsingException uexc(final char c, final String message) {
 947  95
         final JsonLocation location = createLocation();
 948  95
         return new JsonParsingException("Unexpected character '" + c + "' (Codepoint: " + String.valueOf(c).codePointAt(0) + ") on "
 949  
                 + location + ". Reason is [[" + message + "]]", location);
 950  
     }
 951  
 
 952  
     private JsonParsingException uexc(final String message) {
 953  95
         final char c = bufferPos < 0 ? 0 : buffer[bufferPos];
 954  95
         return uexc(c, message);
 955  
     }
 956  
 
 957  
     private JsonParsingException tmc() {
 958  5
         final JsonLocation location = createLocation();
 959  5
         return new JsonParsingException("Too many characters. Maximum string/number length of " + maxValueLength + " exceeded on "
 960  
                 + location, location);
 961  
     }
 962  
 
 963  
     private JsonParsingException uexio(final IOException e) {
 964  0
         final JsonLocation location = createLocation();
 965  0
         return new JsonParsingException("Unexpected IO exception on " + location, e, location);
 966  
     }
 967  
 
 968  
     private JsonParsingException cust(final String message) {
 969  0
         final JsonLocation location = createLocation();
 970  0
         return new JsonParsingException("General exception on " + location + ". Reason is [[" + message + "]]", location);
 971  
     }
 972  
 
 973  
 }