60d0c17f0afc58605784ca5cdb696e70592c2aa2
[unical.git] / gson / com / google / gson / stream / JsonReader.java
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 * Creates a new instance that reads a JSON-encoded stream from {@code in}.
277 */
278 public JsonReader(Reader in) {
279 if (in == null) {
280 throw new NullPointerException("in == null");
281 }
282 this.in = in;
283 }
284
285 /**
286 * Configure this parser to be be liberal in what it accepts. By default,
287 * this parser is strict and only accepts JSON as specified by <a
288 * href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the
289 * parser to lenient causes it to ignore the following syntax errors:
290 *
291 * <ul>
292 * <li>Streams that start with the <a href="#nonexecuteprefix">non-execute
293 * prefix</a>, <code>")]}'\n"</code>.
294 * <li>Streams that include multiple top-level values. With strict parsing,
295 * each stream must contain exactly one top-level value.
296 * <li>Top-level values of any type. With strict parsing, the top-level
297 * value must be an object or an array.
298 * <li>Numbers may be {@link Double#isNaN() NaNs} or {@link
299 * Double#isInfinite() infinities}.
300 * <li>End of line comments starting with {@code //} or {@code #} and
301 * ending with a newline character.
302 * <li>C-style comments starting with {@code /*} and ending with
303 * {@code *}{@code /}. Such comments may not be nested.
304 * <li>Names that are unquoted or {@code 'single quoted'}.
305 * <li>Strings that are unquoted or {@code 'single quoted'}.
306 * <li>Array elements separated by {@code ;} instead of {@code ,}.
307 * <li>Unnecessary array separators. These are interpreted as if null
308 * was the omitted value.
309 * <li>Names and values separated by {@code =} or {@code =>} instead of
310 * {@code :}.
311 * <li>Name/value pairs separated by {@code ;} instead of {@code ,}.
312 * </ul>
313 */
314 public final void setLenient(boolean lenient) {
315 this.lenient = lenient;
316 }
317
318 /**
319 * Returns true if this parser is liberal in what it accepts.
320 */
321 public final boolean isLenient() {
322 return lenient;
323 }
324
325 /**
326 * Consumes the next token from the JSON stream and asserts that it is the
327 * beginning of a new array.
328 */
329 public void beginArray() throws IOException {
330 int p = peeked;
331 if (p == PEEKED_NONE) {
332 p = doPeek();
333 }
334 if (p == PEEKED_BEGIN_ARRAY) {
335 push(JsonScope.EMPTY_ARRAY);
336 peeked = PEEKED_NONE;
337 } else {
338 throw new IllegalStateException("Expected BEGIN_ARRAY but was " + peek()
339 + " at line " + getLineNumber() + " column " + getColumnNumber());
340 }
341 }
342
343 /**
344 * Consumes the next token from the JSON stream and asserts that it is the
345 * end of the current array.
346 */
347 public void endArray() throws IOException {
348 int p = peeked;
349 if (p == PEEKED_NONE) {
350 p = doPeek();
351 }
352 if (p == PEEKED_END_ARRAY) {
353 stackSize--;
354 peeked = PEEKED_NONE;
355 } else {
356 throw new IllegalStateException("Expected END_ARRAY but was " + peek()
357 + " at line " + getLineNumber() + " column " + getColumnNumber());
358 }
359 }
360
361 /**
362 * Consumes the next token from the JSON stream and asserts that it is the
363 * beginning of a new object.
364 */
365 public void beginObject() throws IOException {
366 int p = peeked;
367 if (p == PEEKED_NONE) {
368 p = doPeek();
369 }
370 if (p == PEEKED_BEGIN_OBJECT) {
371 push(JsonScope.EMPTY_OBJECT);
372 peeked = PEEKED_NONE;
373 } else {
374 throw new IllegalStateException("Expected BEGIN_OBJECT but was " + peek()
375 + " at line " + getLineNumber() + " column " + getColumnNumber());
376 }
377 }
378
379 /**
380 * Consumes the next token from the JSON stream and asserts that it is the
381 * end of the current object.
382 */
383 public void endObject() throws IOException {
384 int p = peeked;
385 if (p == PEEKED_NONE) {
386 p = doPeek();
387 }
388 if (p == PEEKED_END_OBJECT) {
389 stackSize--;
390 peeked = PEEKED_NONE;
391 } else {
392 throw new IllegalStateException("Expected END_OBJECT but was " + peek()
393 + " at line " + getLineNumber() + " column " + getColumnNumber());
394 }
395 }
396
397 /**
398 * Returns true if the current array or object has another element.
399 */
400 public boolean hasNext() throws IOException {
401 int p = peeked;
402 if (p == PEEKED_NONE) {
403 p = doPeek();
404 }
405 return p != PEEKED_END_OBJECT && p != PEEKED_END_ARRAY;
406 }
407
408 /**
409 * Returns the type of the next token without consuming it.
410 */
411 public JsonToken peek() throws IOException {
412 int p = peeked;
413 if (p == PEEKED_NONE) {
414 p = doPeek();
415 }
416
417 switch (p) {
418 case PEEKED_BEGIN_OBJECT:
419 return JsonToken.BEGIN_OBJECT;
420 case PEEKED_END_OBJECT:
421 return JsonToken.END_OBJECT;
422 case PEEKED_BEGIN_ARRAY:
423 return JsonToken.BEGIN_ARRAY;
424 case PEEKED_END_ARRAY:
425 return JsonToken.END_ARRAY;
426 case PEEKED_SINGLE_QUOTED_NAME:
427 case PEEKED_DOUBLE_QUOTED_NAME:
428 case PEEKED_UNQUOTED_NAME:
429 return JsonToken.NAME;
430 case PEEKED_TRUE:
431 case PEEKED_FALSE:
432 return JsonToken.BOOLEAN;
433 case PEEKED_NULL:
434 return JsonToken.NULL;
435 case PEEKED_SINGLE_QUOTED:
436 case PEEKED_DOUBLE_QUOTED:
437 case PEEKED_UNQUOTED:
438 case PEEKED_BUFFERED:
439 return JsonToken.STRING;
440 case PEEKED_LONG:
441 case PEEKED_NUMBER:
442 return JsonToken.NUMBER;
443 case PEEKED_EOF:
444 return JsonToken.END_DOCUMENT;
445 default:
446 throw new AssertionError();
447 }
448 }
449
450 private int doPeek() throws IOException {
451 int peekStack = stack[stackSize - 1];
452 if (peekStack == JsonScope.EMPTY_ARRAY) {
453 stack[stackSize - 1] = JsonScope.NONEMPTY_ARRAY;
454 } else if (peekStack == JsonScope.NONEMPTY_ARRAY) {
455 // Look for a comma before the next element.
456 int c = nextNonWhitespace(true);
457 switch (c) {
458 case ']':
459 return peeked = PEEKED_END_ARRAY;
460 case ';':
461 checkLenient(); // fall-through
462 case ',':
463 break;
464 default:
465 throw syntaxError("Unterminated array");
466 }
467 } else if (peekStack == JsonScope.EMPTY_OBJECT || peekStack == JsonScope.NONEMPTY_OBJECT) {
468 stack[stackSize - 1] = JsonScope.DANGLING_NAME;
469 // Look for a comma before the next element.
470 if (peekStack == JsonScope.NONEMPTY_OBJECT) {
471 int c = nextNonWhitespace(true);
472 switch (c) {
473 case '}':
474 return peeked = PEEKED_END_OBJECT;
475 case ';':
476 checkLenient(); // fall-through
477 case ',':
478 break;
479 default:
480 throw syntaxError("Unterminated object");
481 }
482 }
483 int c = nextNonWhitespace(true);
484 switch (c) {
485 case '"':
486 return peeked = PEEKED_DOUBLE_QUOTED_NAME;
487 case '\'':
488 checkLenient();
489 return peeked = PEEKED_SINGLE_QUOTED_NAME;
490 case '}':
491 if (peekStack != JsonScope.NONEMPTY_OBJECT) {
492 return peeked = PEEKED_END_OBJECT;
493 } else {
494 throw syntaxError("Expected name");
495 }
496 default:
497 checkLenient();
498 pos--; // Don't consume the first character in an unquoted string.
499 if (isLiteral((char) c)) {
500 return peeked = PEEKED_UNQUOTED_NAME;
501 } else {
502 throw syntaxError("Expected name");
503 }
504 }
505 } else if (peekStack == JsonScope.DANGLING_NAME) {
506 stack[stackSize - 1] = JsonScope.NONEMPTY_OBJECT;
507 // Look for a colon before the value.
508 int c = nextNonWhitespace(true);
509 switch (c) {
510 case ':':
511 break;
512 case '=':
513 checkLenient();
514 if ((pos < limit || fillBuffer(1)) && buffer[pos] == '>') {
515 pos++;
516 }
517 break;
518 default:
519 throw syntaxError("Expected ':'");
520 }
521 } else if (peekStack == JsonScope.EMPTY_DOCUMENT) {
522 if (lenient) {
523 consumeNonExecutePrefix();
524 }
525 stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;
526 } else if (peekStack == JsonScope.NONEMPTY_DOCUMENT) {
527 int c = nextNonWhitespace(false);
528 if (c == -1) {
529 return peeked = PEEKED_EOF;
530 } else {
531 checkLenient();
532 pos--;
533 }
534 } else if (peekStack == JsonScope.CLOSED) {
535 throw new IllegalStateException("JsonReader is closed");
536 }
537
538 int c = nextNonWhitespace(true);
539 switch (c) {
540 case ']':
541 if (peekStack == JsonScope.EMPTY_ARRAY) {
542 return peeked = PEEKED_END_ARRAY;
543 }
544 // fall-through to handle ",]"
545 case ';':
546 case ',':
547 // In lenient mode, a 0-length literal in an array means 'null'.
548 if (peekStack == JsonScope.EMPTY_ARRAY || peekStack == JsonScope.NONEMPTY_ARRAY) {
549 checkLenient();
550 pos--;
551 return peeked = PEEKED_NULL;
552 } else {
553 throw syntaxError("Unexpected value");
554 }
555 case '\'':
556 checkLenient();
557 return peeked = PEEKED_SINGLE_QUOTED;
558 case '"':
559 if (stackSize == 1) {
560 checkLenient();
561 }
562 return peeked = PEEKED_DOUBLE_QUOTED;
563 case '[':
564 return peeked = PEEKED_BEGIN_ARRAY;
565 case '{':
566 return peeked = PEEKED_BEGIN_OBJECT;
567 default:
568 pos--; // Don't consume the first character in a literal value.
569 }
570
571 if (stackSize == 1) {
572 checkLenient(); // Top-level value isn't an array or an object.
573 }
574
575 int result = peekKeyword();
576 if (result != PEEKED_NONE) {
577 return result;
578 }
579
580 result = peekNumber();
581 if (result != PEEKED_NONE) {
582 return result;
583 }
584
585 if (!isLiteral(buffer[pos])) {
586 throw syntaxError("Expected value");
587 }
588
589 checkLenient();
590 return peeked = PEEKED_UNQUOTED;
591 }
592
593 private int peekKeyword() throws IOException {
594 // Figure out which keyword we're matching against by its first character.
595 char c = buffer[pos];
596 String keyword;
597 String keywordUpper;
598 int peeking;
599 if (c == 't' || c == 'T') {
600 keyword = "true";
601 keywordUpper = "TRUE";
602 peeking = PEEKED_TRUE;
603 } else if (c == 'f' || c == 'F') {
604 keyword = "false";
605 keywordUpper = "FALSE";
606 peeking = PEEKED_FALSE;
607 } else if (c == 'n' || c == 'N') {
608 keyword = "null";
609 keywordUpper = "NULL";
610 peeking = PEEKED_NULL;
611 } else {
612 return PEEKED_NONE;
613 }
614
615 // Confirm that chars [1..length) match the keyword.
616 int length = keyword.length();
617 for (int i = 1; i < length; i++) {
618 if (pos + i >= limit && !fillBuffer(i + 1)) {
619 return PEEKED_NONE;
620 }
621 c = buffer[pos + i];
622 if (c != keyword.charAt(i) && c != keywordUpper.charAt(i)) {
623 return PEEKED_NONE;
624 }
625 }
626
627 if ((pos + length < limit || fillBuffer(length + 1))
628 && isLiteral(buffer[pos + length])) {
629 return PEEKED_NONE; // Don't match trues, falsey or nullsoft!
630 }
631
632 // We've found the keyword followed either by EOF or by a non-literal character.
633 pos += length;
634 return peeked = peeking;
635 }
636
637 private int peekNumber() throws IOException {
638 // Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field access.
639 char[] buffer = this.buffer;
640 int p = pos;
641 int l = limit;
642
643 long value = 0; // Negative to accommodate Long.MIN_VALUE more easily.
644 boolean negative = false;
645 boolean fitsInLong = true;
646 int last = NUMBER_CHAR_NONE;
647
648 int i = 0;
649
650 charactersOfNumber:
651 for (; true; i++) {
652 if (p + i == l) {
653 if (i == buffer.length) {
654 // Though this looks like a well-formed number, it's too long to continue reading. Give up
655 // and let the application handle this as an unquoted literal.
656 return PEEKED_NONE;
657 }
658 if (!fillBuffer(i + 1)) {
659 break;
660 }
661 p = pos;
662 l = limit;
663 }
664
665 char c = buffer[p + i];
666 switch (c) {
667 case '-':
668 if (last == NUMBER_CHAR_NONE) {
669 negative = true;
670 last = NUMBER_CHAR_SIGN;
671 continue;
672 } else if (last == NUMBER_CHAR_EXP_E) {
673 last = NUMBER_CHAR_EXP_SIGN;
674 continue;
675 }
676 return PEEKED_NONE;
677
678 case '+':
679 if (last == NUMBER_CHAR_EXP_E) {
680 last = NUMBER_CHAR_EXP_SIGN;
681 continue;
682 }
683 return PEEKED_NONE;
684
685 case 'e':
686 case 'E':
687 if (last == NUMBER_CHAR_DIGIT || last == NUMBER_CHAR_FRACTION_DIGIT) {
688 last = NUMBER_CHAR_EXP_E;
689 continue;
690 }
691 return PEEKED_NONE;
692
693 case '.':
694 if (last == NUMBER_CHAR_DIGIT) {
695 last = NUMBER_CHAR_DECIMAL;
696 continue;
697 }
698 return PEEKED_NONE;
699
700 default:
701 if (c < '0' || c > '9') {
702 if (!isLiteral(c)) {
703 break charactersOfNumber;
704 }
705 return PEEKED_NONE;
706 }
707 if (last == NUMBER_CHAR_SIGN || last == NUMBER_CHAR_NONE) {
708 value = -(c - '0');
709 last = NUMBER_CHAR_DIGIT;
710 } else if (last == NUMBER_CHAR_DIGIT) {
711 if (value == 0) {
712 return PEEKED_NONE; // Leading '0' prefix is not allowed (since it could be octal).
713 }
714 long newValue = value * 10 - (c - '0');
715 fitsInLong &= value > MIN_INCOMPLETE_INTEGER
716 || (value == MIN_INCOMPLETE_INTEGER && newValue < value);
717 value = newValue;
718 } else if (last == NUMBER_CHAR_DECIMAL) {
719 last = NUMBER_CHAR_FRACTION_DIGIT;
720 } else if (last == NUMBER_CHAR_EXP_E || last == NUMBER_CHAR_EXP_SIGN) {
721 last = NUMBER_CHAR_EXP_DIGIT;
722 }
723 }
724 }
725
726 // We've read a complete number. Decide if it's a PEEKED_LONG or a PEEKED_NUMBER.
727 if (last == NUMBER_CHAR_DIGIT && fitsInLong && (value != Long.MIN_VALUE || negative)) {
728 peekedLong = negative ? value : -value;
729 pos += i;
730 return peeked = PEEKED_LONG;
731 } else if (last == NUMBER_CHAR_DIGIT || last == NUMBER_CHAR_FRACTION_DIGIT
732 || last == NUMBER_CHAR_EXP_DIGIT) {
733 peekedNumberLength = i;
734 return peeked = PEEKED_NUMBER;
735 } else {
736 return PEEKED_NONE;
737 }
738 }
739
740 private boolean isLiteral(char c) throws IOException {
741 switch (c) {
742 case '/':
743 case '\\':
744 case ';':
745 case '#':
746 case '=':
747 checkLenient(); // fall-through
748 case '{':
749 case '}':
750 case '[':
751 case ']':
752 case ':':
753 case ',':
754 case ' ':
755 case '\t':
756 case '\f':
757 case '\r':
758 case '\n':
759 return false;
760 default:
761 return true;
762 }
763 }
764
765 /**
766 * Returns the next token, a {@link com.google.gson.stream.JsonToken#NAME property name}, and
767 * consumes it.
768 *
769 * @throws java.io.IOException if the next token in the stream is not a property
770 * name.
771 */
772 public String nextName() throws IOException {
773 int p = peeked;
774 if (p == PEEKED_NONE) {
775 p = doPeek();
776 }
777 String result;
778 if (p == PEEKED_UNQUOTED_NAME) {
779 result = nextUnquotedValue();
780 } else if (p == PEEKED_SINGLE_QUOTED_NAME) {
781 result = nextQuotedValue('\'');
782 } else if (p == PEEKED_DOUBLE_QUOTED_NAME) {
783 result = nextQuotedValue('"');
784 } else {
785 throw new IllegalStateException("Expected a name but was " + peek()
786 + " at line " + getLineNumber() + " column " + getColumnNumber());
787 }
788 peeked = PEEKED_NONE;
789 return result;
790 }
791
792 /**
793 * Returns the {@link com.google.gson.stream.JsonToken#STRING string} value of the next token,
794 * consuming it. If the next token is a number, this method will return its
795 * string form.
796 *
797 * @throws IllegalStateException if the next token is not a string or if
798 * this reader is closed.
799 */
800 public String nextString() throws IOException {
801 int p = peeked;
802 if (p == PEEKED_NONE) {
803 p = doPeek();
804 }
805 String result;
806 if (p == PEEKED_UNQUOTED) {
807 result = nextUnquotedValue();
808 } else if (p == PEEKED_SINGLE_QUOTED) {
809 result = nextQuotedValue('\'');
810 } else if (p == PEEKED_DOUBLE_QUOTED) {
811 result = nextQuotedValue('"');
812 } else if (p == PEEKED_BUFFERED) {
813 result = peekedString;
814 peekedString = null;
815 } else if (p == PEEKED_LONG) {
816 result = Long.toString(peekedLong);
817 } else if (p == PEEKED_NUMBER) {
818 result = new String(buffer, pos, peekedNumberLength);
819 pos += peekedNumberLength;
820 } else {
821 throw new IllegalStateException("Expected a string but was " + peek()
822 + " at line " + getLineNumber() + " column " + getColumnNumber());
823 }
824 peeked = PEEKED_NONE;
825 return result;
826 }
827
828 /**
829 * Returns the {@link com.google.gson.stream.JsonToken#BOOLEAN boolean} value of the next token,
830 * consuming it.
831 *
832 * @throws IllegalStateException if the next token is not a boolean or if
833 * this reader is closed.
834 */
835 public boolean nextBoolean() throws IOException {
836 int p = peeked;
837 if (p == PEEKED_NONE) {
838 p = doPeek();
839 }
840 if (p == PEEKED_TRUE) {
841 peeked = PEEKED_NONE;
842 return true;
843 } else if (p == PEEKED_FALSE) {
844 peeked = PEEKED_NONE;
845 return false;
846 }
847 throw new IllegalStateException("Expected a boolean but was " + peek()
848 + " at line " + getLineNumber() + " column " + getColumnNumber());
849 }
850
851 /**
852 * Consumes the next token from the JSON stream and asserts that it is a
853 * literal null.
854 *
855 * @throws IllegalStateException if the next token is not null or if this
856 * reader is closed.
857 */
858 public void nextNull() throws IOException {
859 int p = peeked;
860 if (p == PEEKED_NONE) {
861 p = doPeek();
862 }
863 if (p == PEEKED_NULL) {
864 peeked = PEEKED_NONE;
865 } else {
866 throw new IllegalStateException("Expected null but was " + peek()
867 + " at line " + getLineNumber() + " column " + getColumnNumber());
868 }
869 }
870
871 /**
872 * Returns the {@link com.google.gson.stream.JsonToken#NUMBER double} value of the next token,
873 * consuming it. If the next token is a string, this method will attempt to
874 * parse it as a double using {@link Double#parseDouble(String)}.
875 *
876 * @throws IllegalStateException if the next token is not a literal value.
877 * @throws NumberFormatException if the next literal value cannot be parsed
878 * as a double, or is non-finite.
879 */
880 public double nextDouble() throws IOException {
881 int p = peeked;
882 if (p == PEEKED_NONE) {
883 p = doPeek();
884 }
885
886 if (p == PEEKED_LONG) {
887 peeked = PEEKED_NONE;
888 return (double) peekedLong;
889 }
890
891 if (p == PEEKED_NUMBER) {
892 peekedString = new String(buffer, pos, peekedNumberLength);
893 pos += peekedNumberLength;
894 } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) {
895 peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
896 } else if (p == PEEKED_UNQUOTED) {
897 peekedString = nextUnquotedValue();
898 } else if (p != PEEKED_BUFFERED) {
899 throw new IllegalStateException("Expected a double but was " + peek()
900 + " at line " + getLineNumber() + " column " + getColumnNumber());
901 }
902
903 peeked = PEEKED_BUFFERED;
904 double result = Double.parseDouble(peekedString); // don't catch this NumberFormatException.
905 if (!lenient && (Double.isNaN(result) || Double.isInfinite(result))) {
906 throw new MalformedJsonException("JSON forbids NaN and infinities: " + result
907 + " at line " + getLineNumber() + " column " + getColumnNumber());
908 }
909 peekedString = null;
910 peeked = PEEKED_NONE;
911 return result;
912 }
913
914 /**
915 * Returns the {@link com.google.gson.stream.JsonToken#NUMBER long} value of the next token,
916 * consuming it. If the next token is a string, this method will attempt to
917 * parse it as a long. If the next token's numeric value cannot be exactly
918 * represented by a Java {@code long}, this method throws.
919 *
920 * @throws IllegalStateException if the next token is not a literal value.
921 * @throws NumberFormatException if the next literal value cannot be parsed
922 * as a number, or exactly represented as a long.
923 */
924 public long nextLong() throws IOException {
925 int p = peeked;
926 if (p == PEEKED_NONE) {
927 p = doPeek();
928 }
929
930 if (p == PEEKED_LONG) {
931 peeked = PEEKED_NONE;
932 return peekedLong;
933 }
934
935 if (p == PEEKED_NUMBER) {
936 peekedString = new String(buffer, pos, peekedNumberLength);
937 pos += peekedNumberLength;
938 } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) {
939 peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
940 try {
941 long result = Long.parseLong(peekedString);
942 peeked = PEEKED_NONE;
943 return result;
944 } catch (NumberFormatException ignored) {
945 // Fall back to parse as a double below.
946 }
947 } else {
948 throw new IllegalStateException("Expected a long but was " + peek()
949 + " at line " + getLineNumber() + " column " + getColumnNumber());
950 }
951
952 peeked = PEEKED_BUFFERED;
953 double asDouble = Double.parseDouble(peekedString); // don't catch this NumberFormatException.
954 long result = (long) asDouble;
955 if (result != asDouble) { // Make sure no precision was lost casting to 'long'.
956 throw new NumberFormatException("Expected a long but was " + peekedString
957 + " at line " + getLineNumber() + " column " + getColumnNumber());
958 }
959 peekedString = null;
960 peeked = PEEKED_NONE;
961 return result;
962 }
963
964 /**
965 * Returns the string up to but not including {@code quote}, unescaping any
966 * character escape sequences encountered along the way. The opening quote
967 * should have already been read. This consumes the closing quote, but does
968 * not include it in the returned string.
969 *
970 * @param quote either ' or ".
971 * @throws NumberFormatException if any unicode escape sequences are
972 * malformed.
973 */
974 private String nextQuotedValue(char quote) throws IOException {
975 // Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field access.
976 char[] buffer = this.buffer;
977 StringBuilder builder = new StringBuilder();
978 while (true) {
979 int p = pos;
980 int l = limit;
981 /* the index of the first character not yet appended to the builder. */
982 int start = p;
983 while (p < l) {
984 int c = buffer[p++];
985
986 if (c == quote) {
987 pos = p;
988 builder.append(buffer, start, p - start - 1);
989 return builder.toString();
990 } else if (c == '\\') {
991 pos = p;
992 builder.append(buffer, start, p - start - 1);
993 builder.append(readEscapeCharacter());
994 p = pos;
995 l = limit;
996 start = p;
997 } else if (c == '\n') {
998 lineNumber++;
999 lineStart = p;
1000 }
1001 }
1002
1003 builder.append(buffer, start, p - start);
1004 pos = p;
1005 if (!fillBuffer(1)) {
1006 throw syntaxError("Unterminated string");
1007 }
1008 }
1009 }
1010
1011 /**
1012 * Returns an unquoted value as a string.
1013 */
1014 @SuppressWarnings("fallthrough")
1015 private String nextUnquotedValue() throws IOException {
1016 StringBuilder builder = null;
1017 int i = 0;
1018
1019 findNonLiteralCharacter:
1020 while (true) {
1021 for (; pos + i < limit; i++) {
1022 switch (buffer[pos + i]) {
1023 case '/':
1024 case '\\':
1025 case ';':
1026 case '#':
1027 case '=':
1028 checkLenient(); // fall-through
1029 case '{':
1030 case '}':
1031 case '[':
1032 case ']':
1033 case ':':
1034 case ',':
1035 case ' ':
1036 case '\t':
1037 case '\f':
1038 case '\r':
1039 case '\n':
1040 break findNonLiteralCharacter;
1041 }
1042 }
1043
1044 // Attempt to load the entire literal into the buffer at once.
1045 if (i < buffer.length) {
1046 if (fillBuffer(i + 1)) {
1047 continue;
1048 } else {
1049 break;
1050 }
1051 }
1052
1053 // use a StringBuilder when the value is too long. This is too long to be a number!
1054 if (builder == null) {
1055 builder = new StringBuilder();
1056 }
1057 builder.append(buffer, pos, i);
1058 pos += i;
1059 i = 0;
1060 if (!fillBuffer(1)) {
1061 break;
1062 }
1063 }
1064
1065 String result;
1066 if (builder == null) {
1067 result = new String(buffer, pos, i);
1068 } else {
1069 builder.append(buffer, pos, i);
1070 result = builder.toString();
1071 }
1072 pos += i;
1073 return result;
1074 }
1075
1076 private void skipQuotedValue(char quote) throws IOException {
1077 // Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field access.
1078 char[] buffer = this.buffer;
1079 do {
1080 int p = pos;
1081 int l = limit;
1082 /* the index of the first character not yet appended to the builder. */
1083 while (p < l) {
1084 int c = buffer[p++];
1085 if (c == quote) {
1086 pos = p;
1087 return;
1088 } else if (c == '\\') {
1089 pos = p;
1090 readEscapeCharacter();
1091 p = pos;
1092 l = limit;
1093 } else if (c == '\n') {
1094 lineNumber++;
1095 lineStart = p;
1096 }
1097 }
1098 pos = p;
1099 } while (fillBuffer(1));
1100 throw syntaxError("Unterminated string");
1101 }
1102
1103 private void skipUnquotedValue() throws IOException {
1104 do {
1105 int i = 0;
1106 for (; pos + i < limit; i++) {
1107 switch (buffer[pos + i]) {
1108 case '/':
1109 case '\\':
1110 case ';':
1111 case '#':
1112 case '=':
1113 checkLenient(); // fall-through
1114 case '{':
1115 case '}':
1116 case '[':
1117 case ']':
1118 case ':':
1119 case ',':
1120 case ' ':
1121 case '\t':
1122 case '\f':
1123 case '\r':
1124 case '\n':
1125 pos += i;
1126 return;
1127 }
1128 }
1129 pos += i;
1130 } while (fillBuffer(1));
1131 }
1132
1133 /**
1134 * Returns the {@link com.google.gson.stream.JsonToken#NUMBER int} value of the next token,
1135 * consuming it. If the next token is a string, this method will attempt to
1136 * parse it as an int. If the next token's numeric value cannot be exactly
1137 * represented by a Java {@code int}, this method throws.
1138 *
1139 * @throws IllegalStateException if the next token is not a literal value.
1140 * @throws NumberFormatException if the next literal value cannot be parsed
1141 * as a number, or exactly represented as an int.
1142 */
1143 public int nextInt() throws IOException {
1144 int p = peeked;
1145 if (p == PEEKED_NONE) {
1146 p = doPeek();
1147 }
1148
1149 int result;
1150 if (p == PEEKED_LONG) {
1151 result = (int) peekedLong;
1152 if (peekedLong != result) { // Make sure no precision was lost casting to 'int'.
1153 throw new NumberFormatException("Expected an int but was " + peekedLong
1154 + " at line " + getLineNumber() + " column " + getColumnNumber());
1155 }
1156 peeked = PEEKED_NONE;
1157 return result;
1158 }
1159
1160 if (p == PEEKED_NUMBER) {
1161 peekedString = new String(buffer, pos, peekedNumberLength);
1162 pos += peekedNumberLength;
1163 } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) {
1164 peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
1165 try {
1166 result = Integer.parseInt(peekedString);
1167 peeked = PEEKED_NONE;
1168 return result;
1169 } catch (NumberFormatException ignored) {
1170 // Fall back to parse as a double below.
1171 }
1172 } else {
1173 throw new IllegalStateException("Expected an int but was " + peek()
1174 + " at line " + getLineNumber() + " column " + getColumnNumber());
1175 }
1176
1177 peeked = PEEKED_BUFFERED;
1178 double asDouble = Double.parseDouble(peekedString); // don't catch this NumberFormatException.
1179 result = (int) asDouble;
1180 if (result != asDouble) { // Make sure no precision was lost casting to 'int'.
1181 throw new NumberFormatException("Expected an int but was " + peekedString
1182 + " at line " + getLineNumber() + " column " + getColumnNumber());
1183 }
1184 peekedString = null;
1185 peeked = PEEKED_NONE;
1186 return result;
1187 }
1188
1189 /**
1190 * Closes this JSON reader and the underlying {@link java.io.Reader}.
1191 */
1192 public void close() throws IOException {
1193 peeked = PEEKED_NONE;
1194 stack[0] = JsonScope.CLOSED;
1195 stackSize = 1;
1196 in.close();
1197 }
1198
1199 /**
1200 * Skips the next value recursively. If it is an object or array, all nested
1201 * elements are skipped. This method is intended for use when the JSON token
1202 * stream contains unrecognized or unhandled values.
1203 */
1204 public void skipValue() throws IOException {
1205 int count = 0;
1206 do {
1207 int p = peeked;
1208 if (p == PEEKED_NONE) {
1209 p = doPeek();
1210 }
1211
1212 if (p == PEEKED_BEGIN_ARRAY) {
1213 push(JsonScope.EMPTY_ARRAY);
1214 count++;
1215 } else if (p == PEEKED_BEGIN_OBJECT) {
1216 push(JsonScope.EMPTY_OBJECT);
1217 count++;
1218 } else if (p == PEEKED_END_ARRAY) {
1219 stackSize--;
1220 count--;
1221 } else if (p == PEEKED_END_OBJECT) {
1222 stackSize--;
1223 count--;
1224 } else if (p == PEEKED_UNQUOTED_NAME || p == PEEKED_UNQUOTED) {
1225 skipUnquotedValue();
1226 } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_SINGLE_QUOTED_NAME) {
1227 skipQuotedValue('\'');
1228 } else if (p == PEEKED_DOUBLE_QUOTED || p == PEEKED_DOUBLE_QUOTED_NAME) {
1229 skipQuotedValue('"');
1230 } else if (p == PEEKED_NUMBER) {
1231 pos += peekedNumberLength;
1232 }
1233 peeked = PEEKED_NONE;
1234 } while (count != 0);
1235 }
1236
1237 private void push(int newTop) {
1238 if (stackSize == stack.length) {
1239 int[] newStack = new int[stackSize * 2];
1240 System.arraycopy(stack, 0, newStack, 0, stackSize);
1241 stack = newStack;
1242 }
1243 stack[stackSize++] = newTop;
1244 }
1245
1246 /**
1247 * Returns true once {@code limit - pos >= minimum}. If the data is
1248 * exhausted before that many characters are available, this returns
1249 * false.
1250 */
1251 private boolean fillBuffer(int minimum) throws IOException {
1252 char[] buffer = this.buffer;
1253 lineStart -= pos;
1254 if (limit != pos) {
1255 limit -= pos;
1256 System.arraycopy(buffer, pos, buffer, 0, limit);
1257 } else {
1258 limit = 0;
1259 }
1260
1261 pos = 0;
1262 int total;
1263 while ((total = in.read(buffer, limit, buffer.length - limit)) != -1) {
1264 limit += total;
1265
1266 // if this is the first read, consume an optional byte order mark (BOM) if it exists
1267 if (lineNumber == 0 && lineStart == 0 && limit > 0 && buffer[0] == '\ufeff') {
1268 pos++;
1269 lineStart++;
1270 minimum++;
1271 }
1272
1273 if (limit >= minimum) {
1274 return true;
1275 }
1276 }
1277 return false;
1278 }
1279
1280 private int getLineNumber() {
1281 return lineNumber + 1;
1282 }
1283
1284 private int getColumnNumber() {
1285 return pos - lineStart + 1;
1286 }
1287
1288 /**
1289 * Returns the next character in the stream that is neither whitespace nor a
1290 * part of a comment. When this returns, the returned character is always at
1291 * {@code buffer[pos-1]}; this means the caller can always push back the
1292 * returned character by decrementing {@code pos}.
1293 */
1294 private int nextNonWhitespace(boolean throwOnEof) throws IOException {
1295 /*
1296 * This code uses ugly local variables 'p' and 'l' representing the 'pos'
1297 * and 'limit' fields respectively. Using locals rather than fields saves
1298 * a few field reads for each whitespace character in a pretty-printed
1299 * document, resulting in a 5% speedup. We need to flush 'p' to its field
1300 * before any (potentially indirect) call to fillBuffer() and reread both
1301 * 'p' and 'l' after any (potentially indirect) call to the same method.
1302 */
1303 char[] buffer = this.buffer;
1304 int p = pos;
1305 int l = limit;
1306 while (true) {
1307 if (p == l) {
1308 pos = p;
1309 if (!fillBuffer(1)) {
1310 break;
1311 }
1312 p = pos;
1313 l = limit;
1314 }
1315
1316 int c = buffer[p++];
1317 if (c == '\n') {
1318 lineNumber++;
1319 lineStart = p;
1320 continue;
1321 } else if (c == ' ' || c == '\r' || c == '\t') {
1322 continue;
1323 }
1324
1325 if (c == '/') {
1326 pos = p;
1327 if (p == l) {
1328 pos--; // push back '/' so it's still in the buffer when this method returns
1329 boolean charsLoaded = fillBuffer(2);
1330 pos++; // consume the '/' again
1331 if (!charsLoaded) {
1332 return c;
1333 }
1334 }
1335
1336 checkLenient();
1337 char peek = buffer[pos];
1338 switch (peek) {
1339 case '*':
1340 // skip a /* c-style comment */
1341 pos++;
1342 if (!skipTo("*/")) {
1343 throw syntaxError("Unterminated comment");
1344 }
1345 p = pos + 2;
1346 l = limit;
1347 continue;
1348
1349 case '/':
1350 // skip a // end-of-line comment
1351 pos++;
1352 skipToEndOfLine();
1353 p = pos;
1354 l = limit;
1355 continue;
1356
1357 default:
1358 return c;
1359 }
1360 } else if (c == '#') {
1361 pos = p;
1362 /*
1363 * Skip a # hash end-of-line comment. The JSON RFC doesn't
1364 * specify this behaviour, but it's required to parse
1365 * existing documents. See http://b/2571423.
1366 */
1367 checkLenient();
1368 skipToEndOfLine();
1369 p = pos;
1370 l = limit;
1371 } else {
1372 pos = p;
1373 return c;
1374 }
1375 }
1376 if (throwOnEof) {
1377 throw new EOFException("End of input"
1378 + " at line " + getLineNumber() + " column " + getColumnNumber());
1379 } else {
1380 return -1;
1381 }
1382 }
1383
1384 private void checkLenient() throws IOException {
1385 if (!lenient) {
1386 throw syntaxError("Use JsonReader.setLenient(true) to accept malformed JSON");
1387 }
1388 }
1389
1390 /**
1391 * Advances the position until after the next newline character. If the line
1392 * is terminated by "\r\n", the '\n' must be consumed as whitespace by the
1393 * caller.
1394 */
1395 private void skipToEndOfLine() throws IOException {
1396 while (pos < limit || fillBuffer(1)) {
1397 char c = buffer[pos++];
1398 if (c == '\n') {
1399 lineNumber++;
1400 lineStart = pos;
1401 break;
1402 } else if (c == '\r') {
1403 break;
1404 }
1405 }
1406 }
1407
1408 /**
1409 * @param toFind a string to search for. Must not contain a newline.
1410 */
1411 private boolean skipTo(String toFind) throws IOException {
1412 outer:
1413 for (; pos + toFind.length() <= limit || fillBuffer(toFind.length()); pos++) {
1414 if (buffer[pos] == '\n') {
1415 lineNumber++;
1416 lineStart = pos + 1;
1417 continue;
1418 }
1419 for (int c = 0; c < toFind.length(); c++) {
1420 if (buffer[pos + c] != toFind.charAt(c)) {
1421 continue outer;
1422 }
1423 }
1424 return true;
1425 }
1426 return false;
1427 }
1428
1429 @Override public String toString() {
1430 return getClass().getSimpleName()
1431 + " at line " + getLineNumber() + " column " + getColumnNumber();
1432 }
1433
1434 /**
1435 * Unescapes the character identified by the character or characters that
1436 * immediately follow a backslash. The backslash '\' should have already
1437 * been read. This supports both unicode escapes "u000A" and two-character
1438 * escapes "\n".
1439 *
1440 * @throws NumberFormatException if any unicode escape sequences are
1441 * malformed.
1442 */
1443 private char readEscapeCharacter() throws IOException {
1444 if (pos == limit && !fillBuffer(1)) {
1445 throw syntaxError("Unterminated escape sequence");
1446 }
1447
1448 char escaped = buffer[pos++];
1449 switch (escaped) {
1450 case 'u':
1451 if (pos + 4 > limit && !fillBuffer(4)) {
1452 throw syntaxError("Unterminated escape sequence");
1453 }
1454 // Equivalent to Integer.parseInt(stringPool.get(buffer, pos, 4), 16);
1455 char result = 0;
1456 for (int i = pos, end = i + 4; i < end; i++) {
1457 char c = buffer[i];
1458 result <<= 4;
1459 if (c >= '0' && c <= '9') {
1460 result += (c - '0');
1461 } else if (c >= 'a' && c <= 'f') {
1462 result += (c - 'a' + 10);
1463 } else if (c >= 'A' && c <= 'F') {
1464 result += (c - 'A' + 10);
1465 } else {
1466 throw new NumberFormatException("\\u" + new String(buffer, pos, 4));
1467 }
1468 }
1469 pos += 4;
1470 return result;
1471
1472 case 't':
1473 return '\t';
1474
1475 case 'b':
1476 return '\b';
1477
1478 case 'n':
1479 return '\n';
1480
1481 case 'r':
1482 return '\r';
1483
1484 case 'f':
1485 return '\f';
1486
1487 case '\n':
1488 lineNumber++;
1489 lineStart = pos;
1490 // fall-through
1491
1492 case '\'':
1493 case '"':
1494 case '\\':
1495 default:
1496 return escaped;
1497 }
1498 }
1499
1500 /**
1501 * Throws a new IO exception with the given message and a context snippet
1502 * with this reader's content.
1503 */
1504 private IOException syntaxError(String message) throws IOException {
1505 throw new MalformedJsonException(message
1506 + " at line " + getLineNumber() + " column " + getColumnNumber());
1507 }
1508
1509 /**
1510 * Consumes the non-execute prefix if it exists.
1511 */
1512 private void consumeNonExecutePrefix() throws IOException {
1513 // fast forward through the leading whitespace
1514 nextNonWhitespace(true);
1515 pos--;
1516
1517 if (pos + NON_EXECUTE_PREFIX.length > limit && !fillBuffer(NON_EXECUTE_PREFIX.length)) {
1518 return;
1519 }
1520
1521 for (int i = 0; i < NON_EXECUTE_PREFIX.length; i++) {
1522 if (buffer[pos + i] != NON_EXECUTE_PREFIX[i]) {
1523 return; // not a security token!
1524 }
1525 }
1526
1527 // we consumed a security token!
1528 pos += NON_EXECUTE_PREFIX.length;
1529 }
1530
1531 static {
1532 JsonReaderInternalAccess.INSTANCE = new JsonReaderInternalAccess() {
1533 @Override public void promoteNameToValue(JsonReader reader) throws IOException {
1534 if (reader instanceof JsonTreeReader) {
1535 ((JsonTreeReader)reader).promoteNameToValue();
1536 return;
1537 }
1538 int p = reader.peeked;
1539 if (p == PEEKED_NONE) {
1540 p = reader.doPeek();
1541 }
1542 if (p == PEEKED_DOUBLE_QUOTED_NAME) {
1543 reader.peeked = PEEKED_DOUBLE_QUOTED;
1544 } else if (p == PEEKED_SINGLE_QUOTED_NAME) {
1545 reader.peeked = PEEKED_SINGLE_QUOTED;
1546 } else if (p == PEEKED_UNQUOTED_NAME) {
1547 reader.peeked = PEEKED_UNQUOTED;
1548 } else {
1549 throw new IllegalStateException("Expected a name but was " + reader.peek() + " "
1550 + " at line " + reader.getLineNumber() + " column " + reader.getColumnNumber());
1551 }
1552 }
1553 };
1554 }
1555 }
This page took 0.061043 seconds and 3 git commands to generate.