Home | History | Annotate | Download | only in util
      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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    300     public void endObject() throws IOException {
    301         expect(JsonToken.END_OBJECT);
    302     }
    303 
    304     /**
    305      * Consumes {@code expected}.
    306      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    566     private JsonScope peekStack() {
    567         return stack.get(stack.size() - 1);
    568     }
    569 
    570     private JsonScope pop() {
    571         return stack.remove(stack.size() - 1);
    572     }
    573 
    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      */
    581     private void replaceTop(JsonScope newTop) {
    582         stack.set(stack.size() - 1, newTop);
    583     }
    584 
    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 
    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 
    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 
    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      */
    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 
    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 
    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 
    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 
    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      */
    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 
    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      */
    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      */
    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 
    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      */
   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      */
   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      */
   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      */
   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      */
   1158     private IOException syntaxError(String message) throws IOException {
   1159         throw new MalformedJsonException(message
   1160                 + " at line " + getLineNumber() + " column " + getColumnNumber());
   1161     }
   1162 
   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