github.com/keysonZZZ/kmg@v0.0.0-20151121023212-05317bfd7d39/kmgRpc/kmgRpcJava/java/src/com/google/gson/stream/JsonReader.java (about)

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