github.com/keysonZZZ/kmg@v0.0.0-20151121023212-05317bfd7d39/kmgRpc/kmgRpcJava/java/src/com/google/gson/stream/JsonReader.java (about) 1 /* 2 * Copyright (C) 2010 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.gson.stream; 18 19 import com.google.gson.internal.JsonReaderInternalAccess; 20 import com.google.gson.internal.bind.JsonTreeReader; 21 import java.io.Closeable; 22 import java.io.EOFException; 23 import java.io.IOException; 24 import java.io.Reader; 25 26 /** 27 * Reads a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>) 28 * encoded value as a stream of tokens. This stream includes both literal 29 * values (strings, numbers, booleans, and nulls) as well as the begin and 30 * end delimiters of objects and arrays. The tokens are traversed in 31 * depth-first order, the same order that they appear in the JSON document. 32 * Within JSON objects, name/value pairs are represented by a single token. 33 * 34 * <h3>Parsing JSON</h3> 35 * To create a recursive descent parser for your own JSON streams, first create 36 * an entry point method that creates a {@code JsonReader}. 37 * 38 * <p>Next, create handler methods for each structure in your JSON text. You'll 39 * need a method for each object type and for each array type. 40 * <ul> 41 * <li>Within <strong>array handling</strong> methods, first call {@link 42 * #beginArray} to consume the array's opening bracket. Then create a 43 * while loop that accumulates values, terminating when {@link #hasNext} 44 * is false. Finally, read the array's closing bracket by calling {@link 45 * #endArray}. 46 * <li>Within <strong>object handling</strong> methods, first call {@link 47 * #beginObject} to consume the object's opening brace. Then create a 48 * while loop that assigns values to local variables based on their name. 49 * This loop should terminate when {@link #hasNext} is false. Finally, 50 * read the object's closing brace by calling {@link #endObject}. 51 * </ul> 52 * <p>When a nested object or array is encountered, delegate to the 53 * corresponding handler method. 54 * 55 * <p>When an unknown name is encountered, strict parsers should fail with an 56 * exception. Lenient parsers should call {@link #skipValue()} to recursively 57 * skip the value's nested tokens, which may otherwise conflict. 58 * 59 * <p>If a value may be null, you should first check using {@link #peek()}. 60 * Null literals can be consumed using either {@link #nextNull()} or {@link 61 * #skipValue()}. 62 * 63 * <h3>Example</h3> 64 * Suppose we'd like to parse a stream of messages such as the following: <pre> {@code 65 * [ 66 * { 67 * "id": 912345678901, 68 * "text": "How do I read a JSON stream in Java?", 69 * "geo": null, 70 * "user": { 71 * "name": "json_newb", 72 * "followers_count": 41 73 * } 74 * }, 75 * { 76 * "id": 912345678902, 77 * "text": "@json_newb just use JsonReader!", 78 * "geo": [50.454722, -104.606667], 79 * "user": { 80 * "name": "jesse", 81 * "followers_count": 2 82 * } 83 * } 84 * ]}</pre> 85 * This code implements the parser for the above structure: <pre> {@code 86 * 87 * public List<Message> readJsonStream(InputStream in) throws IOException { 88 * JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8")); 89 * try { 90 * return readMessagesArray(reader); 91 * } finally { 92 * reader.close(); 93 * } 94 * } 95 * 96 * public List<Message> readMessagesArray(JsonReader reader) throws IOException { 97 * List<Message> messages = new ArrayList<Message>(); 98 * 99 * reader.beginArray(); 100 * while (reader.hasNext()) { 101 * messages.add(readMessage(reader)); 102 * } 103 * reader.endArray(); 104 * return messages; 105 * } 106 * 107 * public Message readMessage(JsonReader reader) throws IOException { 108 * long id = -1; 109 * String text = null; 110 * User user = null; 111 * List<Double> geo = null; 112 * 113 * reader.beginObject(); 114 * while (reader.hasNext()) { 115 * String name = reader.nextName(); 116 * if (name.equals("id")) { 117 * id = reader.nextLong(); 118 * } else if (name.equals("text")) { 119 * text = reader.nextString(); 120 * } else if (name.equals("geo") && reader.peek() != JsonToken.NULL) { 121 * geo = readDoublesArray(reader); 122 * } else if (name.equals("user")) { 123 * user = readUser(reader); 124 * } else { 125 * reader.skipValue(); 126 * } 127 * } 128 * reader.endObject(); 129 * return new Message(id, text, user, geo); 130 * } 131 * 132 * public List<Double> readDoublesArray(JsonReader reader) throws IOException { 133 * List<Double> doubles = new ArrayList<Double>(); 134 * 135 * reader.beginArray(); 136 * while (reader.hasNext()) { 137 * doubles.add(reader.nextDouble()); 138 * } 139 * reader.endArray(); 140 * return doubles; 141 * } 142 * 143 * public User readUser(JsonReader reader) throws IOException { 144 * String username = null; 145 * int followersCount = -1; 146 * 147 * reader.beginObject(); 148 * while (reader.hasNext()) { 149 * String name = reader.nextName(); 150 * if (name.equals("name")) { 151 * username = reader.nextString(); 152 * } else if (name.equals("followers_count")) { 153 * followersCount = reader.nextInt(); 154 * } else { 155 * reader.skipValue(); 156 * } 157 * } 158 * reader.endObject(); 159 * return new User(username, followersCount); 160 * }}</pre> 161 * 162 * <h3>Number Handling</h3> 163 * This reader permits numeric values to be read as strings and string values to 164 * be read as numbers. For example, both elements of the JSON array {@code 165 * [1, "1"]} may be read using either {@link #nextInt} or {@link #nextString}. 166 * This behavior is intended to prevent lossy numeric conversions: double is 167 * JavaScript's only numeric type and very large values like {@code 168 * 9007199254740993} cannot be represented exactly on that platform. To minimize 169 * precision loss, extremely large values should be written and read as strings 170 * in JSON. 171 * 172 * <a name="nonexecuteprefix"/><h3>Non-Execute Prefix</h3> 173 * Web servers that serve private data using JSON may be vulnerable to <a 174 * href="http://en.wikipedia.org/wiki/JSON#Cross-site_request_forgery">Cross-site 175 * request forgery</a> attacks. In such an attack, a malicious site gains access 176 * to a private JSON file by executing it with an HTML {@code <script>} tag. 177 * 178 * <p>Prefixing JSON files with <code>")]}'\n"</code> makes them non-executable 179 * by {@code <script>} tags, disarming the attack. Since the prefix is malformed 180 * JSON, strict parsing fails when it is encountered. This class permits the 181 * non-execute prefix when {@link #setLenient(boolean) lenient parsing} is 182 * enabled. 183 * 184 * <p>Each {@code JsonReader} may be used to read a single JSON stream. Instances 185 * of this class are not thread safe. 186 * 187 * @author Jesse Wilson 188 * @since 1.6 189 */ 190 public class JsonReader implements Closeable { 191 /** The only non-execute prefix this parser permits */ 192 private static final char[] NON_EXECUTE_PREFIX = ")]}'\n".toCharArray(); 193 private static final long MIN_INCOMPLETE_INTEGER = Long.MIN_VALUE / 10; 194 195 private static final int PEEKED_NONE = 0; 196 private static final int PEEKED_BEGIN_OBJECT = 1; 197 private static final int PEEKED_END_OBJECT = 2; 198 private static final int PEEKED_BEGIN_ARRAY = 3; 199 private static final int PEEKED_END_ARRAY = 4; 200 private static final int PEEKED_TRUE = 5; 201 private static final int PEEKED_FALSE = 6; 202 private static final int PEEKED_NULL = 7; 203 private static final int PEEKED_SINGLE_QUOTED = 8; 204 private static final int PEEKED_DOUBLE_QUOTED = 9; 205 private static final int PEEKED_UNQUOTED = 10; 206 /** When this is returned, the string value is stored in peekedString. */ 207 private static final int PEEKED_BUFFERED = 11; 208 private static final int PEEKED_SINGLE_QUOTED_NAME = 12; 209 private static final int PEEKED_DOUBLE_QUOTED_NAME = 13; 210 private static final int PEEKED_UNQUOTED_NAME = 14; 211 /** When this is returned, the integer value is stored in peekedLong. */ 212 private static final int PEEKED_LONG = 15; 213 private static final int PEEKED_NUMBER = 16; 214 private static final int PEEKED_EOF = 17; 215 216 /* State machine when parsing numbers */ 217 private static final int NUMBER_CHAR_NONE = 0; 218 private static final int NUMBER_CHAR_SIGN = 1; 219 private static final int NUMBER_CHAR_DIGIT = 2; 220 private static final int NUMBER_CHAR_DECIMAL = 3; 221 private static final int NUMBER_CHAR_FRACTION_DIGIT = 4; 222 private static final int NUMBER_CHAR_EXP_E = 5; 223 private static final int NUMBER_CHAR_EXP_SIGN = 6; 224 private static final int NUMBER_CHAR_EXP_DIGIT = 7; 225 226 /** The input JSON. */ 227 private final Reader in; 228 229 /** True to accept non-spec compliant JSON */ 230 private boolean lenient = false; 231 232 /** 233 * Use a manual buffer to easily read and unread upcoming characters, and 234 * also so we can create strings without an intermediate StringBuilder. 235 * We decode literals directly out of this buffer, so it must be at least as 236 * long as the longest token that can be reported as a number. 237 */ 238 private final char[] buffer = new char[1024]; 239 private int pos = 0; 240 private int limit = 0; 241 242 private int lineNumber = 0; 243 private int lineStart = 0; 244 245 private int peeked = PEEKED_NONE; 246 247 /** 248 * A peeked value that was composed entirely of digits with an optional 249 * leading dash. Positive values may not have a leading 0. 250 */ 251 private long peekedLong; 252 253 /** 254 * The number of characters in a peeked number literal. Increment 'pos' by 255 * this after reading a number. 256 */ 257 private int peekedNumberLength; 258 259 /** 260 * A peeked string that should be parsed on the next double, long or string. 261 * This is populated before a numeric value is parsed and used if that parsing 262 * fails. 263 */ 264 private String peekedString; 265 266 /* 267 * The nesting stack. Using a manual array rather than an ArrayList saves 20%. 268 */ 269 private int[] stack = new int[32]; 270 private int stackSize = 0; 271 { 272 stack[stackSize++] = JsonScope.EMPTY_DOCUMENT; 273 } 274 275 /* 276 * The path members. It corresponds directly to stack: At indices where the 277 * stack contains an object (EMPTY_OBJECT, DANGLING_NAME or NONEMPTY_OBJECT), 278 * pathNames contains the name at this scope. Where it contains an array 279 * (EMPTY_ARRAY, NONEMPTY_ARRAY) pathIndices contains the current index in 280 * that array. Otherwise the value is undefined, and we take advantage of that 281 * by incrementing pathIndices when doing so isn't useful. 282 */ 283 private String[] pathNames = new String[32]; 284 private int[] pathIndices = new int[32]; 285 286 /** 287 * Creates a new instance that reads a JSON-encoded stream from {@code in}. 288 */ 289 public JsonReader(Reader in) { 290 if (in == null) { 291 throw new NullPointerException("in == null"); 292 } 293 this.in = in; 294 } 295 296 /** 297 * Configure this parser to be be liberal in what it accepts. By default, 298 * this parser is strict and only accepts JSON as specified by <a 299 * href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the 300 * parser to lenient causes it to ignore the following syntax errors: 301 * 302 * <ul> 303 * <li>Streams that start with the <a href="#nonexecuteprefix">non-execute 304 * prefix</a>, <code>")]}'\n"</code>. 305 * <li>Streams that include multiple top-level values. With strict parsing, 306 * each stream must contain exactly one top-level value. 307 * <li>Top-level values of any type. With strict parsing, the top-level 308 * value must be an object or an array. 309 * <li>Numbers may be {@link Double#isNaN() NaNs} or {@link 310 * Double#isInfinite() infinities}. 311 * <li>End of line comments starting with {@code //} or {@code #} and 312 * ending with a newline character. 313 * <li>C-style comments starting with {@code /*} and ending with 314 * {@code *}{@code /}. Such comments may not be nested. 315 * <li>Names that are unquoted or {@code 'single quoted'}. 316 * <li>Strings that are unquoted or {@code 'single quoted'}. 317 * <li>Array elements separated by {@code ;} instead of {@code ,}. 318 * <li>Unnecessary array separators. These are interpreted as if null 319 * was the omitted value. 320 * <li>Names and values separated by {@code =} or {@code =>} instead of 321 * {@code :}. 322 * <li>Name/value pairs separated by {@code ;} instead of {@code ,}. 323 * </ul> 324 */ 325 public final void setLenient(boolean lenient) { 326 this.lenient = lenient; 327 } 328 329 /** 330 * Returns true if this parser is liberal in what it accepts. 331 */ 332 public final boolean isLenient() { 333 return lenient; 334 } 335 336 /** 337 * Consumes the next token from the JSON stream and asserts that it is the 338 * beginning of a new array. 339 */ 340 public void beginArray() throws IOException { 341 int p = peeked; 342 if (p == PEEKED_NONE) { 343 p = doPeek(); 344 } 345 if (p == PEEKED_BEGIN_ARRAY) { 346 push(JsonScope.EMPTY_ARRAY); 347 pathIndices[stackSize - 1] = 0; 348 peeked = PEEKED_NONE; 349 } else { 350 throw new IllegalStateException("Expected BEGIN_ARRAY but was " + peek() 351 + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath()); 352 } 353 } 354 355 /** 356 * Consumes the next token from the JSON stream and asserts that it is the 357 * end of the current array. 358 */ 359 public void endArray() throws IOException { 360 int p = peeked; 361 if (p == PEEKED_NONE) { 362 p = doPeek(); 363 } 364 if (p == PEEKED_END_ARRAY) { 365 stackSize--; 366 pathIndices[stackSize - 1]++; 367 peeked = PEEKED_NONE; 368 } else { 369 throw new IllegalStateException("Expected END_ARRAY but was " + peek() 370 + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath()); 371 } 372 } 373 374 /** 375 * Consumes the next token from the JSON stream and asserts that it is the 376 * beginning of a new object. 377 */ 378 public void beginObject() throws IOException { 379 int p = peeked; 380 if (p == PEEKED_NONE) { 381 p = doPeek(); 382 } 383 if (p == PEEKED_BEGIN_OBJECT) { 384 push(JsonScope.EMPTY_OBJECT); 385 peeked = PEEKED_NONE; 386 } else { 387 throw new IllegalStateException("Expected BEGIN_OBJECT but was " + peek() 388 + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath()); 389 } 390 } 391 392 /** 393 * Consumes the next token from the JSON stream and asserts that it is the 394 * end of the current object. 395 */ 396 public void endObject() throws IOException { 397 int p = peeked; 398 if (p == PEEKED_NONE) { 399 p = doPeek(); 400 } 401 if (p == PEEKED_END_OBJECT) { 402 stackSize--; 403 pathNames[stackSize] = null; // Free the last path name so that it can be garbage collected! 404 pathIndices[stackSize - 1]++; 405 peeked = PEEKED_NONE; 406 } else { 407 throw new IllegalStateException("Expected END_OBJECT but was " + peek() 408 + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath()); 409 } 410 } 411 412 /** 413 * Returns true if the current array or object has another element. 414 */ 415 public boolean hasNext() throws IOException { 416 int p = peeked; 417 if (p == PEEKED_NONE) { 418 p = doPeek(); 419 } 420 return p != PEEKED_END_OBJECT && p != PEEKED_END_ARRAY; 421 } 422 423 /** 424 * Returns the type of the next token without consuming it. 425 */ 426 public JsonToken peek() throws IOException { 427 int p = peeked; 428 if (p == PEEKED_NONE) { 429 p = doPeek(); 430 } 431 432 switch (p) { 433 case PEEKED_BEGIN_OBJECT: 434 return JsonToken.BEGIN_OBJECT; 435 case PEEKED_END_OBJECT: 436 return JsonToken.END_OBJECT; 437 case PEEKED_BEGIN_ARRAY: 438 return JsonToken.BEGIN_ARRAY; 439 case PEEKED_END_ARRAY: 440 return JsonToken.END_ARRAY; 441 case PEEKED_SINGLE_QUOTED_NAME: 442 case PEEKED_DOUBLE_QUOTED_NAME: 443 case PEEKED_UNQUOTED_NAME: 444 return JsonToken.NAME; 445 case PEEKED_TRUE: 446 case PEEKED_FALSE: 447 return JsonToken.BOOLEAN; 448 case PEEKED_NULL: 449 return JsonToken.NULL; 450 case PEEKED_SINGLE_QUOTED: 451 case PEEKED_DOUBLE_QUOTED: 452 case PEEKED_UNQUOTED: 453 case PEEKED_BUFFERED: 454 return JsonToken.STRING; 455 case PEEKED_LONG: 456 case PEEKED_NUMBER: 457 return JsonToken.NUMBER; 458 case PEEKED_EOF: 459 return JsonToken.END_DOCUMENT; 460 default: 461 throw new AssertionError(); 462 } 463 } 464 465 private int doPeek() throws IOException { 466 int peekStack = stack[stackSize - 1]; 467 if (peekStack == JsonScope.EMPTY_ARRAY) { 468 stack[stackSize - 1] = JsonScope.NONEMPTY_ARRAY; 469 } else if (peekStack == JsonScope.NONEMPTY_ARRAY) { 470 // Look for a comma before the next element. 471 int c = nextNonWhitespace(true); 472 switch (c) { 473 case ']': 474 return peeked = PEEKED_END_ARRAY; 475 case ';': 476 checkLenient(); // fall-through 477 case ',': 478 break; 479 default: 480 throw syntaxError("Unterminated array"); 481 } 482 } else if (peekStack == JsonScope.EMPTY_OBJECT || peekStack == JsonScope.NONEMPTY_OBJECT) { 483 stack[stackSize - 1] = JsonScope.DANGLING_NAME; 484 // Look for a comma before the next element. 485 if (peekStack == JsonScope.NONEMPTY_OBJECT) { 486 int c = nextNonWhitespace(true); 487 switch (c) { 488 case '}': 489 return peeked = PEEKED_END_OBJECT; 490 case ';': 491 checkLenient(); // fall-through 492 case ',': 493 break; 494 default: 495 throw syntaxError("Unterminated object"); 496 } 497 } 498 int c = nextNonWhitespace(true); 499 switch (c) { 500 case '"': 501 return peeked = PEEKED_DOUBLE_QUOTED_NAME; 502 case '\'': 503 checkLenient(); 504 return peeked = PEEKED_SINGLE_QUOTED_NAME; 505 case '}': 506 if (peekStack != JsonScope.NONEMPTY_OBJECT) { 507 return peeked = PEEKED_END_OBJECT; 508 } else { 509 throw syntaxError("Expected name"); 510 } 511 default: 512 checkLenient(); 513 pos--; // Don't consume the first character in an unquoted string. 514 if (isLiteral((char) c)) { 515 return peeked = PEEKED_UNQUOTED_NAME; 516 } else { 517 throw syntaxError("Expected name"); 518 } 519 } 520 } else if (peekStack == JsonScope.DANGLING_NAME) { 521 stack[stackSize - 1] = JsonScope.NONEMPTY_OBJECT; 522 // Look for a colon before the value. 523 int c = nextNonWhitespace(true); 524 switch (c) { 525 case ':': 526 break; 527 case '=': 528 checkLenient(); 529 if ((pos < limit || fillBuffer(1)) && buffer[pos] == '>') { 530 pos++; 531 } 532 break; 533 default: 534 throw syntaxError("Expected ':'"); 535 } 536 } else if (peekStack == JsonScope.EMPTY_DOCUMENT) { 537 if (lenient) { 538 consumeNonExecutePrefix(); 539 } 540 stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT; 541 } else if (peekStack == JsonScope.NONEMPTY_DOCUMENT) { 542 int c = nextNonWhitespace(false); 543 if (c == -1) { 544 return peeked = PEEKED_EOF; 545 } else { 546 checkLenient(); 547 pos--; 548 } 549 } else if (peekStack == JsonScope.CLOSED) { 550 throw new IllegalStateException("JsonReader is closed"); 551 } 552 553 int c = nextNonWhitespace(true); 554 switch (c) { 555 case ']': 556 if (peekStack == JsonScope.EMPTY_ARRAY) { 557 return peeked = PEEKED_END_ARRAY; 558 } 559 // fall-through to handle ",]" 560 case ';': 561 case ',': 562 // In lenient mode, a 0-length literal in an array means 'null'. 563 if (peekStack == JsonScope.EMPTY_ARRAY || peekStack == JsonScope.NONEMPTY_ARRAY) { 564 checkLenient(); 565 pos--; 566 return peeked = PEEKED_NULL; 567 } else { 568 throw syntaxError("Unexpected value"); 569 } 570 case '\'': 571 checkLenient(); 572 return peeked = PEEKED_SINGLE_QUOTED; 573 case '"': 574 if (stackSize == 1) { 575 checkLenient(); 576 } 577 return peeked = PEEKED_DOUBLE_QUOTED; 578 case '[': 579 return peeked = PEEKED_BEGIN_ARRAY; 580 case '{': 581 return peeked = PEEKED_BEGIN_OBJECT; 582 default: 583 pos--; // Don't consume the first character in a literal value. 584 } 585 586 if (stackSize == 1) { 587 checkLenient(); // Top-level value isn't an array or an object. 588 } 589 590 int result = peekKeyword(); 591 if (result != PEEKED_NONE) { 592 return result; 593 } 594 595 result = peekNumber(); 596 if (result != PEEKED_NONE) { 597 return result; 598 } 599 600 if (!isLiteral(buffer[pos])) { 601 throw syntaxError("Expected value"); 602 } 603 604 checkLenient(); 605 return peeked = PEEKED_UNQUOTED; 606 } 607 608 private int peekKeyword() throws IOException { 609 // Figure out which keyword we're matching against by its first character. 610 char c = buffer[pos]; 611 String keyword; 612 String keywordUpper; 613 int peeking; 614 if (c == 't' || c == 'T') { 615 keyword = "true"; 616 keywordUpper = "TRUE"; 617 peeking = PEEKED_TRUE; 618 } else if (c == 'f' || c == 'F') { 619 keyword = "false"; 620 keywordUpper = "FALSE"; 621 peeking = PEEKED_FALSE; 622 } else if (c == 'n' || c == 'N') { 623 keyword = "null"; 624 keywordUpper = "NULL"; 625 peeking = PEEKED_NULL; 626 } else { 627 return PEEKED_NONE; 628 } 629 630 // Confirm that chars [1..length) match the keyword. 631 int length = keyword.length(); 632 for (int i = 1; i < length; i++) { 633 if (pos + i >= limit && !fillBuffer(i + 1)) { 634 return PEEKED_NONE; 635 } 636 c = buffer[pos + i]; 637 if (c != keyword.charAt(i) && c != keywordUpper.charAt(i)) { 638 return PEEKED_NONE; 639 } 640 } 641 642 if ((pos + length < limit || fillBuffer(length + 1)) 643 && isLiteral(buffer[pos + length])) { 644 return PEEKED_NONE; // Don't match trues, falsey or nullsoft! 645 } 646 647 // We've found the keyword followed either by EOF or by a non-literal character. 648 pos += length; 649 return peeked = peeking; 650 } 651 652 private int peekNumber() throws IOException { 653 // Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field access. 654 char[] buffer = this.buffer; 655 int p = pos; 656 int l = limit; 657 658 long value = 0; // Negative to accommodate Long.MIN_VALUE more easily. 659 boolean negative = false; 660 boolean fitsInLong = true; 661 int last = NUMBER_CHAR_NONE; 662 663 int i = 0; 664 665 charactersOfNumber: 666 for (; true; i++) { 667 if (p + i == l) { 668 if (i == buffer.length) { 669 // Though this looks like a well-formed number, it's too long to continue reading. Give up 670 // and let the application handle this as an unquoted literal. 671 return PEEKED_NONE; 672 } 673 if (!fillBuffer(i + 1)) { 674 break; 675 } 676 p = pos; 677 l = limit; 678 } 679 680 char c = buffer[p + i]; 681 switch (c) { 682 case '-': 683 if (last == NUMBER_CHAR_NONE) { 684 negative = true; 685 last = NUMBER_CHAR_SIGN; 686 continue; 687 } else if (last == NUMBER_CHAR_EXP_E) { 688 last = NUMBER_CHAR_EXP_SIGN; 689 continue; 690 } 691 return PEEKED_NONE; 692 693 case '+': 694 if (last == NUMBER_CHAR_EXP_E) { 695 last = NUMBER_CHAR_EXP_SIGN; 696 continue; 697 } 698 return PEEKED_NONE; 699 700 case 'e': 701 case 'E': 702 if (last == NUMBER_CHAR_DIGIT || last == NUMBER_CHAR_FRACTION_DIGIT) { 703 last = NUMBER_CHAR_EXP_E; 704 continue; 705 } 706 return PEEKED_NONE; 707 708 case '.': 709 if (last == NUMBER_CHAR_DIGIT) { 710 last = NUMBER_CHAR_DECIMAL; 711 continue; 712 } 713 return PEEKED_NONE; 714 715 default: 716 if (c < '0' || c > '9') { 717 if (!isLiteral(c)) { 718 break charactersOfNumber; 719 } 720 return PEEKED_NONE; 721 } 722 if (last == NUMBER_CHAR_SIGN || last == NUMBER_CHAR_NONE) { 723 value = -(c - '0'); 724 last = NUMBER_CHAR_DIGIT; 725 } else if (last == NUMBER_CHAR_DIGIT) { 726 if (value == 0) { 727 return PEEKED_NONE; // Leading '0' prefix is not allowed (since it could be octal). 728 } 729 long newValue = value * 10 - (c - '0'); 730 fitsInLong &= value > MIN_INCOMPLETE_INTEGER 731 || (value == MIN_INCOMPLETE_INTEGER && newValue < value); 732 value = newValue; 733 } else if (last == NUMBER_CHAR_DECIMAL) { 734 last = NUMBER_CHAR_FRACTION_DIGIT; 735 } else if (last == NUMBER_CHAR_EXP_E || last == NUMBER_CHAR_EXP_SIGN) { 736 last = NUMBER_CHAR_EXP_DIGIT; 737 } 738 } 739 } 740 741 // We've read a complete number. Decide if it's a PEEKED_LONG or a PEEKED_NUMBER. 742 if (last == NUMBER_CHAR_DIGIT && fitsInLong && (value != Long.MIN_VALUE || negative)) { 743 peekedLong = negative ? value : -value; 744 pos += i; 745 return peeked = PEEKED_LONG; 746 } else if (last == NUMBER_CHAR_DIGIT || last == NUMBER_CHAR_FRACTION_DIGIT 747 || last == NUMBER_CHAR_EXP_DIGIT) { 748 peekedNumberLength = i; 749 return peeked = PEEKED_NUMBER; 750 } else { 751 return PEEKED_NONE; 752 } 753 } 754 755 private boolean isLiteral(char c) throws IOException { 756 switch (c) { 757 case '/': 758 case '\\': 759 case ';': 760 case '#': 761 case '=': 762 checkLenient(); // fall-through 763 case '{': 764 case '}': 765 case '[': 766 case ']': 767 case ':': 768 case ',': 769 case ' ': 770 case '\t': 771 case '\f': 772 case '\r': 773 case '\n': 774 return false; 775 default: 776 return true; 777 } 778 } 779 780 /** 781 * Returns the next token, a {@link JsonToken#NAME property name}, and 782 * consumes it. 783 * 784 * @throws IOException if the next token in the stream is not a property 785 * name. 786 */ 787 public String nextName() throws IOException { 788 int p = peeked; 789 if (p == PEEKED_NONE) { 790 p = doPeek(); 791 } 792 String result; 793 if (p == PEEKED_UNQUOTED_NAME) { 794 result = nextUnquotedValue(); 795 } else if (p == PEEKED_SINGLE_QUOTED_NAME) { 796 result = nextQuotedValue('\''); 797 } else if (p == PEEKED_DOUBLE_QUOTED_NAME) { 798 result = nextQuotedValue('"'); 799 } else { 800 throw new IllegalStateException("Expected a name but was " + peek() 801 + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath()); 802 } 803 peeked = PEEKED_NONE; 804 pathNames[stackSize - 1] = result; 805 return result; 806 } 807 808 /** 809 * Returns the {@link JsonToken#STRING string} value of the next token, 810 * consuming it. If the next token is a number, this method will return its 811 * string form. 812 * 813 * @throws IllegalStateException if the next token is not a string or if 814 * this reader is closed. 815 */ 816 public String nextString() throws IOException { 817 int p = peeked; 818 if (p == PEEKED_NONE) { 819 p = doPeek(); 820 } 821 String result; 822 if (p == PEEKED_UNQUOTED) { 823 result = nextUnquotedValue(); 824 } else if (p == PEEKED_SINGLE_QUOTED) { 825 result = nextQuotedValue('\''); 826 } else if (p == PEEKED_DOUBLE_QUOTED) { 827 result = nextQuotedValue('"'); 828 } else if (p == PEEKED_BUFFERED) { 829 result = peekedString; 830 peekedString = null; 831 } else if (p == PEEKED_LONG) { 832 result = Long.toString(peekedLong); 833 } else if (p == PEEKED_NUMBER) { 834 result = new String(buffer, pos, peekedNumberLength); 835 pos += peekedNumberLength; 836 } else { 837 throw new IllegalStateException("Expected a string but was " + peek() 838 + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath()); 839 } 840 peeked = PEEKED_NONE; 841 pathIndices[stackSize - 1]++; 842 return result; 843 } 844 845 /** 846 * Returns the {@link JsonToken#BOOLEAN boolean} value of the next token, 847 * consuming it. 848 * 849 * @throws IllegalStateException if the next token is not a boolean or if 850 * this reader is closed. 851 */ 852 public boolean nextBoolean() throws IOException { 853 int p = peeked; 854 if (p == PEEKED_NONE) { 855 p = doPeek(); 856 } 857 if (p == PEEKED_TRUE) { 858 peeked = PEEKED_NONE; 859 pathIndices[stackSize - 1]++; 860 return true; 861 } else if (p == PEEKED_FALSE) { 862 peeked = PEEKED_NONE; 863 pathIndices[stackSize - 1]++; 864 return false; 865 } 866 throw new IllegalStateException("Expected a boolean but was " + peek() 867 + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath()); 868 } 869 870 /** 871 * Consumes the next token from the JSON stream and asserts that it is a 872 * literal null. 873 * 874 * @throws IllegalStateException if the next token is not null or if this 875 * reader is closed. 876 */ 877 public void nextNull() throws IOException { 878 int p = peeked; 879 if (p == PEEKED_NONE) { 880 p = doPeek(); 881 } 882 if (p == PEEKED_NULL) { 883 peeked = PEEKED_NONE; 884 pathIndices[stackSize - 1]++; 885 } else { 886 throw new IllegalStateException("Expected null but was " + peek() 887 + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath()); 888 } 889 } 890 891 /** 892 * Returns the {@link JsonToken#NUMBER double} value of the next token, 893 * consuming it. If the next token is a string, this method will attempt to 894 * parse it as a double using {@link Double#parseDouble(String)}. 895 * 896 * @throws IllegalStateException if the next token is not a literal value. 897 * @throws NumberFormatException if the next literal value cannot be parsed 898 * as a double, or is non-finite. 899 */ 900 public double nextDouble() throws IOException { 901 int p = peeked; 902 if (p == PEEKED_NONE) { 903 p = doPeek(); 904 } 905 906 if (p == PEEKED_LONG) { 907 peeked = PEEKED_NONE; 908 pathIndices[stackSize - 1]++; 909 return (double) peekedLong; 910 } 911 912 if (p == PEEKED_NUMBER) { 913 peekedString = new String(buffer, pos, peekedNumberLength); 914 pos += peekedNumberLength; 915 } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) { 916 peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"'); 917 } else if (p == PEEKED_UNQUOTED) { 918 peekedString = nextUnquotedValue(); 919 } else if (p != PEEKED_BUFFERED) { 920 throw new IllegalStateException("Expected a double but was " + peek() 921 + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath()); 922 } 923 924 peeked = PEEKED_BUFFERED; 925 double result = Double.parseDouble(peekedString); // don't catch this NumberFormatException. 926 if (!lenient && (Double.isNaN(result) || Double.isInfinite(result))) { 927 throw new MalformedJsonException("JSON forbids NaN and infinities: " + result 928 + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath()); 929 } 930 peekedString = null; 931 peeked = PEEKED_NONE; 932 pathIndices[stackSize - 1]++; 933 return result; 934 } 935 936 /** 937 * Returns the {@link JsonToken#NUMBER long} value of the next token, 938 * consuming it. If the next token is a string, this method will attempt to 939 * parse it as a long. If the next token's numeric value cannot be exactly 940 * represented by a Java {@code long}, this method throws. 941 * 942 * @throws IllegalStateException if the next token is not a literal value. 943 * @throws NumberFormatException if the next literal value cannot be parsed 944 * as a number, or exactly represented as a long. 945 */ 946 public long nextLong() throws IOException { 947 int p = peeked; 948 if (p == PEEKED_NONE) { 949 p = doPeek(); 950 } 951 952 if (p == PEEKED_LONG) { 953 peeked = PEEKED_NONE; 954 pathIndices[stackSize - 1]++; 955 return peekedLong; 956 } 957 958 if (p == PEEKED_NUMBER) { 959 peekedString = new String(buffer, pos, peekedNumberLength); 960 pos += peekedNumberLength; 961 } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) { 962 peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"'); 963 try { 964 long result = Long.parseLong(peekedString); 965 peeked = PEEKED_NONE; 966 pathIndices[stackSize - 1]++; 967 return result; 968 } catch (NumberFormatException ignored) { 969 // Fall back to parse as a double below. 970 } 971 } else { 972 throw new IllegalStateException("Expected a long but was " + peek() 973 + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath()); 974 } 975 976 peeked = PEEKED_BUFFERED; 977 double asDouble = Double.parseDouble(peekedString); // don't catch this NumberFormatException. 978 long result = (long) asDouble; 979 if (result != asDouble) { // Make sure no precision was lost casting to 'long'. 980 throw new NumberFormatException("Expected a long but was " + peekedString 981 + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath()); 982 } 983 peekedString = null; 984 peeked = PEEKED_NONE; 985 pathIndices[stackSize - 1]++; 986 return result; 987 } 988 989 /** 990 * Returns the string up to but not including {@code quote}, unescaping any 991 * character escape sequences encountered along the way. The opening quote 992 * should have already been read. This consumes the closing quote, but does 993 * not include it in the returned string. 994 * 995 * @param quote either ' or ". 996 * @throws NumberFormatException if any unicode escape sequences are 997 * malformed. 998 */ 999 private String nextQuotedValue(char quote) throws IOException { 1000 // Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field access. 1001 char[] buffer = this.buffer; 1002 StringBuilder builder = new StringBuilder(); 1003 while (true) { 1004 int p = pos; 1005 int l = limit; 1006 /* the index of the first character not yet appended to the builder. */ 1007 int start = p; 1008 while (p < l) { 1009 int c = buffer[p++]; 1010 1011 if (c == quote) { 1012 pos = p; 1013 builder.append(buffer, start, p - start - 1); 1014 return builder.toString(); 1015 } else if (c == '\\') { 1016 pos = p; 1017 builder.append(buffer, start, p - start - 1); 1018 builder.append(readEscapeCharacter()); 1019 p = pos; 1020 l = limit; 1021 start = p; 1022 } else if (c == '\n') { 1023 lineNumber++; 1024 lineStart = p; 1025 } 1026 } 1027 1028 builder.append(buffer, start, p - start); 1029 pos = p; 1030 if (!fillBuffer(1)) { 1031 throw syntaxError("Unterminated string"); 1032 } 1033 } 1034 } 1035 1036 /** 1037 * Returns an unquoted value as a string. 1038 */ 1039 @SuppressWarnings("fallthrough") 1040 private String nextUnquotedValue() throws IOException { 1041 StringBuilder builder = null; 1042 int i = 0; 1043 1044 findNonLiteralCharacter: 1045 while (true) { 1046 for (; pos + i < limit; i++) { 1047 switch (buffer[pos + i]) { 1048 case '/': 1049 case '\\': 1050 case ';': 1051 case '#': 1052 case '=': 1053 checkLenient(); // fall-through 1054 case '{': 1055 case '}': 1056 case '[': 1057 case ']': 1058 case ':': 1059 case ',': 1060 case ' ': 1061 case '\t': 1062 case '\f': 1063 case '\r': 1064 case '\n': 1065 break findNonLiteralCharacter; 1066 } 1067 } 1068 1069 // Attempt to load the entire literal into the buffer at once. 1070 if (i < buffer.length) { 1071 if (fillBuffer(i + 1)) { 1072 continue; 1073 } else { 1074 break; 1075 } 1076 } 1077 1078 // use a StringBuilder when the value is too long. This is too long to be a number! 1079 if (builder == null) { 1080 builder = new StringBuilder(); 1081 } 1082 builder.append(buffer, pos, i); 1083 pos += i; 1084 i = 0; 1085 if (!fillBuffer(1)) { 1086 break; 1087 } 1088 } 1089 1090 String result; 1091 if (builder == null) { 1092 result = new String(buffer, pos, i); 1093 } else { 1094 builder.append(buffer, pos, i); 1095 result = builder.toString(); 1096 } 1097 pos += i; 1098 return result; 1099 } 1100 1101 private void skipQuotedValue(char quote) throws IOException { 1102 // Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field access. 1103 char[] buffer = this.buffer; 1104 do { 1105 int p = pos; 1106 int l = limit; 1107 /* the index of the first character not yet appended to the builder. */ 1108 while (p < l) { 1109 int c = buffer[p++]; 1110 if (c == quote) { 1111 pos = p; 1112 return; 1113 } else if (c == '\\') { 1114 pos = p; 1115 readEscapeCharacter(); 1116 p = pos; 1117 l = limit; 1118 } else if (c == '\n') { 1119 lineNumber++; 1120 lineStart = p; 1121 } 1122 } 1123 pos = p; 1124 } while (fillBuffer(1)); 1125 throw syntaxError("Unterminated string"); 1126 } 1127 1128 private void skipUnquotedValue() throws IOException { 1129 do { 1130 int i = 0; 1131 for (; pos + i < limit; i++) { 1132 switch (buffer[pos + i]) { 1133 case '/': 1134 case '\\': 1135 case ';': 1136 case '#': 1137 case '=': 1138 checkLenient(); // fall-through 1139 case '{': 1140 case '}': 1141 case '[': 1142 case ']': 1143 case ':': 1144 case ',': 1145 case ' ': 1146 case '\t': 1147 case '\f': 1148 case '\r': 1149 case '\n': 1150 pos += i; 1151 return; 1152 } 1153 } 1154 pos += i; 1155 } while (fillBuffer(1)); 1156 } 1157 1158 /** 1159 * Returns the {@link JsonToken#NUMBER int} value of the next token, 1160 * consuming it. If the next token is a string, this method will attempt to 1161 * parse it as an int. If the next token's numeric value cannot be exactly 1162 * represented by a Java {@code int}, this method throws. 1163 * 1164 * @throws IllegalStateException if the next token is not a literal value. 1165 * @throws NumberFormatException if the next literal value cannot be parsed 1166 * as a number, or exactly represented as an int. 1167 */ 1168 public int nextInt() throws IOException { 1169 int p = peeked; 1170 if (p == PEEKED_NONE) { 1171 p = doPeek(); 1172 } 1173 1174 int result; 1175 if (p == PEEKED_LONG) { 1176 result = (int) peekedLong; 1177 if (peekedLong != result) { // Make sure no precision was lost casting to 'int'. 1178 throw new NumberFormatException("Expected an int but was " + peekedLong 1179 + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath()); 1180 } 1181 peeked = PEEKED_NONE; 1182 pathIndices[stackSize - 1]++; 1183 return result; 1184 } 1185 1186 if (p == PEEKED_NUMBER) { 1187 peekedString = new String(buffer, pos, peekedNumberLength); 1188 pos += peekedNumberLength; 1189 } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) { 1190 peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"'); 1191 try { 1192 result = Integer.parseInt(peekedString); 1193 peeked = PEEKED_NONE; 1194 pathIndices[stackSize - 1]++; 1195 return result; 1196 } catch (NumberFormatException ignored) { 1197 // Fall back to parse as a double below. 1198 } 1199 } else { 1200 throw new IllegalStateException("Expected an int but was " + peek() 1201 + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath()); 1202 } 1203 1204 peeked = PEEKED_BUFFERED; 1205 double asDouble = Double.parseDouble(peekedString); // don't catch this NumberFormatException. 1206 result = (int) asDouble; 1207 if (result != asDouble) { // Make sure no precision was lost casting to 'int'. 1208 throw new NumberFormatException("Expected an int but was " + peekedString 1209 + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath()); 1210 } 1211 peekedString = null; 1212 peeked = PEEKED_NONE; 1213 pathIndices[stackSize - 1]++; 1214 return result; 1215 } 1216 1217 /** 1218 * Closes this JSON reader and the underlying {@link Reader}. 1219 */ 1220 public void close() throws IOException { 1221 peeked = PEEKED_NONE; 1222 stack[0] = JsonScope.CLOSED; 1223 stackSize = 1; 1224 in.close(); 1225 } 1226 1227 /** 1228 * Skips the next value recursively. If it is an object or array, all nested 1229 * elements are skipped. This method is intended for use when the JSON token 1230 * stream contains unrecognized or unhandled values. 1231 */ 1232 public void skipValue() throws IOException { 1233 int count = 0; 1234 do { 1235 int p = peeked; 1236 if (p == PEEKED_NONE) { 1237 p = doPeek(); 1238 } 1239 1240 if (p == PEEKED_BEGIN_ARRAY) { 1241 push(JsonScope.EMPTY_ARRAY); 1242 count++; 1243 } else if (p == PEEKED_BEGIN_OBJECT) { 1244 push(JsonScope.EMPTY_OBJECT); 1245 count++; 1246 } else if (p == PEEKED_END_ARRAY) { 1247 stackSize--; 1248 count--; 1249 } else if (p == PEEKED_END_OBJECT) { 1250 stackSize--; 1251 count--; 1252 } else if (p == PEEKED_UNQUOTED_NAME || p == PEEKED_UNQUOTED) { 1253 skipUnquotedValue(); 1254 } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_SINGLE_QUOTED_NAME) { 1255 skipQuotedValue('\''); 1256 } else if (p == PEEKED_DOUBLE_QUOTED || p == PEEKED_DOUBLE_QUOTED_NAME) { 1257 skipQuotedValue('"'); 1258 } else if (p == PEEKED_NUMBER) { 1259 pos += peekedNumberLength; 1260 } 1261 peeked = PEEKED_NONE; 1262 } while (count != 0); 1263 1264 pathIndices[stackSize - 1]++; 1265 pathNames[stackSize - 1] = "null"; 1266 } 1267 1268 private void push(int newTop) { 1269 if (stackSize == stack.length) { 1270 int[] newStack = new int[stackSize * 2]; 1271 int[] newPathIndices = new int[stackSize * 2]; 1272 String[] newPathNames = new String[stackSize * 2]; 1273 System.arraycopy(stack, 0, newStack, 0, stackSize); 1274 System.arraycopy(pathIndices, 0, newPathIndices, 0, stackSize); 1275 System.arraycopy(pathNames, 0, newPathNames, 0, stackSize); 1276 stack = newStack; 1277 pathIndices = newPathIndices; 1278 pathNames = newPathNames; 1279 } 1280 stack[stackSize++] = newTop; 1281 } 1282 1283 /** 1284 * Returns true once {@code limit - pos >= minimum}. If the data is 1285 * exhausted before that many characters are available, this returns 1286 * false. 1287 */ 1288 private boolean fillBuffer(int minimum) throws IOException { 1289 char[] buffer = this.buffer; 1290 lineStart -= pos; 1291 if (limit != pos) { 1292 limit -= pos; 1293 System.arraycopy(buffer, pos, buffer, 0, limit); 1294 } else { 1295 limit = 0; 1296 } 1297 1298 pos = 0; 1299 int total; 1300 while ((total = in.read(buffer, limit, buffer.length - limit)) != -1) { 1301 limit += total; 1302 1303 // if this is the first read, consume an optional byte order mark (BOM) if it exists 1304 if (lineNumber == 0 && lineStart == 0 && limit > 0 && buffer[0] == '\ufeff') { 1305 pos++; 1306 lineStart++; 1307 minimum++; 1308 } 1309 1310 if (limit >= minimum) { 1311 return true; 1312 } 1313 } 1314 return false; 1315 } 1316 1317 private int getLineNumber() { 1318 return lineNumber + 1; 1319 } 1320 1321 private int getColumnNumber() { 1322 return pos - lineStart + 1; 1323 } 1324 1325 /** 1326 * Returns the next character in the stream that is neither whitespace nor a 1327 * part of a comment. When this returns, the returned character is always at 1328 * {@code buffer[pos-1]}; this means the caller can always push back the 1329 * returned character by decrementing {@code pos}. 1330 */ 1331 private int nextNonWhitespace(boolean throwOnEof) throws IOException { 1332 /* 1333 * This code uses ugly local variables 'p' and 'l' representing the 'pos' 1334 * and 'limit' fields respectively. Using locals rather than fields saves 1335 * a few field reads for each whitespace character in a pretty-printed 1336 * document, resulting in a 5% speedup. We need to flush 'p' to its field 1337 * before any (potentially indirect) call to fillBuffer() and reread both 1338 * 'p' and 'l' after any (potentially indirect) call to the same method. 1339 */ 1340 char[] buffer = this.buffer; 1341 int p = pos; 1342 int l = limit; 1343 while (true) { 1344 if (p == l) { 1345 pos = p; 1346 if (!fillBuffer(1)) { 1347 break; 1348 } 1349 p = pos; 1350 l = limit; 1351 } 1352 1353 int c = buffer[p++]; 1354 if (c == '\n') { 1355 lineNumber++; 1356 lineStart = p; 1357 continue; 1358 } else if (c == ' ' || c == '\r' || c == '\t') { 1359 continue; 1360 } 1361 1362 if (c == '/') { 1363 pos = p; 1364 if (p == l) { 1365 pos--; // push back '/' so it's still in the buffer when this method returns 1366 boolean charsLoaded = fillBuffer(2); 1367 pos++; // consume the '/' again 1368 if (!charsLoaded) { 1369 return c; 1370 } 1371 } 1372 1373 checkLenient(); 1374 char peek = buffer[pos]; 1375 switch (peek) { 1376 case '*': 1377 // skip a /* c-style comment */ 1378 pos++; 1379 if (!skipTo("*/")) { 1380 throw syntaxError("Unterminated comment"); 1381 } 1382 p = pos + 2; 1383 l = limit; 1384 continue; 1385 1386 case '/': 1387 // skip a // end-of-line comment 1388 pos++; 1389 skipToEndOfLine(); 1390 p = pos; 1391 l = limit; 1392 continue; 1393 1394 default: 1395 return c; 1396 } 1397 } else if (c == '#') { 1398 pos = p; 1399 /* 1400 * Skip a # hash end-of-line comment. The JSON RFC doesn't 1401 * specify this behaviour, but it's required to parse 1402 * existing documents. See http://b/2571423. 1403 */ 1404 checkLenient(); 1405 skipToEndOfLine(); 1406 p = pos; 1407 l = limit; 1408 } else { 1409 pos = p; 1410 return c; 1411 } 1412 } 1413 if (throwOnEof) { 1414 throw new EOFException("End of input" 1415 + " at line " + getLineNumber() + " column " + getColumnNumber()); 1416 } else { 1417 return -1; 1418 } 1419 } 1420 1421 private void checkLenient() throws IOException { 1422 if (!lenient) { 1423 throw syntaxError("Use JsonReader.setLenient(true) to accept malformed JSON"); 1424 } 1425 } 1426 1427 /** 1428 * Advances the position until after the next newline character. If the line 1429 * is terminated by "\r\n", the '\n' must be consumed as whitespace by the 1430 * caller. 1431 */ 1432 private void skipToEndOfLine() throws IOException { 1433 while (pos < limit || fillBuffer(1)) { 1434 char c = buffer[pos++]; 1435 if (c == '\n') { 1436 lineNumber++; 1437 lineStart = pos; 1438 break; 1439 } else if (c == '\r') { 1440 break; 1441 } 1442 } 1443 } 1444 1445 /** 1446 * @param toFind a string to search for. Must not contain a newline. 1447 */ 1448 private boolean skipTo(String toFind) throws IOException { 1449 outer: 1450 for (; pos + toFind.length() <= limit || fillBuffer(toFind.length()); pos++) { 1451 if (buffer[pos] == '\n') { 1452 lineNumber++; 1453 lineStart = pos + 1; 1454 continue; 1455 } 1456 for (int c = 0; c < toFind.length(); c++) { 1457 if (buffer[pos + c] != toFind.charAt(c)) { 1458 continue outer; 1459 } 1460 } 1461 return true; 1462 } 1463 return false; 1464 } 1465 1466 @Override public String toString() { 1467 return getClass().getSimpleName() 1468 + " at line " + getLineNumber() + " column " + getColumnNumber(); 1469 } 1470 1471 /** 1472 * Returns a <a href="http://goessner.net/articles/JsonPath/">JsonPath</a> to 1473 * the current location in the JSON value. 1474 */ 1475 public String getPath() { 1476 StringBuilder result = new StringBuilder().append('$'); 1477 for (int i = 0, size = stackSize; i < size; i++) { 1478 switch (stack[i]) { 1479 case JsonScope.EMPTY_ARRAY: 1480 case JsonScope.NONEMPTY_ARRAY: 1481 result.append('[').append(pathIndices[i]).append(']'); 1482 break; 1483 1484 case JsonScope.EMPTY_OBJECT: 1485 case JsonScope.DANGLING_NAME: 1486 case JsonScope.NONEMPTY_OBJECT: 1487 result.append('.'); 1488 if (pathNames[i] != null) { 1489 result.append(pathNames[i]); 1490 } 1491 break; 1492 1493 case JsonScope.NONEMPTY_DOCUMENT: 1494 case JsonScope.EMPTY_DOCUMENT: 1495 case JsonScope.CLOSED: 1496 break; 1497 } 1498 } 1499 return result.toString(); 1500 } 1501 1502 /** 1503 * Unescapes the character identified by the character or characters that 1504 * immediately follow a backslash. The backslash '\' should have already 1505 * been read. This supports both unicode escapes "u000A" and two-character 1506 * escapes "\n". 1507 * 1508 * @throws NumberFormatException if any unicode escape sequences are 1509 * malformed. 1510 */ 1511 private char readEscapeCharacter() throws IOException { 1512 if (pos == limit && !fillBuffer(1)) { 1513 throw syntaxError("Unterminated escape sequence"); 1514 } 1515 1516 char escaped = buffer[pos++]; 1517 switch (escaped) { 1518 case 'u': 1519 if (pos + 4 > limit && !fillBuffer(4)) { 1520 throw syntaxError("Unterminated escape sequence"); 1521 } 1522 // Equivalent to Integer.parseInt(stringPool.get(buffer, pos, 4), 16); 1523 char result = 0; 1524 for (int i = pos, end = i + 4; i < end; i++) { 1525 char c = buffer[i]; 1526 result <<= 4; 1527 if (c >= '0' && c <= '9') { 1528 result += (c - '0'); 1529 } else if (c >= 'a' && c <= 'f') { 1530 result += (c - 'a' + 10); 1531 } else if (c >= 'A' && c <= 'F') { 1532 result += (c - 'A' + 10); 1533 } else { 1534 throw new NumberFormatException("\\u" + new String(buffer, pos, 4)); 1535 } 1536 } 1537 pos += 4; 1538 return result; 1539 1540 case 't': 1541 return '\t'; 1542 1543 case 'b': 1544 return '\b'; 1545 1546 case 'n': 1547 return '\n'; 1548 1549 case 'r': 1550 return '\r'; 1551 1552 case 'f': 1553 return '\f'; 1554 1555 case '\n': 1556 lineNumber++; 1557 lineStart = pos; 1558 // fall-through 1559 1560 case '\'': 1561 case '"': 1562 case '\\': 1563 default: 1564 return escaped; 1565 } 1566 } 1567 1568 /** 1569 * Throws a new IO exception with the given message and a context snippet 1570 * with this reader's content. 1571 */ 1572 private IOException syntaxError(String message) throws IOException { 1573 throw new MalformedJsonException(message 1574 + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath()); 1575 } 1576 1577 /** 1578 * Consumes the non-execute prefix if it exists. 1579 */ 1580 private void consumeNonExecutePrefix() throws IOException { 1581 // fast forward through the leading whitespace 1582 nextNonWhitespace(true); 1583 pos--; 1584 1585 if (pos + NON_EXECUTE_PREFIX.length > limit && !fillBuffer(NON_EXECUTE_PREFIX.length)) { 1586 return; 1587 } 1588 1589 for (int i = 0; i < NON_EXECUTE_PREFIX.length; i++) { 1590 if (buffer[pos + i] != NON_EXECUTE_PREFIX[i]) { 1591 return; // not a security token! 1592 } 1593 } 1594 1595 // we consumed a security token! 1596 pos += NON_EXECUTE_PREFIX.length; 1597 } 1598 1599 static { 1600 JsonReaderInternalAccess.INSTANCE = new JsonReaderInternalAccess() { 1601 @Override public void promoteNameToValue(JsonReader reader) throws IOException { 1602 if (reader instanceof JsonTreeReader) { 1603 ((JsonTreeReader)reader).promoteNameToValue(); 1604 return; 1605 } 1606 int p = reader.peeked; 1607 if (p == PEEKED_NONE) { 1608 p = reader.doPeek(); 1609 } 1610 if (p == PEEKED_DOUBLE_QUOTED_NAME) { 1611 reader.peeked = PEEKED_DOUBLE_QUOTED; 1612 } else if (p == PEEKED_SINGLE_QUOTED_NAME) { 1613 reader.peeked = PEEKED_SINGLE_QUOTED; 1614 } else if (p == PEEKED_UNQUOTED_NAME) { 1615 reader.peeked = PEEKED_UNQUOTED; 1616 } else { 1617 throw new IllegalStateException("Expected a name but was " + reader.peek() + " " 1618 + " at line " + reader.getLineNumber() + " column " + reader.getColumnNumber() 1619 + " path " + reader.getPath()); 1620 } 1621 } 1622 }; 1623 } 1624 }