15977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot/* 25977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Copyright (C) 2010 The Android Open Source Project 35977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 45977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Licensed under the Apache License, Version 2.0 (the "License"); 55977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * you may not use this file except in compliance with the License. 65977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * You may obtain a copy of the License at 75977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 85977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * http://www.apache.org/licenses/LICENSE-2.0 95977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 105977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Unless required by applicable law or agreed to in writing, software 115977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * distributed under the License is distributed on an "AS IS" BASIS, 125977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * See the License for the specific language governing permissions and 145977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * limitations under the License. 155977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 165977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 175977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabotpackage com.android.json.stream; 185977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 195977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabotimport java.io.IOException; 205977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabotimport java.io.Reader; 215977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabotimport java.io.Closeable; 225977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabotimport java.util.ArrayList; 235977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabotimport java.util.List; 245977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 255977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot/** 265977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Reads a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>) 275977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * encoded value as a stream of tokens. This stream includes both literal 285977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * values (strings, numbers, booleans, and nulls) as well as the begin and 295977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * end delimiters of objects and arrays. The tokens are traversed in 305977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * depth-first order, the same order that they appear in the JSON document. 315977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Within JSON objects, name/value pairs are represented by a single token. 325977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 335977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <h3>Parsing JSON</h3> 345977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * To create a recursive descent parser for your own JSON streams, first create 355977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * an entry point method that creates a {@code JsonReader}. 365977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 375977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <p>Next, create handler methods for each structure in your JSON text. You'll 385977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * need a method for each object type and for each array type. 395977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <ul> 405977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <li>Within <strong>array handling</strong> methods, first call {@link 415977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * #beginArray} to consume the array's opening bracket. Then create a 425977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * while loop that accumulates values, terminating when {@link #hasNext} 435977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * is false. Finally, read the array's closing bracket by calling {@link 445977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * #endArray}. 455977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <li>Within <strong>object handling</strong> methods, first call {@link 465977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * #beginObject} to consume the object's opening brace. Then create a 475977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * while loop that assigns values to local variables based on their name. 485977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * This loop should terminate when {@link #hasNext} is false. Finally, 495977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * read the object's closing brace by calling {@link #endObject}. 505977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * </ul> 515977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <p>When a nested object or array is encountered, delegate to the 525977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * corresponding handler method. 535977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 545977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <p>When an unknown name is encountered, strict parsers should fail with an 555977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * exception. Lenient parsers should call {@link #skipValue()} to recursively 565977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * skip the value's nested tokens, which may otherwise conflict. 575977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 585977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <p>If a value may be null, you should first check using {@link #peek()}. 595977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Null literals can be consumed using either {@link #nextNull()} or {@link 605977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * #skipValue()}. 615977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 625977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <h3>Example</h3> 635977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Suppose we'd like to parse a stream of messages such as the following: <pre> {@code 645977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * [ 655977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * { 665977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * "id": 912345678901, 675977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * "text": "How do I read JSON on Android?", 685977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * "geo": null, 695977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * "user": { 705977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * "name": "android_newb", 715977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * "followers_count": 41 725977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } 735977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * }, 745977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * { 755977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * "id": 912345678902, 765977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * "text": "@android_newb just use android.util.JsonReader!", 775977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * "geo": [50.454722, -104.606667], 785977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * "user": { 795977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * "name": "jesse", 805977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * "followers_count": 2 815977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } 825977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } 835977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * ]}</pre> 845977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * This code implements the parser for the above structure: <pre> {@code 855977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 865977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * public List<Message> readJsonStream(InputStream in) throws IOException { 875977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8")); 885977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * return readMessagesArray(reader); 895977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } 905977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 915977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * public List<Message> readMessagesArray(JsonReader reader) throws IOException { 925977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * List<Message> messages = new ArrayList<Message>(); 935977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 945977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * reader.beginArray(); 955977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * while (reader.hasNext()) { 965977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * messages.add(readMessage(reader)); 975977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } 985977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * reader.endArray(); 995977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * return messages; 1005977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } 1015977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 1025977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * public Message readMessage(JsonReader reader) throws IOException { 1035977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * long id = -1; 1045977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * String text = null; 1055977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * User user = null; 1065977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * List<Double> geo = null; 1075977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 1085977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * reader.beginObject(); 1095977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * while (reader.hasNext()) { 1105977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * String name = reader.nextName(); 1115977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * if (name.equals("id")) { 1125977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * id = reader.nextLong(); 1135977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } else if (name.equals("text")) { 1145977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * text = reader.nextString(); 1155977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } else if (name.equals("geo") && reader.peek() != JsonToken.NULL) { 1165977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * geo = readDoublesArray(reader); 1175977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } else if (name.equals("user")) { 1185977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * user = readUser(reader); 1195977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } else { 1205977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * reader.skipValue(); 1215977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } 1225977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } 1235977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * reader.endObject(); 1245977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * return new Message(id, text, user, geo); 1255977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } 1265977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 1275977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * public List<Double> readDoublesArray(JsonReader reader) throws IOException { 1285977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * List<Double> doubles = new ArrayList<Double>(); 1295977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 1305977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * reader.beginArray(); 1315977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * while (reader.hasNext()) { 1325977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * doubles.add(reader.nextDouble()); 1335977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } 1345977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * reader.endArray(); 1355977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * return doubles; 1365977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } 1375977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 1385977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * public User readUser(JsonReader reader) throws IOException { 1395977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * String username = null; 1405977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * int followersCount = -1; 1415977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 1425977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * reader.beginObject(); 1435977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * while (reader.hasNext()) { 1445977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * String name = reader.nextName(); 1455977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * if (name.equals("name")) { 1465977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * username = reader.nextString(); 1475977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } else if (name.equals("followers_count")) { 1485977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * followersCount = reader.nextInt(); 1495977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } else { 1505977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * reader.skipValue(); 1515977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } 1525977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * } 1535977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * reader.endObject(); 1545977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * return new User(username, followersCount); 1555977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * }}</pre> 1565977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 1575977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <h3>Number Handling</h3> 1585977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * This reader permits numeric values to be read as strings and string values to 1595977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * be read as numbers. For example, both elements of the JSON array {@code 1605977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * [1, "1"]} may be read using either {@link #nextInt} or {@link #nextString}. 1615977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * This behavior is intended to prevent lossy numeric conversions: double is 1625977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * JavaScript's only numeric type and very large values like {@code 1635977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 9007199254740993} cannot be represented exactly on that platform. To minimize 1645977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * precision loss, extremely large values should be written and read as strings 1655977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * in JSON. 1665977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 1675977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <p>Each {@code JsonReader} may be used to read a single JSON stream. Instances 1685977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * of this class are not thread safe. 1695977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 1705977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabotpublic final class JsonReader implements Closeable { 1715977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 1725977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private static final String TRUE = "true"; 1735977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private static final String FALSE = "false"; 1745977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 1755977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** The input JSON. */ 1765977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private final Reader in; 1775977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 1785977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** True to accept non-spec compliant JSON */ 1795977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private boolean lenient = false; 1805977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 1815977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 1825977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Use a manual buffer to easily read and unread upcoming characters, and 1835977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * also so we can create strings without an intermediate StringBuilder. 1845977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * We decode literals directly out of this buffer, so it must be at least as 1855977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * long as the longest token that can be reported as a number. 1865977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 1875977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private final char[] buffer = new char[1024]; 1885977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private int pos = 0; 1895977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private int limit = 0; 1905977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 1915977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private final List<JsonScope> stack = new ArrayList<JsonScope>(); 1925977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot { 1935977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot push(JsonScope.EMPTY_DOCUMENT); 1945977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 1955977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 1965977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 1975977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * The type of the next token to be returned by {@link #peek} and {@link 1985977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * #advance}. If null, peek() will assign a value. 1995977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 2005977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private JsonToken token; 2015977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 2025977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** The text of the next name. */ 2035977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private String name; 2045977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 2055977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /* 2065977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * For the next literal value, we may have the text value, or the position 2075977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * and length in the buffer. 2085977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 2095977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private String value; 2105977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private int valuePos; 2115977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private int valueLength; 2125977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 2135977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** True if we're currently handling a skipValue() call. */ 2145977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private boolean skipping = false; 2155977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 2165977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 2175977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Creates a new instance that reads a JSON-encoded stream from {@code in}. 2185977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 2195977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot public JsonReader(Reader in) { 2205977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (in == null) { 2215977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw new NullPointerException("in == null"); 2225977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 2235977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot this.in = in; 2245977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 2255977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 2265977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 2275977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Configure this parser to be be liberal in what it accepts. By default, 2285977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * this parser is strict and only accepts JSON as specified by <a 2295977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the 2305977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * parser to lenient causes it to ignore the following syntax errors: 2315977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 2325977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <ul> 2335977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <li>End of line comments starting with {@code //} or {@code #} and 2345977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * ending with a newline character. 2355977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <li>C-style comments starting with {@code /*} and ending with 2365977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * {@code *}{@code /}. Such comments may not be nested. 2375977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <li>Names that are unquoted or {@code 'single quoted'}. 2385977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <li>Strings that are unquoted or {@code 'single quoted'}. 2395977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <li>Array elements separated by {@code ;} instead of {@code ,}. 2405977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <li>Unnecessary array separators. These are interpreted as if null 2415977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * was the omitted value. 2425977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <li>Names and values separated by {@code =} or {@code =>} instead of 2435977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * {@code :}. 2445977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <li>Name/value pairs separated by {@code ;} instead of {@code ,}. 2455977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * </ul> 2465977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 2475977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot public void setLenient(boolean lenient) { 2485977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot this.lenient = lenient; 2495977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 2505977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 2515977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 2525977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Consumes the next token from the JSON stream and asserts that it is the 2535977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * beginning of a new array. 2545977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 2555977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot public void beginArray() throws IOException { 2565977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot expect(JsonToken.BEGIN_ARRAY); 2575977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 2585977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 2595977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 2605977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Consumes the next token from the JSON stream and asserts that it is the 2615977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * end of the current array. 2625977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 2635977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot public void endArray() throws IOException { 2645977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot expect(JsonToken.END_ARRAY); 2655977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 2665977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 2675977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 2685977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Consumes the next token from the JSON stream and asserts that it is the 2695977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * beginning of a new object. 2705977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 2715977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot public void beginObject() throws IOException { 2725977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot expect(JsonToken.BEGIN_OBJECT); 2735977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 2745977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 2755977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 2765977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Consumes the next token from the JSON stream and asserts that it is the 2775977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * end of the current array. 2785977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 2795977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot public void endObject() throws IOException { 2805977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot expect(JsonToken.END_OBJECT); 2815977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 2825977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 2835977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 2845977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Consumes {@code expected}. 2855977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 2865977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private void expect(JsonToken expected) throws IOException { 2875977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot peek(); 2885977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (token != expected) { 2895977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw new IllegalStateException("Expected " + expected + " but was " + peek()); 2905977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 2915977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot advance(); 2925977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 2935977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 2945977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 2955977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Returns true if the current array or object has another element. 2965977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 2975977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot public boolean hasNext() throws IOException { 2985977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot peek(); 2995977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return token != JsonToken.END_OBJECT && token != JsonToken.END_ARRAY; 3005977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 3015977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 3025977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 3035977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Returns the type of the next token without consuming it. 3045977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 3055977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot public JsonToken peek() throws IOException { 3065977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (token != null) { 3075977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return token; 3085977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 3095977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 3105977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot switch (peekStack()) { 3115977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case EMPTY_DOCUMENT: 3125977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot replaceTop(JsonScope.NONEMPTY_DOCUMENT); 3135977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot JsonToken firstToken = nextValue(); 3145977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (token != JsonToken.BEGIN_ARRAY && token != JsonToken.BEGIN_OBJECT) { 3155977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw new IOException( 3165977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot "Expected JSON document to start with '[' or '{' but was " + token); 3175977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 3185977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return firstToken; 3195977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case EMPTY_ARRAY: 3205977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return nextInArray(true); 3215977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case NONEMPTY_ARRAY: 3225977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return nextInArray(false); 3235977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case EMPTY_OBJECT: 3245977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return nextInObject(true); 3255977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case DANGLING_NAME: 3265977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return objectValue(); 3275977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case NONEMPTY_OBJECT: 3285977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return nextInObject(false); 3295977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case NONEMPTY_DOCUMENT: 3305977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return token = JsonToken.END_DOCUMENT; 3315977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case CLOSED: 3325977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw new IllegalStateException("JsonReader is closed"); 3335977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot default: 3345977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw new AssertionError(); 3355977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 3365977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 3375977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 3385977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 3395977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Advances the cursor in the JSON stream to the next token. 3405977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 3415977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private JsonToken advance() throws IOException { 3425977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot peek(); 3435977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 3445977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot JsonToken result = token; 3455977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot token = null; 3465977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot value = null; 3475977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot name = null; 3485977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return result; 3495977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 3505977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 3515977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 3525977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Returns the next token, a {@link JsonToken#NAME property name}, and 3535977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * consumes it. 3545977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 3555977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * @throws IOException if the next token in the stream is not a property 3565977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * name. 3575977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 3585977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot public String nextName() throws IOException { 3595977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot peek(); 3605977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (token != JsonToken.NAME) { 3615977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw new IllegalStateException("Expected a name but was " + peek()); 3625977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 3635977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot String result = name; 3645977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot advance(); 3655977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return result; 3665977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 3675977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 3685977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 3695977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Returns the {@link JsonToken#STRING string} value of the next token, 3705977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * consuming it. If the next token is a number, this method will return its 3715977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * string form. 3725977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 3735977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * @throws IllegalStateException if the next token is not a string or if 3745977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * this reader is closed. 3755977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 3765977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot public String nextString() throws IOException { 3775977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot peek(); 3785977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (token != JsonToken.STRING && token != JsonToken.NUMBER) { 3795977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw new IllegalStateException("Expected a string but was " + peek()); 3805977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 3815977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 3825977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot String result = value; 3835977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot advance(); 3845977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return result; 3855977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 3865977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 3875977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 3885977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Returns the {@link JsonToken#BOOLEAN boolean} value of the next token, 3895977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * consuming it. 3905977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 3915977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * @throws IllegalStateException if the next token is not a boolean or if 3925977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * this reader is closed. 3935977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 3945977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot public boolean nextBoolean() throws IOException { 3955977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot peek(); 3965977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (token != JsonToken.BOOLEAN) { 3975977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw new IllegalStateException("Expected a boolean but was " + token); 3985977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 3995977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 4005977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot boolean result = (value == TRUE); 4015977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot advance(); 4025977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return result; 4035977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 4045977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 4055977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 4065977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Consumes the next token from the JSON stream and asserts that it is a 4075977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * literal null. 4085977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 4095977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * @throws IllegalStateException if the next token is not null or if this 4105977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * reader is closed. 4115977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 4125977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot public void nextNull() throws IOException { 4135977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot peek(); 4145977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (token != JsonToken.NULL) { 4155977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw new IllegalStateException("Expected null but was " + token); 4165977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 4175977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 4185977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot advance(); 4195977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 4205977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 4215977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 4225977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Returns the {@link JsonToken#NUMBER double} value of the next token, 4235977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * consuming it. If the next token is a string, this method will attempt to 4245977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * parse it as a double using {@link Double#parseDouble(String)}. 4255977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 4265977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * @throws IllegalStateException if the next token is not a literal value. 4275977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 4285977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot public double nextDouble() throws IOException { 4295977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot peek(); 4305977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (token != JsonToken.STRING && token != JsonToken.NUMBER) { 4315977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw new IllegalStateException("Expected a double but was " + token); 4325977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 4335977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 4345977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot double result = Double.parseDouble(value); 4355977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot advance(); 4365977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return result; 4375977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 4385977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 4395977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 4405977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Returns the {@link JsonToken#NUMBER long} value of the next token, 4415977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * consuming it. If the next token is a string, this method will attempt to 4425977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * parse it as a long. If the next token's numeric value cannot be exactly 4435977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * represented by a Java {@code long}, this method throws. 4445977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 4455977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * @throws IllegalStateException if the next token is not a literal value. 4465977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * @throws NumberFormatException if the next literal value cannot be parsed 4475977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * as a number, or exactly represented as a long. 4485977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 4495977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot public long nextLong() throws IOException { 4505977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot peek(); 4515977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (token != JsonToken.STRING && token != JsonToken.NUMBER) { 4525977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw new IllegalStateException("Expected a long but was " + token); 4535977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 4545977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 4555977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot long result; 4565977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot try { 4575977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot result = Long.parseLong(value); 4585977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } catch (NumberFormatException ignored) { 4595977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot double asDouble = Double.parseDouble(value); // don't catch this NumberFormatException 4605977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot result = (long) asDouble; 4615977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if ((double) result != asDouble) { 4625977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw new NumberFormatException(value); 4635977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 4645977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 4655977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 4665977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot advance(); 4675977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return result; 4685977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 4695977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 4705977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 4715977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Returns the {@link JsonToken#NUMBER int} value of the next token, 4725977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * consuming it. If the next token is a string, this method will attempt to 4735977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * parse it as an int. If the next token's numeric value cannot be exactly 4745977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * represented by a Java {@code int}, this method throws. 4755977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 4765977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * @throws IllegalStateException if the next token is not a literal value. 4775977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * @throws NumberFormatException if the next literal value cannot be parsed 4785977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * as a number, or exactly represented as an int. 4795977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 4805977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot public int nextInt() throws IOException { 4815977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot peek(); 4825977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (token != JsonToken.STRING && token != JsonToken.NUMBER) { 4835977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw new IllegalStateException("Expected an int but was " + token); 4845977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 4855977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 4865977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot int result; 4875977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot try { 4885977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot result = Integer.parseInt(value); 4895977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } catch (NumberFormatException ignored) { 4905977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot double asDouble = Double.parseDouble(value); // don't catch this NumberFormatException 4915977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot result = (int) asDouble; 4925977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if ((double) result != asDouble) { 4935977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw new NumberFormatException(value); 4945977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 4955977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 4965977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 4975977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot advance(); 4985977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return result; 4995977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 5005977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 5015977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 5025977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Closes this JSON reader and the underlying {@link Reader}. 5035977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 5045977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot public void close() throws IOException { 5055977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot value = null; 5065977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot token = null; 5075977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot stack.clear(); 5085977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot stack.add(JsonScope.CLOSED); 5095977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot in.close(); 5105977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 5115977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 5125977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 5135977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Skips the next value recursively. If it is an object or array, all nested 5145977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * elements are skipped. This method is intended for use when the JSON token 5155977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * stream contains unrecognized or unhandled values. 5165977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 5175977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot public void skipValue() throws IOException { 5185977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot skipping = true; 5195977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot try { 5205977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot int count = 0; 5215977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot do { 5225977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot JsonToken token = advance(); 5235977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (token == JsonToken.BEGIN_ARRAY || token == JsonToken.BEGIN_OBJECT) { 5245977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot count++; 5255977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else if (token == JsonToken.END_ARRAY || token == JsonToken.END_OBJECT) { 5265977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot count--; 5275977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 5285977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } while (count != 0); 5295977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } finally { 5305977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot skipping = false; 5315977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 5325977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 5335977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 5345977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private JsonScope peekStack() { 5355977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return stack.get(stack.size() - 1); 5365977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 5375977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 5385977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private JsonScope pop() { 5395977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return stack.remove(stack.size() - 1); 5405977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 5415977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 5425977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private void push(JsonScope newTop) { 5435977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot stack.add(newTop); 5445977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 5455977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 5465977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 5475977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Replace the value on the top of the stack with the given value. 5485977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 5495977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private void replaceTop(JsonScope newTop) { 5505977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot stack.set(stack.size() - 1, newTop); 5515977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 5525977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 5535977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private JsonToken nextInArray(boolean firstElement) throws IOException { 5545977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (firstElement) { 5555977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot replaceTop(JsonScope.NONEMPTY_ARRAY); 5565977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else { 5575977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /* Look for a comma before each element after the first element. */ 5585977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot switch (nextNonWhitespace()) { 5595977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case ']': 5605977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot pop(); 5615977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return token = JsonToken.END_ARRAY; 5625977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case ';': 5635977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot checkLenient(); // fall-through 5645977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case ',': 5655977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot break; 5665977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot default: 5675977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw syntaxError("Unterminated array"); 5685977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 5695977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 5705977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 5715977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot switch (nextNonWhitespace()) { 5725977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case ']': 5735977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (firstElement) { 5745977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot pop(); 5755977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return token = JsonToken.END_ARRAY; 5765977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 5775977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot // fall-through to handle ",]" 5785977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case ';': 5795977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case ',': 5805977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /* In lenient mode, a 0-length literal means 'null' */ 5815977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot checkLenient(); 5825977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot pos--; 5835977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot value = "null"; 5845977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return token = JsonToken.NULL; 5855977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot default: 5865977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot pos--; 5875977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return nextValue(); 5885977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 5895977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 5905977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 5915977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private JsonToken nextInObject(boolean firstElement) throws IOException { 5925977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /* 5935977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Read delimiters. Either a comma/semicolon separating this and the 5945977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * previous name-value pair, or a close brace to denote the end of the 5955977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * object. 5965977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 5975977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (firstElement) { 5985977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /* Peek to see if this is the empty object. */ 5995977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot switch (nextNonWhitespace()) { 6005977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '}': 6015977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot pop(); 6025977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return token = JsonToken.END_OBJECT; 6035977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot default: 6045977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot pos--; 6055977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 6065977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else { 6075977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot switch (nextNonWhitespace()) { 6085977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '}': 6095977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot pop(); 6105977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return token = JsonToken.END_OBJECT; 6115977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case ';': 6125977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case ',': 6135977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot break; 6145977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot default: 6155977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw syntaxError("Unterminated object"); 6165977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 6175977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 6185977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 6195977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /* Read the name. */ 6205977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot int quote = nextNonWhitespace(); 6215977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot switch (quote) { 6225977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '\'': 6235977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot checkLenient(); // fall-through 6245977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '"': 6255977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot name = nextString((char) quote); 6265977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot break; 6275977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot default: 6285977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot checkLenient(); 6295977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot pos--; 6305977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot name = nextLiteral(false); 6315977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (name.isEmpty()) { 6325977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw syntaxError("Expected name"); 6335977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 6345977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 6355977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 6365977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot replaceTop(JsonScope.DANGLING_NAME); 6375977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return token = JsonToken.NAME; 6385977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 6395977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 6405977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private JsonToken objectValue() throws IOException { 6415977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /* 6425977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Read the name/value separator. Usually a colon ':'. In lenient mode 6435977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * we also accept an equals sign '=', or an arrow "=>". 6445977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 6455977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot switch (nextNonWhitespace()) { 6465977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case ':': 6475977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot break; 6485977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '=': 6495977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot checkLenient(); 6505977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if ((pos < limit || fillBuffer(1)) && buffer[pos] == '>') { 6515977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot pos++; 6525977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 6535977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot break; 6545977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot default: 6555977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw syntaxError("Expected ':'"); 6565977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 6575977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 6585977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot replaceTop(JsonScope.NONEMPTY_OBJECT); 6595977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return nextValue(); 6605977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 6615977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 6625977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private JsonToken nextValue() throws IOException { 6635977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot int c = nextNonWhitespace(); 6645977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot switch (c) { 6655977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '{': 6665977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot push(JsonScope.EMPTY_OBJECT); 6675977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return token = JsonToken.BEGIN_OBJECT; 6685977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 6695977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '[': 6705977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot push(JsonScope.EMPTY_ARRAY); 6715977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return token = JsonToken.BEGIN_ARRAY; 6725977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 6735977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '\'': 6745977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot checkLenient(); // fall-through 6755977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '"': 6765977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot value = nextString((char) c); 6775977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return token = JsonToken.STRING; 6785977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 6795977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot default: 6805977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot pos--; 6815977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return readLiteral(); 6825977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 6835977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 6845977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 6855977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 6865977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Returns true once {@code limit - pos >= minimum}. If the data is 6875977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * exhausted before that many characters are available, this returns 6885977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * false. 6895977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 6905977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private boolean fillBuffer(int minimum) throws IOException { 6915977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (limit != pos) { 6925977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot limit -= pos; 6935977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot System.arraycopy(buffer, pos, buffer, 0, limit); 6945977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else { 6955977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot limit = 0; 6965977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 6975977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 6985977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot pos = 0; 6995977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot int total; 7005977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot while ((total = in.read(buffer, limit, buffer.length - limit)) != -1) { 7015977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot limit += total; 7025977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (limit >= minimum) { 7035977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return true; 7045977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 7055977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 7065977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return false; 7075977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 7085977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 7095977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private int nextNonWhitespace() throws IOException { 7105977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot while (pos < limit || fillBuffer(1)) { 7115977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot int c = buffer[pos++]; 7125977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot switch (c) { 7135977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '\t': 7145977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case ' ': 7155977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '\n': 7165977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '\r': 7175977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot continue; 7185977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 7195977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '/': 7205977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (pos == limit && !fillBuffer(1)) { 7215977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return c; 7225977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 7235977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 7245977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot checkLenient(); 7255977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot char peek = buffer[pos]; 7265977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot switch (peek) { 7275977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '*': 7285977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot // skip a /* c-style comment */ 7295977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot pos++; 7305977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (!skipTo("*/")) { 7315977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw syntaxError("Unterminated comment"); 7325977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 7335977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot pos += 2; 7345977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot continue; 7355977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 7365977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '/': 7375977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot // skip a // end-of-line comment 7385977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot pos++; 7395977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot skipToEndOfLine(); 7405977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot continue; 7415977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 7425977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot default: 7435977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return c; 7445977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 7455977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 7465977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '#': 7475977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /* 7485977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Skip a # hash end-of-line comment. The JSON RFC doesn't 7495977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * specify this behaviour, but it's required to parse 7505977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * existing documents. See http://b/2571423. 7515977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 7525977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot checkLenient(); 7535977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot skipToEndOfLine(); 7545977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot continue; 7555977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 7565977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot default: 7575977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return c; 7585977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 7595977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 7605977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 7615977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw syntaxError("End of input"); 7625977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 7635977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 7645977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private void checkLenient() throws IOException { 7655977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (!lenient) { 7665977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw syntaxError("Use JsonReader.setLenient(true) to accept malformed JSON"); 7675977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 7685977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 7695977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 7705977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 7715977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Advances the position until after the next newline character. If the line 7725977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * is terminated by "\r\n", the '\n' must be consumed as whitespace by the 7735977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * caller. 7745977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 7755977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private void skipToEndOfLine() throws IOException { 7765977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot while (pos < limit || fillBuffer(1)) { 7775977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot char c = buffer[pos++]; 7785977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (c == '\r' || c == '\n') { 7795977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot break; 7805977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 7815977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 7825977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 7835977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 7845977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private boolean skipTo(String toFind) throws IOException { 7855977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot outer: 7865977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot for (; pos + toFind.length() < limit || fillBuffer(toFind.length()); pos++) { 7875977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot for (int c = 0; c < toFind.length(); c++) { 7885977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (buffer[pos + c] != toFind.charAt(c)) { 7895977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot continue outer; 7905977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 7915977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 7925977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return true; 7935977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 7945977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return false; 7955977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 7965977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 7975977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 7985977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Returns the string up to but not including {@code quote}, unescaping any 7995977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * character escape sequences encountered along the way. The opening quote 8005977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * should have already been read. This consumes the closing quote, but does 8015977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * not include it in the returned string. 8025977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 8035977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * @param quote either ' or ". 8045977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * @throws NumberFormatException if any unicode escape sequences are 8055977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * malformed. 8065977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 8075977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private String nextString(char quote) throws IOException { 8085977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot StringBuilder builder = null; 8095977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot do { 8105977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /* the index of the first character not yet appended to the builder. */ 8115977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot int start = pos; 8125977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot while (pos < limit) { 8135977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot int c = buffer[pos++]; 8145977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 8155977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (c == quote) { 8165977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (skipping) { 8175977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return "skipped!"; 8185977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else if (builder == null) { 8195977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return new String(buffer, start, pos - start - 1); 8205977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else { 8215977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot builder.append(buffer, start, pos - start - 1); 8225977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return builder.toString(); 8235977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 8245977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 8255977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else if (c == '\\') { 8265977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (builder == null) { 8275977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot builder = new StringBuilder(); 8285977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 8295977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot builder.append(buffer, start, pos - start - 1); 8305977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot builder.append(readEscapeCharacter()); 8315977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot start = pos; 8325977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 8335977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 8345977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 8355977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (builder == null) { 8365977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot builder = new StringBuilder(); 8375977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 8385977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot builder.append(buffer, start, pos - start); 8395977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } while (fillBuffer(1)); 8405977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 8415977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw syntaxError("Unterminated string"); 8425977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 8435977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 8445977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 8455977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Reads the value up to but not including any delimiter characters. This 8465977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * does not consume the delimiter character. 8475977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 8485977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * @param assignOffsetsOnly true for this method to only set the valuePos 8495977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * and valueLength fields and return a null result. This only works if 8505977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * the literal is short; a string is returned otherwise. 8515977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 8525977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private String nextLiteral(boolean assignOffsetsOnly) throws IOException { 8535977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot StringBuilder builder = null; 8545977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot valuePos = -1; 8555977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot valueLength = 0; 8565977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot int i = 0; 8575977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 8585977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot findNonLiteralCharacter: 8595977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot while (true) { 8605977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot for (; pos + i < limit; i++) { 8615977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot switch (buffer[pos + i]) { 8625977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '/': 8635977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '\\': 8645977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case ';': 8655977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '#': 8665977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '=': 8675977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot checkLenient(); // fall-through 8685977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '{': 8695977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '}': 8705977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '[': 8715977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case ']': 8725977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case ':': 8735977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case ',': 8745977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case ' ': 8755977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '\t': 8765977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '\f': 8775977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '\r': 8785977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '\n': 8795977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot break findNonLiteralCharacter; 8805977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 8815977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 8825977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 8835977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /* 8845977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Attempt to load the entire literal into the buffer at once. If 8855977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * we run out of input, add a non-literal character at the end so 8865977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * that decoding doesn't need to do bounds checks. 8875977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 8885977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (i < buffer.length) { 8895977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (fillBuffer(i + 1)) { 8905977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot continue; 8915977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else { 8925977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot buffer[limit] = '\0'; 8935977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot break; 8945977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 8955977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 8965977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 8975977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot // use a StringBuilder when the value is too long. It must be an unquoted string. 8985977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (builder == null) { 8995977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot builder = new StringBuilder(); 9005977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 9015977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot builder.append(buffer, pos, i); 9025977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot valueLength += i; 9035977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot pos += i; 9045977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot i = 0; 9055977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (!fillBuffer(1)) { 9065977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot break; 9075977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 9085977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 9095977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 9105977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot String result; 9115977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (assignOffsetsOnly && builder == null) { 9125977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot valuePos = pos; 9135977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot result = null; 9145977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else if (skipping) { 9155977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot result = "skipped!"; 9165977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else if (builder == null) { 9175977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot result = new String(buffer, pos, i); 9185977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else { 9195977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot builder.append(buffer, pos, i); 9205977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot result = builder.toString(); 9215977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 9225977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot valueLength += i; 9235977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot pos += i; 9245977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return result; 9255977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 9265977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 9275977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot @Override public String toString() { 9285977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return getClass().getSimpleName() + " near " + getSnippet(); 9295977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 9305977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 9315977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 9325977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Unescapes the character identified by the character or characters that 9335977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * immediately follow a backslash. The backslash '\' should have already 9345977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * been read. This supports both unicode escapes "u000A" and two-character 9355977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * escapes "\n". 9365977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 9375977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * @throws NumberFormatException if any unicode escape sequences are 9385977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * malformed. 9395977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 9405977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private char readEscapeCharacter() throws IOException { 9415977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (pos == limit && !fillBuffer(1)) { 9425977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw syntaxError("Unterminated escape sequence"); 9435977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 9445977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 9455977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot char escaped = buffer[pos++]; 9465977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot switch (escaped) { 9475977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case 'u': 9485977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (pos + 4 > limit && !fillBuffer(4)) { 9495977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw syntaxError("Unterminated escape sequence"); 9505977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 9515977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot String hex = new String(buffer, pos, 4); 9525977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot pos += 4; 9535977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return (char) Integer.parseInt(hex, 16); 9545977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 9555977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case 't': 9565977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return '\t'; 9575977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 9585977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case 'b': 9595977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return '\b'; 9605977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 9615977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case 'n': 9625977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return '\n'; 9635977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 9645977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case 'r': 9655977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return '\r'; 9665977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 9675977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case 'f': 9685977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return '\f'; 9695977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 9705977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '\'': 9715977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '"': 9725977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot case '\\': 9735977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot default: 9745977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return escaped; 9755977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 9765977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 9775977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 9785977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 9795977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Reads a null, boolean, numeric or unquoted string literal value. 9805977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 9815977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private JsonToken readLiteral() throws IOException { 9825977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot value = nextLiteral(true); 9835977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (valueLength == 0) { 9845977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw syntaxError("Expected literal value"); 9855977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 9865977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot token = decodeLiteral(); 9875977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (token == JsonToken.STRING) { 9885977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot checkLenient(); 9895977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 9905977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return token; 9915977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 9925977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 9935977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 9945977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Assigns {@code nextToken} based on the value of {@code nextValue}. 9955977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 9965977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private JsonToken decodeLiteral() throws IOException { 9975977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (valuePos == -1) { 9985977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot // it was too long to fit in the buffer so it can only be a string 9995977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return JsonToken.STRING; 10005977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else if (valueLength == 4 10015977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot && ('n' == buffer[valuePos ] || 'N' == buffer[valuePos ]) 10025977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot && ('u' == buffer[valuePos + 1] || 'U' == buffer[valuePos + 1]) 10035977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot && ('l' == buffer[valuePos + 2] || 'L' == buffer[valuePos + 2]) 10045977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot && ('l' == buffer[valuePos + 3] || 'L' == buffer[valuePos + 3])) { 10055977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot value = "null"; 10065977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return JsonToken.NULL; 10075977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else if (valueLength == 4 10085977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot && ('t' == buffer[valuePos ] || 'T' == buffer[valuePos ]) 10095977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot && ('r' == buffer[valuePos + 1] || 'R' == buffer[valuePos + 1]) 10105977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot && ('u' == buffer[valuePos + 2] || 'U' == buffer[valuePos + 2]) 10115977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot && ('e' == buffer[valuePos + 3] || 'E' == buffer[valuePos + 3])) { 10125977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot value = TRUE; 10135977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return JsonToken.BOOLEAN; 10145977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else if (valueLength == 5 10155977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot && ('f' == buffer[valuePos ] || 'F' == buffer[valuePos ]) 10165977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot && ('a' == buffer[valuePos + 1] || 'A' == buffer[valuePos + 1]) 10175977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot && ('l' == buffer[valuePos + 2] || 'L' == buffer[valuePos + 2]) 10185977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot && ('s' == buffer[valuePos + 3] || 'S' == buffer[valuePos + 3]) 10195977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot && ('e' == buffer[valuePos + 4] || 'E' == buffer[valuePos + 4])) { 10205977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot value = FALSE; 10215977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return JsonToken.BOOLEAN; 10225977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else { 10235977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot value = new String(buffer, valuePos, valueLength); 10245977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return decodeNumber(buffer, valuePos, valueLength); 10255977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 10265977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 10275977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 10285977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 10295977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Determine whether the characters is a JSON number. Numbers are of the 10305977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * form -12.34e+56. Fractional and exponential parts are optional. Leading 10315977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * zeroes are not allowed in the value or exponential part, but are allowed 10325977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * in the fraction. 10335977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * 10345977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * <p>This has a side effect of setting isInteger. 10355977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 10365977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private JsonToken decodeNumber(char[] chars, int offset, int length) { 10375977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot int i = offset; 10385977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot int c = chars[i]; 10395977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 10405977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (c == '-') { 10415977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot c = chars[++i]; 10425977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 10435977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 10445977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (c == '0') { 10455977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot c = chars[++i]; 10465977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else if (c >= '1' && c <= '9') { 10475977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot c = chars[++i]; 10485977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot while (c >= '0' && c <= '9') { 10495977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot c = chars[++i]; 10505977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 10515977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else { 10525977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return JsonToken.STRING; 10535977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 10545977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 10555977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (c == '.') { 10565977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot c = chars[++i]; 10575977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot while (c >= '0' && c <= '9') { 10585977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot c = chars[++i]; 10595977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 10605977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 10615977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 10625977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (c == 'e' || c == 'E') { 10635977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot c = chars[++i]; 10645977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (c == '+' || c == '-') { 10655977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot c = chars[++i]; 10665977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 10675977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (c >= '0' && c <= '9') { 10685977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot c = chars[++i]; 10695977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot while (c >= '0' && c <= '9') { 10705977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot c = chars[++i]; 10715977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 10725977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else { 10735977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return JsonToken.STRING; 10745977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 10755977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 10765977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 10775977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot if (i == offset + length) { 10785977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return JsonToken.NUMBER; 10795977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } else { 10805977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return JsonToken.STRING; 10815977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 10825977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 10835977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 10845977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot /** 10855977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * Throws a new IO exception with the given message and a context snippet 10865977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot * with this reader's content. 10875977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot */ 10885977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot public IOException syntaxError(String message) throws IOException { 10895977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot throw new JsonSyntaxException(message + " near " + getSnippet()); 10905977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 10915977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 10925977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private CharSequence getSnippet() { 10935977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot StringBuilder snippet = new StringBuilder(); 10945977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot int beforePos = Math.min(pos, 20); 10955977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot snippet.append(buffer, pos - beforePos, beforePos); 10965977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot int afterPos = Math.min(limit - pos, 20); 10975977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot snippet.append(buffer, pos, afterPos); 10985977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot return snippet; 10995977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 11005977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot 11015977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private static class JsonSyntaxException extends IOException { 11025977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot private JsonSyntaxException(String s) { 11035977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot super(s); 11045977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 11055977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot } 11065977e94bfe57100042cdf41e476d7cb971137e5fBrett Chabot} 1107