• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
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 android.util;
18 
19 import java.io.Closeable;
20 import java.io.EOFException;
21 import java.io.IOException;
22 import java.io.Reader;
23 import java.util.ArrayList;
24 import java.util.List;
25 import libcore.internal.StringPool;
26 
27 /**
28  * Reads a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
29  * encoded value as a stream of tokens. This stream includes both literal
30  * values (strings, numbers, booleans, and nulls) as well as the begin and
31  * end delimiters of objects and arrays. The tokens are traversed in
32  * depth-first order, the same order that they appear in the JSON document.
33  * Within JSON objects, name/value pairs are represented by a single token.
34  *
35  * <h3>Parsing JSON</h3>
36  * To create a recursive descent parser for your own JSON streams, first create
37  * an entry point method that creates a {@code JsonReader}.
38  *
39  * <p>Next, create handler methods for each structure in your JSON text. You'll
40  * need a method for each object type and for each array type.
41  * <ul>
42  *   <li>Within <strong>array handling</strong> methods, first call {@link
43  *       #beginArray} to consume the array's opening bracket. Then create a
44  *       while loop that accumulates values, terminating when {@link #hasNext}
45  *       is false. Finally, read the array's closing bracket by calling {@link
46  *       #endArray}.
47  *   <li>Within <strong>object handling</strong> methods, first call {@link
48  *       #beginObject} to consume the object's opening brace. Then create a
49  *       while loop that assigns values to local variables based on their name.
50  *       This loop should terminate when {@link #hasNext} is false. Finally,
51  *       read the object's closing brace by calling {@link #endObject}.
52  * </ul>
53  * <p>When a nested object or array is encountered, delegate to the
54  * corresponding handler method.
55  *
56  * <p>When an unknown name is encountered, strict parsers should fail with an
57  * exception. Lenient parsers should call {@link #skipValue()} to recursively
58  * skip the value's nested tokens, which may otherwise conflict.
59  *
60  * <p>If a value may be null, you should first check using {@link #peek()}.
61  * Null literals can be consumed using either {@link #nextNull()} or {@link
62  * #skipValue()}.
63  *
64  * <h3>Example</h3>
65  * Suppose we'd like to parse a stream of messages such as the following: <pre> {@code
66  * [
67  *   {
68  *     "id": 912345678901,
69  *     "text": "How do I read JSON on Android?",
70  *     "geo": null,
71  *     "user": {
72  *       "name": "android_newb",
73  *       "followers_count": 41
74  *      }
75  *   },
76  *   {
77  *     "id": 912345678902,
78  *     "text": "@android_newb just use android.util.JsonReader!",
79  *     "geo": [50.454722, -104.606667],
80  *     "user": {
81  *       "name": "jesse",
82  *       "followers_count": 2
83  *     }
84  *   }
85  * ]}</pre>
86  * This code implements the parser for the above structure: <pre>   {@code
87  *
88  *   public List<Message> readJsonStream(InputStream in) throws IOException {
89  *     JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
90  *     try {
91  *       return readMessagesArray(reader);
92  *     } finally {
93  *       reader.close();
94  *     }
95  *   }
96  *
97  *   public List<Message> readMessagesArray(JsonReader reader) throws IOException {
98  *     List<Message> messages = new ArrayList<Message>();
99  *
100  *     reader.beginArray();
101  *     while (reader.hasNext()) {
102  *       messages.add(readMessage(reader));
103  *     }
104  *     reader.endArray();
105  *     return messages;
106  *   }
107  *
108  *   public Message readMessage(JsonReader reader) throws IOException {
109  *     long id = -1;
110  *     String text = null;
111  *     User user = null;
112  *     List<Double> geo = null;
113  *
114  *     reader.beginObject();
115  *     while (reader.hasNext()) {
116  *       String name = reader.nextName();
117  *       if (name.equals("id")) {
118  *         id = reader.nextLong();
119  *       } else if (name.equals("text")) {
120  *         text = reader.nextString();
121  *       } else if (name.equals("geo") && reader.peek() != JsonToken.NULL) {
122  *         geo = readDoublesArray(reader);
123  *       } else if (name.equals("user")) {
124  *         user = readUser(reader);
125  *       } else {
126  *         reader.skipValue();
127  *       }
128  *     }
129  *     reader.endObject();
130  *     return new Message(id, text, user, geo);
131  *   }
132  *
133  *   public List<Double> readDoublesArray(JsonReader reader) throws IOException {
134  *     List<Double> doubles = new ArrayList<Double>();
135  *
136  *     reader.beginArray();
137  *     while (reader.hasNext()) {
138  *       doubles.add(reader.nextDouble());
139  *     }
140  *     reader.endArray();
141  *     return doubles;
142  *   }
143  *
144  *   public User readUser(JsonReader reader) throws IOException {
145  *     String username = null;
146  *     int followersCount = -1;
147  *
148  *     reader.beginObject();
149  *     while (reader.hasNext()) {
150  *       String name = reader.nextName();
151  *       if (name.equals("name")) {
152  *         username = reader.nextString();
153  *       } else if (name.equals("followers_count")) {
154  *         followersCount = reader.nextInt();
155  *       } else {
156  *         reader.skipValue();
157  *       }
158  *     }
159  *     reader.endObject();
160  *     return new User(username, followersCount);
161  *   }}</pre>
162  *
163  * <h3>Number Handling</h3>
164  * This reader permits numeric values to be read as strings and string values to
165  * be read as numbers. For example, both elements of the JSON array {@code
166  * [1, "1"]} may be read using either {@link #nextInt} or {@link #nextString}.
167  * This behavior is intended to prevent lossy numeric conversions: double is
168  * JavaScript's only numeric type and very large values like {@code
169  * 9007199254740993} cannot be represented exactly on that platform. To minimize
170  * precision loss, extremely large values should be written and read as strings
171  * in JSON.
172  *
173  * <p>Each {@code JsonReader} may be used to read a single JSON stream. Instances
174  * of this class are not thread safe.
175  */
176 public final class JsonReader implements Closeable {
177 
178     private static final String TRUE = "true";
179     private static final String FALSE = "false";
180 
181     private final StringPool stringPool = new StringPool();
182 
183     /** The input JSON. */
184     private final Reader in;
185 
186     /** True to accept non-spec compliant JSON */
187     private boolean lenient = false;
188 
189     /**
190      * Use a manual buffer to easily read and unread upcoming characters, and
191      * also so we can create strings without an intermediate StringBuilder.
192      * We decode literals directly out of this buffer, so it must be at least as
193      * long as the longest token that can be reported as a number.
194      */
195     private final char[] buffer = new char[1024];
196     private int pos = 0;
197     private int limit = 0;
198 
199     /*
200      * The offset of the first character in the buffer.
201      */
202     private int bufferStartLine = 1;
203     private int bufferStartColumn = 1;
204 
205     private final List<JsonScope> stack = new ArrayList<JsonScope>();
206     {
207         push(JsonScope.EMPTY_DOCUMENT);
208     }
209 
210     /**
211      * The type of the next token to be returned by {@link #peek} and {@link
212      * #advance}. If null, peek() will assign a value.
213      */
214     private JsonToken token;
215 
216     /** The text of the next name. */
217     private String name;
218 
219     /*
220      * For the next literal value, we may have the text value, or the position
221      * and length in the buffer.
222      */
223     private String value;
224     private int valuePos;
225     private int valueLength;
226 
227     /** True if we're currently handling a skipValue() call. */
228     private boolean skipping = false;
229 
230     /**
231      * Creates a new instance that reads a JSON-encoded stream from {@code in}.
232      */
JsonReader(Reader in)233     public JsonReader(Reader in) {
234         if (in == null) {
235             throw new NullPointerException("in == null");
236         }
237         this.in = in;
238     }
239 
240     /**
241      * Configure this parser to be  be liberal in what it accepts. By default,
242      * this parser is strict and only accepts JSON as specified by <a
243      * href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the
244      * parser to lenient causes it to ignore the following syntax errors:
245      *
246      * <ul>
247      *   <li>End of line comments starting with {@code //} or {@code #} and
248      *       ending with a newline character.
249      *   <li>C-style comments starting with {@code /*} and ending with
250      *       {@code *}{@code /}. Such comments may not be nested.
251      *   <li>Names that are unquoted or {@code 'single quoted'}.
252      *   <li>Strings that are unquoted or {@code 'single quoted'}.
253      *   <li>Array elements separated by {@code ;} instead of {@code ,}.
254      *   <li>Unnecessary array separators. These are interpreted as if null
255      *       was the omitted value.
256      *   <li>Names and values separated by {@code =} or {@code =>} instead of
257      *       {@code :}.
258      *   <li>Name/value pairs separated by {@code ;} instead of {@code ,}.
259      * </ul>
260      */
setLenient(boolean lenient)261     public void setLenient(boolean lenient) {
262         this.lenient = lenient;
263     }
264 
265     /**
266      * Returns true if this parser is liberal in what it accepts.
267      */
isLenient()268     public boolean isLenient() {
269         return lenient;
270     }
271 
272     /**
273      * Consumes the next token from the JSON stream and asserts that it is the
274      * beginning of a new array.
275      */
beginArray()276     public void beginArray() throws IOException {
277         expect(JsonToken.BEGIN_ARRAY);
278     }
279 
280     /**
281      * Consumes the next token from the JSON stream and asserts that it is the
282      * end of the current array.
283      */
endArray()284     public void endArray() throws IOException {
285         expect(JsonToken.END_ARRAY);
286     }
287 
288     /**
289      * Consumes the next token from the JSON stream and asserts that it is the
290      * beginning of a new object.
291      */
beginObject()292     public void beginObject() throws IOException {
293         expect(JsonToken.BEGIN_OBJECT);
294     }
295 
296     /**
297      * Consumes the next token from the JSON stream and asserts that it is the
298      * end of the current array.
299      */
endObject()300     public void endObject() throws IOException {
301         expect(JsonToken.END_OBJECT);
302     }
303 
304     /**
305      * Consumes {@code expected}.
306      */
expect(JsonToken expected)307     private void expect(JsonToken expected) throws IOException {
308         peek();
309         if (token != expected) {
310             throw new IllegalStateException("Expected " + expected + " but was " + peek());
311         }
312         advance();
313     }
314 
315     /**
316      * Returns true if the current array or object has another element.
317      */
hasNext()318     public boolean hasNext() throws IOException {
319         peek();
320         return token != JsonToken.END_OBJECT && token != JsonToken.END_ARRAY;
321     }
322 
323     /**
324      * Returns the type of the next token without consuming it.
325      */
peek()326     public JsonToken peek() throws IOException {
327         if (token != null) {
328           return token;
329         }
330 
331         switch (peekStack()) {
332             case EMPTY_DOCUMENT:
333                 replaceTop(JsonScope.NONEMPTY_DOCUMENT);
334                 JsonToken firstToken = nextValue();
335                 if (!lenient && token != JsonToken.BEGIN_ARRAY && token != JsonToken.BEGIN_OBJECT) {
336                     throw new IOException(
337                             "Expected JSON document to start with '[' or '{' but was " + token);
338                 }
339                 return firstToken;
340             case EMPTY_ARRAY:
341                 return nextInArray(true);
342             case NONEMPTY_ARRAY:
343                 return nextInArray(false);
344             case EMPTY_OBJECT:
345                 return nextInObject(true);
346             case DANGLING_NAME:
347                 return objectValue();
348             case NONEMPTY_OBJECT:
349                 return nextInObject(false);
350             case NONEMPTY_DOCUMENT:
351                 try {
352                     JsonToken token = nextValue();
353                     if (lenient) {
354                         return token;
355                     }
356                     throw syntaxError("Expected EOF");
357                 } catch (EOFException e) {
358                     return token = JsonToken.END_DOCUMENT; // TODO: avoid throwing here?
359                 }
360             case CLOSED:
361                 throw new IllegalStateException("JsonReader is closed");
362             default:
363                 throw new AssertionError();
364         }
365     }
366 
367     /**
368      * Advances the cursor in the JSON stream to the next token.
369      */
advance()370     private JsonToken advance() throws IOException {
371         peek();
372 
373         JsonToken result = token;
374         token = null;
375         value = null;
376         name = null;
377         return result;
378     }
379 
380     /**
381      * Returns the next token, a {@link JsonToken#NAME property name}, and
382      * consumes it.
383      *
384      * @throws IOException if the next token in the stream is not a property
385      *     name.
386      */
nextName()387     public String nextName() throws IOException {
388         peek();
389         if (token != JsonToken.NAME) {
390             throw new IllegalStateException("Expected a name but was " + peek());
391         }
392         String result = name;
393         advance();
394         return result;
395     }
396 
397     /**
398      * Returns the {@link JsonToken#STRING string} value of the next token,
399      * consuming it. If the next token is a number, this method will return its
400      * string form.
401      *
402      * @throws IllegalStateException if the next token is not a string or if
403      *     this reader is closed.
404      */
nextString()405     public String nextString() throws IOException {
406         peek();
407         if (token != JsonToken.STRING && token != JsonToken.NUMBER) {
408             throw new IllegalStateException("Expected a string but was " + peek());
409         }
410 
411         String result = value;
412         advance();
413         return result;
414     }
415 
416     /**
417      * Returns the {@link JsonToken#BOOLEAN boolean} value of the next token,
418      * consuming it.
419      *
420      * @throws IllegalStateException if the next token is not a boolean or if
421      *     this reader is closed.
422      */
nextBoolean()423     public boolean nextBoolean() throws IOException {
424         peek();
425         if (token != JsonToken.BOOLEAN) {
426             throw new IllegalStateException("Expected a boolean but was " + token);
427         }
428 
429         boolean result = (value == TRUE);
430         advance();
431         return result;
432     }
433 
434     /**
435      * Consumes the next token from the JSON stream and asserts that it is a
436      * literal null.
437      *
438      * @throws IllegalStateException if the next token is not null or if this
439      *     reader is closed.
440      */
nextNull()441     public void nextNull() throws IOException {
442         peek();
443         if (token != JsonToken.NULL) {
444             throw new IllegalStateException("Expected null but was " + token);
445         }
446 
447         advance();
448     }
449 
450     /**
451      * Returns the {@link JsonToken#NUMBER double} value of the next token,
452      * consuming it. If the next token is a string, this method will attempt to
453      * parse it as a double using {@link Double#parseDouble(String)}.
454      *
455      * @throws IllegalStateException if the next token is not a literal value.
456      */
nextDouble()457     public double nextDouble() throws IOException {
458         peek();
459         if (token != JsonToken.STRING && token != JsonToken.NUMBER) {
460             throw new IllegalStateException("Expected a double but was " + token);
461         }
462 
463         double result = Double.parseDouble(value);
464         advance();
465         return result;
466     }
467 
468     /**
469      * Returns the {@link JsonToken#NUMBER long} value of the next token,
470      * consuming it. If the next token is a string, this method will attempt to
471      * parse it as a long. If the next token's numeric value cannot be exactly
472      * represented by a Java {@code long}, this method throws.
473      *
474      * @throws IllegalStateException if the next token is not a literal value.
475      * @throws NumberFormatException if the next literal value cannot be parsed
476      *     as a number, or exactly represented as a long.
477      */
nextLong()478     public long nextLong() throws IOException {
479         peek();
480         if (token != JsonToken.STRING && token != JsonToken.NUMBER) {
481             throw new IllegalStateException("Expected a long but was " + token);
482         }
483 
484         long result;
485         try {
486             result = Long.parseLong(value);
487         } catch (NumberFormatException ignored) {
488             double asDouble = Double.parseDouble(value); // don't catch this NumberFormatException
489             result = (long) asDouble;
490             if ((double) result != asDouble) {
491                 throw new NumberFormatException(value);
492             }
493         }
494 
495         advance();
496         return result;
497     }
498 
499     /**
500      * Returns the {@link JsonToken#NUMBER int} value of the next token,
501      * consuming it. If the next token is a string, this method will attempt to
502      * parse it as an int. If the next token's numeric value cannot be exactly
503      * represented by a Java {@code int}, this method throws.
504      *
505      * @throws IllegalStateException if the next token is not a literal value.
506      * @throws NumberFormatException if the next literal value cannot be parsed
507      *     as a number, or exactly represented as an int.
508      */
nextInt()509     public int nextInt() throws IOException {
510         peek();
511         if (token != JsonToken.STRING && token != JsonToken.NUMBER) {
512             throw new IllegalStateException("Expected an int but was " + token);
513         }
514 
515         int result;
516         try {
517             result = Integer.parseInt(value);
518         } catch (NumberFormatException ignored) {
519             double asDouble = Double.parseDouble(value); // don't catch this NumberFormatException
520             result = (int) asDouble;
521             if ((double) result != asDouble) {
522                 throw new NumberFormatException(value);
523             }
524         }
525 
526         advance();
527         return result;
528     }
529 
530     /**
531      * Closes this JSON reader and the underlying {@link Reader}.
532      */
close()533     public void close() throws IOException {
534         value = null;
535         token = null;
536         stack.clear();
537         stack.add(JsonScope.CLOSED);
538         in.close();
539     }
540 
541     /**
542      * Skips the next value recursively. If it is an object or array, all nested
543      * elements are skipped. This method is intended for use when the JSON token
544      * stream contains unrecognized or unhandled values.
545      */
skipValue()546     public void skipValue() throws IOException {
547         skipping = true;
548         try {
549             if (!hasNext() || peek() == JsonToken.END_DOCUMENT) {
550                 throw new IllegalStateException("No element left to skip");
551             }
552             int count = 0;
553             do {
554                 JsonToken token = advance();
555                 if (token == JsonToken.BEGIN_ARRAY || token == JsonToken.BEGIN_OBJECT) {
556                     count++;
557                 } else if (token == JsonToken.END_ARRAY || token == JsonToken.END_OBJECT) {
558                     count--;
559                 }
560             } while (count != 0);
561         } finally {
562             skipping = false;
563         }
564     }
565 
peekStack()566     private JsonScope peekStack() {
567         return stack.get(stack.size() - 1);
568     }
569 
pop()570     private JsonScope pop() {
571         return stack.remove(stack.size() - 1);
572     }
573 
push(JsonScope newTop)574     private void push(JsonScope newTop) {
575         stack.add(newTop);
576     }
577 
578     /**
579      * Replace the value on the top of the stack with the given value.
580      */
replaceTop(JsonScope newTop)581     private void replaceTop(JsonScope newTop) {
582         stack.set(stack.size() - 1, newTop);
583     }
584 
nextInArray(boolean firstElement)585     private JsonToken nextInArray(boolean firstElement) throws IOException {
586         if (firstElement) {
587             replaceTop(JsonScope.NONEMPTY_ARRAY);
588         } else {
589             /* Look for a comma before each element after the first element. */
590             switch (nextNonWhitespace()) {
591                 case ']':
592                     pop();
593                     return token = JsonToken.END_ARRAY;
594                 case ';':
595                     checkLenient(); // fall-through
596                 case ',':
597                     break;
598                 default:
599                     throw syntaxError("Unterminated array");
600             }
601         }
602 
603         switch (nextNonWhitespace()) {
604             case ']':
605                 if (firstElement) {
606                     pop();
607                     return token = JsonToken.END_ARRAY;
608                 }
609                 // fall-through to handle ",]"
610             case ';':
611             case ',':
612                 /* In lenient mode, a 0-length literal means 'null' */
613                 checkLenient();
614                 pos--;
615                 value = "null";
616                 return token = JsonToken.NULL;
617             default:
618                 pos--;
619                 return nextValue();
620         }
621     }
622 
nextInObject(boolean firstElement)623     private JsonToken nextInObject(boolean firstElement) throws IOException {
624         /*
625          * Read delimiters. Either a comma/semicolon separating this and the
626          * previous name-value pair, or a close brace to denote the end of the
627          * object.
628          */
629         if (firstElement) {
630             /* Peek to see if this is the empty object. */
631             switch (nextNonWhitespace()) {
632                 case '}':
633                     pop();
634                     return token = JsonToken.END_OBJECT;
635                 default:
636                     pos--;
637             }
638         } else {
639             switch (nextNonWhitespace()) {
640                 case '}':
641                     pop();
642                     return token = JsonToken.END_OBJECT;
643                 case ';':
644                 case ',':
645                     break;
646                 default:
647                     throw syntaxError("Unterminated object");
648             }
649         }
650 
651         /* Read the name. */
652         int quote = nextNonWhitespace();
653         switch (quote) {
654             case '\'':
655                 checkLenient(); // fall-through
656             case '"':
657                 name = nextString((char) quote);
658                 break;
659             default:
660                 checkLenient();
661                 pos--;
662                 name = nextLiteral(false);
663                 if (name.isEmpty()) {
664                     throw syntaxError("Expected name");
665                 }
666         }
667 
668         replaceTop(JsonScope.DANGLING_NAME);
669         return token = JsonToken.NAME;
670     }
671 
objectValue()672     private JsonToken objectValue() throws IOException {
673         /*
674          * Read the name/value separator. Usually a colon ':'. In lenient mode
675          * we also accept an equals sign '=', or an arrow "=>".
676          */
677         switch (nextNonWhitespace()) {
678             case ':':
679                 break;
680             case '=':
681                 checkLenient();
682                 if ((pos < limit || fillBuffer(1)) && buffer[pos] == '>') {
683                     pos++;
684                 }
685                 break;
686             default:
687                 throw syntaxError("Expected ':'");
688         }
689 
690         replaceTop(JsonScope.NONEMPTY_OBJECT);
691         return nextValue();
692     }
693 
nextValue()694     private JsonToken nextValue() throws IOException {
695         int c = nextNonWhitespace();
696         switch (c) {
697             case '{':
698                 push(JsonScope.EMPTY_OBJECT);
699                 return token = JsonToken.BEGIN_OBJECT;
700 
701             case '[':
702                 push(JsonScope.EMPTY_ARRAY);
703                 return token = JsonToken.BEGIN_ARRAY;
704 
705             case '\'':
706                 checkLenient(); // fall-through
707             case '"':
708                 value = nextString((char) c);
709                 return token = JsonToken.STRING;
710 
711             default:
712                 pos--;
713                 return readLiteral();
714         }
715     }
716 
717     /**
718      * Returns true once {@code limit - pos >= minimum}. If the data is
719      * exhausted before that many characters are available, this returns
720      * false.
721      */
fillBuffer(int minimum)722     private boolean fillBuffer(int minimum) throws IOException {
723         // Before clobbering the old characters, update where buffer starts
724         for (int i = 0; i < pos; i++) {
725             if (buffer[i] == '\n') {
726                 bufferStartLine++;
727                 bufferStartColumn = 1;
728             } else {
729                 bufferStartColumn++;
730             }
731         }
732 
733         if (limit != pos) {
734             limit -= pos;
735             System.arraycopy(buffer, pos, buffer, 0, limit);
736         } else {
737             limit = 0;
738         }
739 
740         pos = 0;
741         int total;
742         while ((total = in.read(buffer, limit, buffer.length - limit)) != -1) {
743             limit += total;
744 
745             // if this is the first read, consume an optional byte order mark (BOM) if it exists
746                 if (bufferStartLine == 1 && bufferStartColumn == 1
747                         && limit > 0 && buffer[0] == '\ufeff') {
748                 pos++;
749                 bufferStartColumn--;
750             }
751 
752             if (limit >= minimum) {
753                 return true;
754             }
755         }
756         return false;
757     }
758 
getLineNumber()759     private int getLineNumber() {
760         int result = bufferStartLine;
761         for (int i = 0; i < pos; i++) {
762             if (buffer[i] == '\n') {
763                 result++;
764             }
765         }
766         return result;
767     }
768 
getColumnNumber()769     private int getColumnNumber() {
770         int result = bufferStartColumn;
771         for (int i = 0; i < pos; i++) {
772             if (buffer[i] == '\n') {
773                 result = 1;
774             } else {
775                 result++;
776             }
777         }
778         return result;
779     }
780 
nextNonWhitespace()781     private int nextNonWhitespace() throws IOException {
782         while (pos < limit || fillBuffer(1)) {
783             int c = buffer[pos++];
784             switch (c) {
785                 case '\t':
786                 case ' ':
787                 case '\n':
788                 case '\r':
789                     continue;
790 
791                 case '/':
792                     if (pos == limit && !fillBuffer(1)) {
793                         return c;
794                     }
795 
796                     checkLenient();
797                     char peek = buffer[pos];
798                     switch (peek) {
799                         case '*':
800                             // skip a /* c-style comment */
801                             pos++;
802                             if (!skipTo("*/")) {
803                                 throw syntaxError("Unterminated comment");
804                             }
805                             pos += 2;
806                             continue;
807 
808                         case '/':
809                             // skip a // end-of-line comment
810                             pos++;
811                             skipToEndOfLine();
812                             continue;
813 
814                         default:
815                             return c;
816                     }
817 
818                 case '#':
819                     /*
820                      * Skip a # hash end-of-line comment. The JSON RFC doesn't
821                      * specify this behaviour, but it's required to parse
822                      * existing documents. See http://b/2571423.
823                      */
824                     checkLenient();
825                     skipToEndOfLine();
826                     continue;
827 
828                 default:
829                     return c;
830             }
831         }
832 
833         throw new EOFException("End of input");
834     }
835 
checkLenient()836     private void checkLenient() throws IOException {
837         if (!lenient) {
838             throw syntaxError("Use JsonReader.setLenient(true) to accept malformed JSON");
839         }
840     }
841 
842     /**
843      * Advances the position until after the next newline character. If the line
844      * is terminated by "\r\n", the '\n' must be consumed as whitespace by the
845      * caller.
846      */
skipToEndOfLine()847     private void skipToEndOfLine() throws IOException {
848         while (pos < limit || fillBuffer(1)) {
849             char c = buffer[pos++];
850             if (c == '\r' || c == '\n') {
851                 break;
852             }
853         }
854     }
855 
skipTo(String toFind)856     private boolean skipTo(String toFind) throws IOException {
857         outer:
858         for (; pos + toFind.length() <= limit || fillBuffer(toFind.length()); pos++) {
859             for (int c = 0; c < toFind.length(); c++) {
860                 if (buffer[pos + c] != toFind.charAt(c)) {
861                     continue outer;
862                 }
863             }
864             return true;
865         }
866         return false;
867     }
868 
869     /**
870      * Returns the string up to but not including {@code quote}, unescaping any
871      * character escape sequences encountered along the way. The opening quote
872      * should have already been read. This consumes the closing quote, but does
873      * not include it in the returned string.
874      *
875      * @param quote either ' or ".
876      * @throws NumberFormatException if any unicode escape sequences are
877      *     malformed.
878      */
nextString(char quote)879     private String nextString(char quote) throws IOException {
880         StringBuilder builder = null;
881         do {
882             /* the index of the first character not yet appended to the builder. */
883             int start = pos;
884             while (pos < limit) {
885                 int c = buffer[pos++];
886 
887                 if (c == quote) {
888                     if (skipping) {
889                         return "skipped!";
890                     } else if (builder == null) {
891                         return stringPool.get(buffer, start, pos - start - 1);
892                     } else {
893                         builder.append(buffer, start, pos - start - 1);
894                         return builder.toString();
895                     }
896 
897                 } else if (c == '\\') {
898                     if (builder == null) {
899                         builder = new StringBuilder();
900                     }
901                     builder.append(buffer, start, pos - start - 1);
902                     builder.append(readEscapeCharacter());
903                     start = pos;
904                 }
905             }
906 
907             if (builder == null) {
908                 builder = new StringBuilder();
909             }
910             builder.append(buffer, start, pos - start);
911         } while (fillBuffer(1));
912 
913         throw syntaxError("Unterminated string");
914     }
915 
916     /**
917      * Reads the value up to but not including any delimiter characters. This
918      * does not consume the delimiter character.
919      *
920      * @param assignOffsetsOnly true for this method to only set the valuePos
921      *     and valueLength fields and return a null result. This only works if
922      *     the literal is short; a string is returned otherwise.
923      */
nextLiteral(boolean assignOffsetsOnly)924     private String nextLiteral(boolean assignOffsetsOnly) throws IOException {
925         StringBuilder builder = null;
926         valuePos = -1;
927         valueLength = 0;
928         int i = 0;
929 
930         findNonLiteralCharacter:
931         while (true) {
932             for (; pos + i < limit; i++) {
933                 switch (buffer[pos + i]) {
934                 case '/':
935                 case '\\':
936                 case ';':
937                 case '#':
938                 case '=':
939                     checkLenient(); // fall-through
940                 case '{':
941                 case '}':
942                 case '[':
943                 case ']':
944                 case ':':
945                 case ',':
946                 case ' ':
947                 case '\t':
948                 case '\f':
949                 case '\r':
950                 case '\n':
951                     break findNonLiteralCharacter;
952                 }
953             }
954 
955             /*
956              * Attempt to load the entire literal into the buffer at once. If
957              * we run out of input, add a non-literal character at the end so
958              * that decoding doesn't need to do bounds checks.
959              */
960             if (i < buffer.length) {
961                 if (fillBuffer(i + 1)) {
962                     continue;
963                 } else {
964                     buffer[limit] = '\0';
965                     break;
966                 }
967             }
968 
969             // use a StringBuilder when the value is too long. It must be an unquoted string.
970             if (builder == null) {
971                 builder = new StringBuilder();
972             }
973             builder.append(buffer, pos, i);
974             valueLength += i;
975             pos += i;
976             i = 0;
977             if (!fillBuffer(1)) {
978                 break;
979             }
980         }
981 
982         String result;
983         if (assignOffsetsOnly && builder == null) {
984             valuePos = pos;
985             result = null;
986         } else if (skipping) {
987             result = "skipped!";
988         } else if (builder == null) {
989             result = stringPool.get(buffer, pos, i);
990         } else {
991             builder.append(buffer, pos, i);
992             result = builder.toString();
993         }
994         valueLength += i;
995         pos += i;
996         return result;
997     }
998 
toString()999     @Override public String toString() {
1000         return getClass().getSimpleName() + " near " + getSnippet();
1001     }
1002 
1003     /**
1004      * Unescapes the character identified by the character or characters that
1005      * immediately follow a backslash. The backslash '\' should have already
1006      * been read. This supports both unicode escapes "u000A" and two-character
1007      * escapes "\n".
1008      *
1009      * @throws NumberFormatException if any unicode escape sequences are
1010      *     malformed.
1011      */
readEscapeCharacter()1012     private char readEscapeCharacter() throws IOException {
1013         if (pos == limit && !fillBuffer(1)) {
1014             throw syntaxError("Unterminated escape sequence");
1015         }
1016 
1017         char escaped = buffer[pos++];
1018         switch (escaped) {
1019             case 'u':
1020                 if (pos + 4 > limit && !fillBuffer(4)) {
1021                     throw syntaxError("Unterminated escape sequence");
1022                 }
1023                 String hex = stringPool.get(buffer, pos, 4);
1024                 pos += 4;
1025                 return (char) Integer.parseInt(hex, 16);
1026 
1027             case 't':
1028                 return '\t';
1029 
1030             case 'b':
1031                 return '\b';
1032 
1033             case 'n':
1034                 return '\n';
1035 
1036             case 'r':
1037                 return '\r';
1038 
1039             case 'f':
1040                 return '\f';
1041 
1042             case '\'':
1043             case '"':
1044             case '\\':
1045             default:
1046                 return escaped;
1047         }
1048     }
1049 
1050     /**
1051      * Reads a null, boolean, numeric or unquoted string literal value.
1052      */
readLiteral()1053     private JsonToken readLiteral() throws IOException {
1054         value = nextLiteral(true);
1055         if (valueLength == 0) {
1056             throw syntaxError("Expected literal value");
1057         }
1058         token = decodeLiteral();
1059         if (token == JsonToken.STRING) {
1060           checkLenient();
1061         }
1062         return token;
1063     }
1064 
1065     /**
1066      * Assigns {@code nextToken} based on the value of {@code nextValue}.
1067      */
decodeLiteral()1068     private JsonToken decodeLiteral() throws IOException {
1069         if (valuePos == -1) {
1070             // it was too long to fit in the buffer so it can only be a string
1071             return JsonToken.STRING;
1072         } else if (valueLength == 4
1073                 && ('n' == buffer[valuePos    ] || 'N' == buffer[valuePos    ])
1074                 && ('u' == buffer[valuePos + 1] || 'U' == buffer[valuePos + 1])
1075                 && ('l' == buffer[valuePos + 2] || 'L' == buffer[valuePos + 2])
1076                 && ('l' == buffer[valuePos + 3] || 'L' == buffer[valuePos + 3])) {
1077             value = "null";
1078             return JsonToken.NULL;
1079         } else if (valueLength == 4
1080                 && ('t' == buffer[valuePos    ] || 'T' == buffer[valuePos    ])
1081                 && ('r' == buffer[valuePos + 1] || 'R' == buffer[valuePos + 1])
1082                 && ('u' == buffer[valuePos + 2] || 'U' == buffer[valuePos + 2])
1083                 && ('e' == buffer[valuePos + 3] || 'E' == buffer[valuePos + 3])) {
1084             value = TRUE;
1085             return JsonToken.BOOLEAN;
1086         } else if (valueLength == 5
1087                 && ('f' == buffer[valuePos    ] || 'F' == buffer[valuePos    ])
1088                 && ('a' == buffer[valuePos + 1] || 'A' == buffer[valuePos + 1])
1089                 && ('l' == buffer[valuePos + 2] || 'L' == buffer[valuePos + 2])
1090                 && ('s' == buffer[valuePos + 3] || 'S' == buffer[valuePos + 3])
1091                 && ('e' == buffer[valuePos + 4] || 'E' == buffer[valuePos + 4])) {
1092             value = FALSE;
1093             return JsonToken.BOOLEAN;
1094         } else {
1095             value = stringPool.get(buffer, valuePos, valueLength);
1096             return decodeNumber(buffer, valuePos, valueLength);
1097         }
1098     }
1099 
1100     /**
1101      * Determine whether the characters is a JSON number. Numbers are of the
1102      * form -12.34e+56. Fractional and exponential parts are optional. Leading
1103      * zeroes are not allowed in the value or exponential part, but are allowed
1104      * in the fraction.
1105      */
decodeNumber(char[] chars, int offset, int length)1106     private JsonToken decodeNumber(char[] chars, int offset, int length) {
1107         int i = offset;
1108         int c = chars[i];
1109 
1110         if (c == '-') {
1111             c = chars[++i];
1112         }
1113 
1114         if (c == '0') {
1115             c = chars[++i];
1116         } else if (c >= '1' && c <= '9') {
1117             c = chars[++i];
1118             while (c >= '0' && c <= '9') {
1119                 c = chars[++i];
1120             }
1121         } else {
1122             return JsonToken.STRING;
1123         }
1124 
1125         if (c == '.') {
1126             c = chars[++i];
1127             while (c >= '0' && c <= '9') {
1128                 c = chars[++i];
1129             }
1130         }
1131 
1132         if (c == 'e' || c == 'E') {
1133             c = chars[++i];
1134             if (c == '+' || c == '-') {
1135                 c = chars[++i];
1136             }
1137             if (c >= '0' && c <= '9') {
1138                 c = chars[++i];
1139                 while (c >= '0' && c <= '9') {
1140                     c = chars[++i];
1141                 }
1142             } else {
1143                 return JsonToken.STRING;
1144             }
1145         }
1146 
1147         if (i == offset + length) {
1148             return JsonToken.NUMBER;
1149         } else {
1150             return JsonToken.STRING;
1151         }
1152     }
1153 
1154     /**
1155      * Throws a new IO exception with the given message and a context snippet
1156      * with this reader's content.
1157      */
syntaxError(String message)1158     private IOException syntaxError(String message) throws IOException {
1159         throw new MalformedJsonException(message
1160                 + " at line " + getLineNumber() + " column " + getColumnNumber());
1161     }
1162 
getSnippet()1163     private CharSequence getSnippet() {
1164         StringBuilder snippet = new StringBuilder();
1165         int beforePos = Math.min(pos, 20);
1166         snippet.append(buffer, pos - beforePos, beforePos);
1167         int afterPos = Math.min(limit - pos, 20);
1168         snippet.append(buffer, pos, afterPos);
1169         return snippet;
1170     }
1171 }
1172