176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson/* 276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Copyright (C) 2010 The Android Open Source Project 376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License"); 576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * you may not use this file except in compliance with the License. 676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * You may obtain a copy of the License at 776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * http://www.apache.org/licenses/LICENSE-2.0 976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 1076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Unless required by applicable law or agreed to in writing, software 1176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS, 1276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * See the License for the specific language governing permissions and 1476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * limitations under the License. 1576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 1676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 1776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonpackage android.util; 1876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 19847cf342c956eac3dec03b7b29fcb188ffe8804fJesse Wilsonimport java.io.Closeable; 20eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilsonimport java.io.EOFException; 2176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.io.IOException; 2276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.io.Reader; 2376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.util.ArrayList; 2476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.util.List; 25847cf342c956eac3dec03b7b29fcb188ffe8804fJesse Wilsonimport libcore.internal.StringPool; 2676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 2776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson/** 2876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Reads a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>) 2976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * encoded value as a stream of tokens. This stream includes both literal 3076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * values (strings, numbers, booleans, and nulls) as well as the begin and 3176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * end delimiters of objects and arrays. The tokens are traversed in 3276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * depth-first order, the same order that they appear in the JSON document. 3376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Within JSON objects, name/value pairs are represented by a single token. 3476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 3576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <h3>Parsing JSON</h3> 363312b297600ec97f28f979dfbdf6c95e164a304fJesse Wilson * To create a recursive descent parser for your own JSON streams, first create 373312b297600ec97f28f979dfbdf6c95e164a304fJesse Wilson * an entry point method that creates a {@code JsonReader}. 3876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 3976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <p>Next, create handler methods for each structure in your JSON text. You'll 4076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * need a method for each object type and for each array type. 4176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <ul> 4276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <li>Within <strong>array handling</strong> methods, first call {@link 4376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * #beginArray} to consume the array's opening bracket. Then create a 4476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * while loop that accumulates values, terminating when {@link #hasNext} 4576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * is false. Finally, read the array's closing bracket by calling {@link 4676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * #endArray}. 4776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <li>Within <strong>object handling</strong> methods, first call {@link 4876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * #beginObject} to consume the object's opening brace. Then create a 4976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * while loop that assigns values to local variables based on their name. 5076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * This loop should terminate when {@link #hasNext} is false. Finally, 5176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * read the object's closing brace by calling {@link #endObject}. 5276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * </ul> 5376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <p>When a nested object or array is encountered, delegate to the 5476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * corresponding handler method. 5576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 5676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <p>When an unknown name is encountered, strict parsers should fail with an 5776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * exception. Lenient parsers should call {@link #skipValue()} to recursively 5876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * skip the value's nested tokens, which may otherwise conflict. 5976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 6076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <p>If a value may be null, you should first check using {@link #peek()}. 6176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Null literals can be consumed using either {@link #nextNull()} or {@link 6276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * #skipValue()}. 6376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 6476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <h3>Example</h3> 6576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Suppose we'd like to parse a stream of messages such as the following: <pre> {@code 6676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * [ 6776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * { 6876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "id": 912345678901, 6976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "text": "How do I read JSON on Android?", 7076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "geo": null, 7176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "user": { 7276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "name": "android_newb", 7376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "followers_count": 41 7476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 7576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * }, 7676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * { 7776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "id": 912345678902, 7876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "text": "@android_newb just use android.util.JsonReader!", 7976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "geo": [50.454722, -104.606667], 8076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "user": { 8176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "name": "jesse", 8276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "followers_count": 2 8376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 8476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 8576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * ]}</pre> 8676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * This code implements the parser for the above structure: <pre> {@code 8776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 8876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * public List<Message> readJsonStream(InputStream in) throws IOException { 8976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8")); 909d30ea033329f76ea9b20fa778f04cddd8fe710cJesse Wilson * try { 919d30ea033329f76ea9b20fa778f04cddd8fe710cJesse Wilson * return readMessagesArray(reader); 929d30ea033329f76ea9b20fa778f04cddd8fe710cJesse Wilson * } finally { 939d30ea033329f76ea9b20fa778f04cddd8fe710cJesse Wilson * reader.close(); 949d30ea033329f76ea9b20fa778f04cddd8fe710cJesse Wilson * } 9576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 9676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 9776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * public List<Message> readMessagesArray(JsonReader reader) throws IOException { 9876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * List<Message> messages = new ArrayList<Message>(); 9976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 10076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * reader.beginArray(); 10176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * while (reader.hasNext()) { 10276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * messages.add(readMessage(reader)); 10376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 10476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * reader.endArray(); 10576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * return messages; 10676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 10776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 10876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * public Message readMessage(JsonReader reader) throws IOException { 10976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * long id = -1; 11076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * String text = null; 11176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * User user = null; 11276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * List<Double> geo = null; 11376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 11476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * reader.beginObject(); 11576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * while (reader.hasNext()) { 11676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * String name = reader.nextName(); 11776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * if (name.equals("id")) { 11876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * id = reader.nextLong(); 11976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } else if (name.equals("text")) { 12076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * text = reader.nextString(); 12176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } else if (name.equals("geo") && reader.peek() != JsonToken.NULL) { 12276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * geo = readDoublesArray(reader); 12376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } else if (name.equals("user")) { 12476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * user = readUser(reader); 12576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } else { 12676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * reader.skipValue(); 12776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 12876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 12976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * reader.endObject(); 13076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * return new Message(id, text, user, geo); 13176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 13276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 13376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * public List<Double> readDoublesArray(JsonReader reader) throws IOException { 13476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * List<Double> doubles = new ArrayList<Double>(); 13576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 13676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * reader.beginArray(); 13776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * while (reader.hasNext()) { 13876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * doubles.add(reader.nextDouble()); 13976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 14076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * reader.endArray(); 14176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * return doubles; 14276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 14376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 14476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * public User readUser(JsonReader reader) throws IOException { 14576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * String username = null; 14676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * int followersCount = -1; 14776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 14876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * reader.beginObject(); 14976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * while (reader.hasNext()) { 15076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * String name = reader.nextName(); 15176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * if (name.equals("name")) { 15276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * username = reader.nextString(); 15376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } else if (name.equals("followers_count")) { 15476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * followersCount = reader.nextInt(); 15576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } else { 15676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * reader.skipValue(); 15776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 15876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 15976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * reader.endObject(); 16076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * return new User(username, followersCount); 16176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * }}</pre> 16276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 16376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <h3>Number Handling</h3> 16476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * This reader permits numeric values to be read as strings and string values to 16576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * be read as numbers. For example, both elements of the JSON array {@code 16676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * [1, "1"]} may be read using either {@link #nextInt} or {@link #nextString}. 16776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * This behavior is intended to prevent lossy numeric conversions: double is 16876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * JavaScript's only numeric type and very large values like {@code 16976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 9007199254740993} cannot be represented exactly on that platform. To minimize 17076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * precision loss, extremely large values should be written and read as strings 17176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * in JSON. 17276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 17376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <p>Each {@code JsonReader} may be used to read a single JSON stream. Instances 17476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * of this class are not thread safe. 17576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 17676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonpublic final class JsonReader implements Closeable { 17776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 1789d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson private static final String TRUE = "true"; 1799d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson private static final String FALSE = "false"; 1809d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson 181847cf342c956eac3dec03b7b29fcb188ffe8804fJesse Wilson private final StringPool stringPool = new StringPool(); 182847cf342c956eac3dec03b7b29fcb188ffe8804fJesse Wilson 18376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** The input JSON. */ 18476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private final Reader in; 18576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 1861ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson /** True to accept non-spec compliant JSON */ 1871ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson private boolean lenient = false; 1881ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson 18976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 19076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Use a manual buffer to easily read and unread upcoming characters, and 19176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * also so we can create strings without an intermediate StringBuilder. 1929d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson * We decode literals directly out of this buffer, so it must be at least as 1939d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson * long as the longest token that can be reported as a number. 19476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 19576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private final char[] buffer = new char[1024]; 19676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private int pos = 0; 19776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private int limit = 0; 19876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 199febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson /* 200febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson * The offset of the first character in the buffer. 201febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson */ 202febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson private int bufferStartLine = 1; 203febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson private int bufferStartColumn = 1; 204febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson 20576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private final List<JsonScope> stack = new ArrayList<JsonScope>(); 20676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson { 20776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson push(JsonScope.EMPTY_DOCUMENT); 20876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 20976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 21076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 21176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * The type of the next token to be returned by {@link #peek} and {@link 2129d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson * #advance}. If null, peek() will assign a value. 21376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 21476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private JsonToken token; 21576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 21676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** The text of the next name. */ 21776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private String name; 21876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 2199d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson /* 2209d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson * For the next literal value, we may have the text value, or the position 2219d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson * and length in the buffer. 2229d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson */ 22376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private String value; 2249d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson private int valuePos; 2259d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson private int valueLength; 22676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 227d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson /** True if we're currently handling a skipValue() call. */ 228d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson private boolean skipping = false; 229d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson 23076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 23176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Creates a new instance that reads a JSON-encoded stream from {@code in}. 23276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 23376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonReader(Reader in) { 23476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (in == null) { 23576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new NullPointerException("in == null"); 23676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 23776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson this.in = in; 23876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 23976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 24076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 2411ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * Configure this parser to be be liberal in what it accepts. By default, 2421ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * this parser is strict and only accepts JSON as specified by <a 2431ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the 2441ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * parser to lenient causes it to ignore the following syntax errors: 2451ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * 2461ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * <ul> 2471ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * <li>End of line comments starting with {@code //} or {@code #} and 2481ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * ending with a newline character. 2491ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * <li>C-style comments starting with {@code /*} and ending with 2501ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * {@code *}{@code /}. Such comments may not be nested. 2511ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * <li>Names that are unquoted or {@code 'single quoted'}. 2521ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * <li>Strings that are unquoted or {@code 'single quoted'}. 2531ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * <li>Array elements separated by {@code ;} instead of {@code ,}. 2541ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * <li>Unnecessary array separators. These are interpreted as if null 2551ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * was the omitted value. 2561ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * <li>Names and values separated by {@code =} or {@code =>} instead of 2571ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * {@code :}. 2581ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * <li>Name/value pairs separated by {@code ;} instead of {@code ,}. 2591ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * </ul> 2601ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson */ 2611ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson public void setLenient(boolean lenient) { 2621ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson this.lenient = lenient; 2631ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson } 2641ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson 2651ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson /** 266eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson * Returns true if this parser is liberal in what it accepts. 267eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson */ 268eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson public boolean isLenient() { 269eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson return lenient; 270eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson } 271eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson 272eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson /** 27376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Consumes the next token from the JSON stream and asserts that it is the 27476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * beginning of a new array. 27576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 27676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public void beginArray() throws IOException { 27776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson expect(JsonToken.BEGIN_ARRAY); 27876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 27976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 28076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 28176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Consumes the next token from the JSON stream and asserts that it is the 28276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * end of the current array. 28376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 28476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public void endArray() throws IOException { 28576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson expect(JsonToken.END_ARRAY); 28676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 28776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 28876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 28976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Consumes the next token from the JSON stream and asserts that it is the 29076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * beginning of a new object. 29176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 29276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public void beginObject() throws IOException { 29376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson expect(JsonToken.BEGIN_OBJECT); 29476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 29576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 29676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 29776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Consumes the next token from the JSON stream and asserts that it is the 29876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * end of the current array. 29976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 30076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public void endObject() throws IOException { 30176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson expect(JsonToken.END_OBJECT); 30276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 30376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 30476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 3051ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * Consumes {@code expected}. 30676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 3071ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson private void expect(JsonToken expected) throws IOException { 3089d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson peek(); 3091ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson if (token != expected) { 3101ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson throw new IllegalStateException("Expected " + expected + " but was " + peek()); 31176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 31276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson advance(); 31376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 31476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 31576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 31676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Returns true if the current array or object has another element. 31776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 31876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public boolean hasNext() throws IOException { 3199d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson peek(); 3201ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson return token != JsonToken.END_OBJECT && token != JsonToken.END_ARRAY; 32176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 32276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 32376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 32476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Returns the type of the next token without consuming it. 32576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 32676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonToken peek() throws IOException { 3279d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (token != null) { 3289d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson return token; 32976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 33076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 33176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson switch (peekStack()) { 33276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case EMPTY_DOCUMENT: 33376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson replaceTop(JsonScope.NONEMPTY_DOCUMENT); 33476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson JsonToken firstToken = nextValue(); 335eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson if (!lenient && token != JsonToken.BEGIN_ARRAY && token != JsonToken.BEGIN_OBJECT) { 33676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IOException( 33776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson "Expected JSON document to start with '[' or '{' but was " + token); 33876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 33976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return firstToken; 34076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case EMPTY_ARRAY: 34176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return nextInArray(true); 34276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case NONEMPTY_ARRAY: 34376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return nextInArray(false); 34476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case EMPTY_OBJECT: 34576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return nextInObject(true); 34676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case DANGLING_NAME: 34776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return objectValue(); 34876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case NONEMPTY_OBJECT: 34976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return nextInObject(false); 35076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case NONEMPTY_DOCUMENT: 351eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson try { 352eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson JsonToken token = nextValue(); 353eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson if (lenient) { 354eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson return token; 355eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson } 356eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson throw syntaxError("Expected EOF"); 357eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson } catch (EOFException e) { 358eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson return token = JsonToken.END_DOCUMENT; // TODO: avoid throwing here? 359eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson } 36076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case CLOSED: 36176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IllegalStateException("JsonReader is closed"); 36276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson default: 36376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new AssertionError(); 36476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 36576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 36676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 36776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 36876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Advances the cursor in the JSON stream to the next token. 36976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 37076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private JsonToken advance() throws IOException { 3719d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson peek(); 37276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 37376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson JsonToken result = token; 37476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson token = null; 37576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson value = null; 37676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson name = null; 37776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return result; 37876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 37976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 38076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 38176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Returns the next token, a {@link JsonToken#NAME property name}, and 38276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * consumes it. 38376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 38476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @throws IOException if the next token in the stream is not a property 38576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * name. 38676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 38776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public String nextName() throws IOException { 3889d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson peek(); 3891ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson if (token != JsonToken.NAME) { 39076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IllegalStateException("Expected a name but was " + peek()); 39176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 39276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson String result = name; 39376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson advance(); 39476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return result; 39576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 39676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 39776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 39876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Returns the {@link JsonToken#STRING string} value of the next token, 39976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * consuming it. If the next token is a number, this method will return its 40076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * string form. 40176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 40276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @throws IllegalStateException if the next token is not a string or if 40376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * this reader is closed. 40476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 40576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public String nextString() throws IOException { 4061ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson peek(); 4079d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (token != JsonToken.STRING && token != JsonToken.NUMBER) { 40876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IllegalStateException("Expected a string but was " + peek()); 40976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 41076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 41176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson String result = value; 41276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson advance(); 41376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return result; 41476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 41576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 41676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 41776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Returns the {@link JsonToken#BOOLEAN boolean} value of the next token, 41876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * consuming it. 41976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 42076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @throws IllegalStateException if the next token is not a boolean or if 42176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * this reader is closed. 42276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 42376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public boolean nextBoolean() throws IOException { 4249d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson peek(); 4259d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (token != JsonToken.BOOLEAN) { 4269d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson throw new IllegalStateException("Expected a boolean but was " + token); 42776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 42876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 4299d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson boolean result = (value == TRUE); 43076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson advance(); 43176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return result; 43276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 43376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 43476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 43576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Consumes the next token from the JSON stream and asserts that it is a 43676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * literal null. 43776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 43876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @throws IllegalStateException if the next token is not null or if this 43976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * reader is closed. 44076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 44176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public void nextNull() throws IOException { 4429d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson peek(); 4439d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (token != JsonToken.NULL) { 4449d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson throw new IllegalStateException("Expected null but was " + token); 44576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 44676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 44776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson advance(); 44876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 44976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 45076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 45176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Returns the {@link JsonToken#NUMBER double} value of the next token, 45276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * consuming it. If the next token is a string, this method will attempt to 4539d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson * parse it as a double using {@link Double#parseDouble(String)}. 45476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 45576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @throws IllegalStateException if the next token is not a literal value. 45676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 45776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public double nextDouble() throws IOException { 4589d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson peek(); 4599d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (token != JsonToken.STRING && token != JsonToken.NUMBER) { 4609d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson throw new IllegalStateException("Expected a double but was " + token); 46176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 46276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 46376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson double result = Double.parseDouble(value); 46476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson advance(); 46576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return result; 46676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 46776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 46876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 46976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Returns the {@link JsonToken#NUMBER long} value of the next token, 47076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * consuming it. If the next token is a string, this method will attempt to 47176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * parse it as a long. If the next token's numeric value cannot be exactly 47276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * represented by a Java {@code long}, this method throws. 47376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 47476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @throws IllegalStateException if the next token is not a literal value. 47576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @throws NumberFormatException if the next literal value cannot be parsed 47676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * as a number, or exactly represented as a long. 47776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 47876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public long nextLong() throws IOException { 4799d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson peek(); 4809d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (token != JsonToken.STRING && token != JsonToken.NUMBER) { 4819d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson throw new IllegalStateException("Expected a long but was " + token); 48276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 48376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 48476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson long result; 48576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson try { 48676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson result = Long.parseLong(value); 48776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } catch (NumberFormatException ignored) { 48876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson double asDouble = Double.parseDouble(value); // don't catch this NumberFormatException 48976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson result = (long) asDouble; 49076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if ((double) result != asDouble) { 49176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new NumberFormatException(value); 49276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 49376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 49476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 49576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson advance(); 49676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return result; 49776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 49876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 49976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 50076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Returns the {@link JsonToken#NUMBER int} value of the next token, 50176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * consuming it. If the next token is a string, this method will attempt to 50276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * parse it as an int. If the next token's numeric value cannot be exactly 50376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * represented by a Java {@code int}, this method throws. 50476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 50576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @throws IllegalStateException if the next token is not a literal value. 50676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @throws NumberFormatException if the next literal value cannot be parsed 50776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * as a number, or exactly represented as an int. 50876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 50976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public int nextInt() throws IOException { 5109d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson peek(); 5119d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (token != JsonToken.STRING && token != JsonToken.NUMBER) { 5129d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson throw new IllegalStateException("Expected an int but was " + token); 51376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 51476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 51576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson int result; 51676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson try { 51776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson result = Integer.parseInt(value); 51876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } catch (NumberFormatException ignored) { 51976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson double asDouble = Double.parseDouble(value); // don't catch this NumberFormatException 52076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson result = (int) asDouble; 52176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if ((double) result != asDouble) { 52276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new NumberFormatException(value); 52376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 52476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 52576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 52676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson advance(); 52776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return result; 52876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 52976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 53076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 53176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Closes this JSON reader and the underlying {@link Reader}. 53276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 53376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public void close() throws IOException { 53476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson value = null; 53576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson token = null; 53676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson stack.clear(); 53776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson stack.add(JsonScope.CLOSED); 53876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson in.close(); 53976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 54076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 54176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 54276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Skips the next value recursively. If it is an object or array, all nested 54376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * elements are skipped. This method is intended for use when the JSON token 54476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * stream contains unrecognized or unhandled values. 54576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 54676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public void skipValue() throws IOException { 547d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson skipping = true; 548d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson try { 5498fbcc6b4cdeed1ad119c28327a0f4f0a579cd24bCalin Juravle if (!hasNext() || peek() == JsonToken.END_DOCUMENT) { 5508fbcc6b4cdeed1ad119c28327a0f4f0a579cd24bCalin Juravle throw new IllegalStateException("No element left to skip"); 5518fbcc6b4cdeed1ad119c28327a0f4f0a579cd24bCalin Juravle } 552d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson int count = 0; 553d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson do { 554d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson JsonToken token = advance(); 555d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson if (token == JsonToken.BEGIN_ARRAY || token == JsonToken.BEGIN_OBJECT) { 556d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson count++; 557d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson } else if (token == JsonToken.END_ARRAY || token == JsonToken.END_OBJECT) { 558d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson count--; 559d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson } 560d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson } while (count != 0); 561d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson } finally { 562d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson skipping = false; 563d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson } 56476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 56576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 56676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private JsonScope peekStack() { 56776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return stack.get(stack.size() - 1); 56876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 56976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 57076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private JsonScope pop() { 57176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return stack.remove(stack.size() - 1); 57276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 57376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 57476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private void push(JsonScope newTop) { 57576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson stack.add(newTop); 57676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 57776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 57876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 57976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Replace the value on the top of the stack with the given value. 58076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 58176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private void replaceTop(JsonScope newTop) { 58276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson stack.set(stack.size() - 1, newTop); 58376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 58476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 58576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private JsonToken nextInArray(boolean firstElement) throws IOException { 58676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (firstElement) { 5871ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson replaceTop(JsonScope.NONEMPTY_ARRAY); 58876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } else { 5891ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson /* Look for a comma before each element after the first element. */ 59076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson switch (nextNonWhitespace()) { 59176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case ']': 59276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson pop(); 59376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return token = JsonToken.END_ARRAY; 59476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case ';': 5951ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson checkLenient(); // fall-through 5961ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson case ',': 59776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 59876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson default: 59976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw syntaxError("Unterminated array"); 60076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 60176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 60276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 6031ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson switch (nextNonWhitespace()) { 6041ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson case ']': 6051ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson if (firstElement) { 6061ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson pop(); 6071ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson return token = JsonToken.END_ARRAY; 6081ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson } 6091ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson // fall-through to handle ",]" 6101ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson case ';': 6111ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson case ',': 6121ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson /* In lenient mode, a 0-length literal means 'null' */ 6131ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson checkLenient(); 6141ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson pos--; 6151ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson value = "null"; 6161ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson return token = JsonToken.NULL; 6171ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson default: 6181ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson pos--; 6191ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson return nextValue(); 6201ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson } 62176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 62276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 62376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private JsonToken nextInObject(boolean firstElement) throws IOException { 62476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /* 62576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Read delimiters. Either a comma/semicolon separating this and the 62676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * previous name-value pair, or a close brace to denote the end of the 62776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * object. 62876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 62976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (firstElement) { 63076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /* Peek to see if this is the empty object. */ 63176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson switch (nextNonWhitespace()) { 63276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '}': 63376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson pop(); 63476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return token = JsonToken.END_OBJECT; 63576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson default: 63676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson pos--; 63776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 63876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } else { 63976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson switch (nextNonWhitespace()) { 64076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '}': 64176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson pop(); 64276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return token = JsonToken.END_OBJECT; 64376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case ';': 64476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case ',': 64576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 64676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson default: 64776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw syntaxError("Unterminated object"); 64876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 64976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 65076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 65176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /* Read the name. */ 65276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson int quote = nextNonWhitespace(); 65376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson switch (quote) { 65476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\'': 6551ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson checkLenient(); // fall-through 65676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '"': 65776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson name = nextString((char) quote); 65876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 65976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson default: 6601ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson checkLenient(); 66176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson pos--; 6629d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson name = nextLiteral(false); 66376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (name.isEmpty()) { 66476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw syntaxError("Expected name"); 66576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 66676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 66776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 66876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson replaceTop(JsonScope.DANGLING_NAME); 66976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return token = JsonToken.NAME; 67076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 67176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 67276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private JsonToken objectValue() throws IOException { 67376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /* 6741ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * Read the name/value separator. Usually a colon ':'. In lenient mode 6751ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * we also accept an equals sign '=', or an arrow "=>". 67676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 6771ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson switch (nextNonWhitespace()) { 6781ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson case ':': 6791ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson break; 6801ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson case '=': 6811ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson checkLenient(); 6821ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson if ((pos < limit || fillBuffer(1)) && buffer[pos] == '>') { 6831ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson pos++; 6841ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson } 6851ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson break; 6861ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson default: 6871ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson throw syntaxError("Expected ':'"); 68876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 68976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 69076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson replaceTop(JsonScope.NONEMPTY_OBJECT); 69176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return nextValue(); 69276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 69376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 69476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private JsonToken nextValue() throws IOException { 69576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson int c = nextNonWhitespace(); 69676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson switch (c) { 69776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '{': 69876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson push(JsonScope.EMPTY_OBJECT); 69976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return token = JsonToken.BEGIN_OBJECT; 70076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 70176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '[': 70276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson push(JsonScope.EMPTY_ARRAY); 70376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return token = JsonToken.BEGIN_ARRAY; 70476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 70576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\'': 7061ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson checkLenient(); // fall-through 70776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '"': 70876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson value = nextString((char) c); 70976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return token = JsonToken.STRING; 71076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 71176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson default: 71276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson pos--; 71376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return readLiteral(); 71476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 71576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 71676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 71776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 71876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Returns true once {@code limit - pos >= minimum}. If the data is 71976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * exhausted before that many characters are available, this returns 72076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * false. 72176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 72276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private boolean fillBuffer(int minimum) throws IOException { 723febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson // Before clobbering the old characters, update where buffer starts 724febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson for (int i = 0; i < pos; i++) { 725febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson if (buffer[i] == '\n') { 726febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson bufferStartLine++; 727febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson bufferStartColumn = 1; 728febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson } else { 729febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson bufferStartColumn++; 730febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson } 731febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson } 732febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson 73376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (limit != pos) { 73476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson limit -= pos; 73576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson System.arraycopy(buffer, pos, buffer, 0, limit); 73676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } else { 73776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson limit = 0; 73876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 73976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 74076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson pos = 0; 74176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson int total; 74276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson while ((total = in.read(buffer, limit, buffer.length - limit)) != -1) { 74376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson limit += total; 7447a2c813d304b910b00046115efb0f462e6431a64Jesse Wilson 7457a2c813d304b910b00046115efb0f462e6431a64Jesse Wilson // if this is the first read, consume an optional byte order mark (BOM) if it exists 746d1ad3c2c3a675c4018f0f43577b203858dad767aJesse Wilson if (bufferStartLine == 1 && bufferStartColumn == 1 747d1ad3c2c3a675c4018f0f43577b203858dad767aJesse Wilson && limit > 0 && buffer[0] == '\ufeff') { 7487a2c813d304b910b00046115efb0f462e6431a64Jesse Wilson pos++; 7497a2c813d304b910b00046115efb0f462e6431a64Jesse Wilson bufferStartColumn--; 7507a2c813d304b910b00046115efb0f462e6431a64Jesse Wilson } 7517a2c813d304b910b00046115efb0f462e6431a64Jesse Wilson 75276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (limit >= minimum) { 75376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return true; 75476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 75576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 75676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return false; 75776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 75876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 759febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson private int getLineNumber() { 760febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson int result = bufferStartLine; 761febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson for (int i = 0; i < pos; i++) { 762febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson if (buffer[i] == '\n') { 763febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson result++; 764febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson } 765febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson } 766febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson return result; 767febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson } 768febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson 769febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson private int getColumnNumber() { 770febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson int result = bufferStartColumn; 771febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson for (int i = 0; i < pos; i++) { 772febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson if (buffer[i] == '\n') { 773febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson result = 1; 774febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson } else { 775febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson result++; 776febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson } 777febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson } 778febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson return result; 779febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson } 780febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson 78176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private int nextNonWhitespace() throws IOException { 78276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson while (pos < limit || fillBuffer(1)) { 78376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson int c = buffer[pos++]; 78476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson switch (c) { 78576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\t': 78676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case ' ': 78776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\n': 78876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\r': 78976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson continue; 79076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 79176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '/': 79276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (pos == limit && !fillBuffer(1)) { 79376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return c; 79476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 79576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 7961ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson checkLenient(); 79776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson char peek = buffer[pos]; 79876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson switch (peek) { 79976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '*': 80076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson // skip a /* c-style comment */ 80176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson pos++; 80276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (!skipTo("*/")) { 80376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw syntaxError("Unterminated comment"); 80476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 80576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson pos += 2; 80676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson continue; 80776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 80876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '/': 80976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson // skip a // end-of-line comment 81076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson pos++; 81176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson skipToEndOfLine(); 81276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson continue; 81376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 81476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson default: 81576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return c; 81676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 81776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 81876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '#': 81976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /* 82076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Skip a # hash end-of-line comment. The JSON RFC doesn't 82176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * specify this behaviour, but it's required to parse 82276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * existing documents. See http://b/2571423. 82376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 8241ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson checkLenient(); 82576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson skipToEndOfLine(); 82676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson continue; 82776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 82876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson default: 82976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return c; 83076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 83176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 83276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 833eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson throw new EOFException("End of input"); 83476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 83576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 8361ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson private void checkLenient() throws IOException { 8371ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson if (!lenient) { 8381ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson throw syntaxError("Use JsonReader.setLenient(true) to accept malformed JSON"); 8391ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson } 8401ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson } 8411ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson 84276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 84376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Advances the position until after the next newline character. If the line 84476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * is terminated by "\r\n", the '\n' must be consumed as whitespace by the 84576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * caller. 84676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 84776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private void skipToEndOfLine() throws IOException { 84876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson while (pos < limit || fillBuffer(1)) { 84976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson char c = buffer[pos++]; 85076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (c == '\r' || c == '\n') { 85176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 85276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 85376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 85476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 85576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 85676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private boolean skipTo(String toFind) throws IOException { 85776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson outer: 858d1ad3c2c3a675c4018f0f43577b203858dad767aJesse Wilson for (; pos + toFind.length() <= limit || fillBuffer(toFind.length()); pos++) { 85976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson for (int c = 0; c < toFind.length(); c++) { 86076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (buffer[pos + c] != toFind.charAt(c)) { 86176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson continue outer; 86276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 86376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 86476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return true; 86576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 86676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return false; 86776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 86876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 86976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 87076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Returns the string up to but not including {@code quote}, unescaping any 87176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * character escape sequences encountered along the way. The opening quote 87276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * should have already been read. This consumes the closing quote, but does 87376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * not include it in the returned string. 87476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 87576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @param quote either ' or ". 87676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @throws NumberFormatException if any unicode escape sequences are 87776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * malformed. 87876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 87976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private String nextString(char quote) throws IOException { 88076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson StringBuilder builder = null; 88176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson do { 88276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /* the index of the first character not yet appended to the builder. */ 88376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson int start = pos; 88476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson while (pos < limit) { 88576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson int c = buffer[pos++]; 88676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 88776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (c == quote) { 888d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson if (skipping) { 889d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson return "skipped!"; 890d07fb882f84e9fa7b758870261747456f2752ba5Jesse Wilson } else if (builder == null) { 891847cf342c956eac3dec03b7b29fcb188ffe8804fJesse Wilson return stringPool.get(buffer, start, pos - start - 1); 89276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } else { 89376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson builder.append(buffer, start, pos - start - 1); 89476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return builder.toString(); 89576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 89676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 89776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } else if (c == '\\') { 89876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (builder == null) { 89976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson builder = new StringBuilder(); 90076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 90176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson builder.append(buffer, start, pos - start - 1); 90276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson builder.append(readEscapeCharacter()); 90376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson start = pos; 90476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 90576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 90676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 90776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (builder == null) { 90876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson builder = new StringBuilder(); 90976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 91076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson builder.append(buffer, start, pos - start); 91176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } while (fillBuffer(1)); 91276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 91376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw syntaxError("Unterminated string"); 91476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 91576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 91676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 9179d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson * Reads the value up to but not including any delimiter characters. This 91876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * does not consume the delimiter character. 9199d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson * 9209d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson * @param assignOffsetsOnly true for this method to only set the valuePos 9219d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson * and valueLength fields and return a null result. This only works if 9229d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson * the literal is short; a string is returned otherwise. 92376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 9249d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson private String nextLiteral(boolean assignOffsetsOnly) throws IOException { 92576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson StringBuilder builder = null; 9269d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson valuePos = -1; 9279d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson valueLength = 0; 9289d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson int i = 0; 9299d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson 9309d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson findNonLiteralCharacter: 9319d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson while (true) { 9329d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson for (; pos + i < limit; i++) { 9339d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson switch (buffer[pos + i]) { 9349d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson case '/': 9359d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson case '\\': 9369d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson case ';': 9379d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson case '#': 9389d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson case '=': 9399d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson checkLenient(); // fall-through 9409d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson case '{': 9419d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson case '}': 9429d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson case '[': 9439d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson case ']': 9449d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson case ':': 9459d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson case ',': 9469d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson case ' ': 9479d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson case '\t': 9489d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson case '\f': 9499d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson case '\r': 9509d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson case '\n': 9519d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson break findNonLiteralCharacter; 9529d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } 9539d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } 9549d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson 9559d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson /* 9569d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson * Attempt to load the entire literal into the buffer at once. If 9579d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson * we run out of input, add a non-literal character at the end so 9589d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson * that decoding doesn't need to do bounds checks. 9599d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson */ 9609d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (i < buffer.length) { 9619d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (fillBuffer(i + 1)) { 9629d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson continue; 9639d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } else { 9649d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson buffer[limit] = '\0'; 9659d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson break; 96676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 96776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 96876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 9699d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson // use a StringBuilder when the value is too long. It must be an unquoted string. 97076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (builder == null) { 97176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson builder = new StringBuilder(); 97276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 9739d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson builder.append(buffer, pos, i); 9749d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson valueLength += i; 9759d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson pos += i; 9769d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson i = 0; 9779d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (!fillBuffer(1)) { 9789d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson break; 9799d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } 9809d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } 98176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 9829d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson String result; 9839d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (assignOffsetsOnly && builder == null) { 9849d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson valuePos = pos; 9859d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson result = null; 9869d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } else if (skipping) { 9879d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson result = "skipped!"; 9889d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } else if (builder == null) { 989847cf342c956eac3dec03b7b29fcb188ffe8804fJesse Wilson result = stringPool.get(buffer, pos, i); 9909d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } else { 9919d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson builder.append(buffer, pos, i); 9929d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson result = builder.toString(); 9939d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } 9949d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson valueLength += i; 9959d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson pos += i; 9969d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson return result; 99776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 99876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 99976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson @Override public String toString() { 100076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return getClass().getSimpleName() + " near " + getSnippet(); 100176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 100276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 100376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 100476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Unescapes the character identified by the character or characters that 100576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * immediately follow a backslash. The backslash '\' should have already 100676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * been read. This supports both unicode escapes "u000A" and two-character 100776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * escapes "\n". 100876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 100976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @throws NumberFormatException if any unicode escape sequences are 101076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * malformed. 101176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 101276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private char readEscapeCharacter() throws IOException { 101376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (pos == limit && !fillBuffer(1)) { 101476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw syntaxError("Unterminated escape sequence"); 101576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 101676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 101776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson char escaped = buffer[pos++]; 101876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson switch (escaped) { 101976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case 'u': 102076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (pos + 4 > limit && !fillBuffer(4)) { 102176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw syntaxError("Unterminated escape sequence"); 102276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 1023847cf342c956eac3dec03b7b29fcb188ffe8804fJesse Wilson String hex = stringPool.get(buffer, pos, 4); 102476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson pos += 4; 102576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return (char) Integer.parseInt(hex, 16); 102676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 102776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case 't': 102876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return '\t'; 102976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 103076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case 'b': 103176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return '\b'; 103276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 103376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case 'n': 103476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return '\n'; 103576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 103676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case 'r': 103776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return '\r'; 103876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 103976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case 'f': 104076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return '\f'; 104176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 104276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\'': 104376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '"': 104476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\\': 104576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson default: 104676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return escaped; 104776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 104876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 104976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 105076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 105176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Reads a null, boolean, numeric or unquoted string literal value. 105276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 105376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private JsonToken readLiteral() throws IOException { 10549d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson value = nextLiteral(true); 10559d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (valueLength == 0) { 105676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw syntaxError("Expected literal value"); 105776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 10589d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson token = decodeLiteral(); 10599d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (token == JsonToken.STRING) { 10609d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson checkLenient(); 10619d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } 10629d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson return token; 106376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 106476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 106576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 106676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Assigns {@code nextToken} based on the value of {@code nextValue}. 106776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 10689d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson private JsonToken decodeLiteral() throws IOException { 10699d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (valuePos == -1) { 10709d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson // it was too long to fit in the buffer so it can only be a string 10719d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson return JsonToken.STRING; 10729d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } else if (valueLength == 4 10739d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson && ('n' == buffer[valuePos ] || 'N' == buffer[valuePos ]) 10749d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson && ('u' == buffer[valuePos + 1] || 'U' == buffer[valuePos + 1]) 10759d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson && ('l' == buffer[valuePos + 2] || 'L' == buffer[valuePos + 2]) 10769d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson && ('l' == buffer[valuePos + 3] || 'L' == buffer[valuePos + 3])) { 10779d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson value = "null"; 10789d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson return JsonToken.NULL; 10799d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } else if (valueLength == 4 10809d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson && ('t' == buffer[valuePos ] || 'T' == buffer[valuePos ]) 10819d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson && ('r' == buffer[valuePos + 1] || 'R' == buffer[valuePos + 1]) 10829d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson && ('u' == buffer[valuePos + 2] || 'U' == buffer[valuePos + 2]) 10839d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson && ('e' == buffer[valuePos + 3] || 'E' == buffer[valuePos + 3])) { 10849d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson value = TRUE; 10859d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson return JsonToken.BOOLEAN; 10869d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } else if (valueLength == 5 10879d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson && ('f' == buffer[valuePos ] || 'F' == buffer[valuePos ]) 10889d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson && ('a' == buffer[valuePos + 1] || 'A' == buffer[valuePos + 1]) 10899d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson && ('l' == buffer[valuePos + 2] || 'L' == buffer[valuePos + 2]) 10909d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson && ('s' == buffer[valuePos + 3] || 'S' == buffer[valuePos + 3]) 10919d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson && ('e' == buffer[valuePos + 4] || 'E' == buffer[valuePos + 4])) { 10929d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson value = FALSE; 10939d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson return JsonToken.BOOLEAN; 109476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } else { 1095847cf342c956eac3dec03b7b29fcb188ffe8804fJesse Wilson value = stringPool.get(buffer, valuePos, valueLength); 10969d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson return decodeNumber(buffer, valuePos, valueLength); 10979d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } 10989d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } 10999d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson 11009d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson /** 11019d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson * Determine whether the characters is a JSON number. Numbers are of the 11029d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson * form -12.34e+56. Fractional and exponential parts are optional. Leading 11039d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson * zeroes are not allowed in the value or exponential part, but are allowed 11049d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson * in the fraction. 11059d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson */ 11069d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson private JsonToken decodeNumber(char[] chars, int offset, int length) { 11079d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson int i = offset; 11089d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson int c = chars[i]; 11099d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson 11109d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (c == '-') { 11119d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson c = chars[++i]; 11129d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } 11139d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson 11149d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (c == '0') { 11159d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson c = chars[++i]; 11169d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } else if (c >= '1' && c <= '9') { 11179d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson c = chars[++i]; 11189d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson while (c >= '0' && c <= '9') { 11199d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson c = chars[++i]; 11209d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } 11219d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } else { 11229d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson return JsonToken.STRING; 11239d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } 11249d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson 11259d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (c == '.') { 11269d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson c = chars[++i]; 11279d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson while (c >= '0' && c <= '9') { 11289d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson c = chars[++i]; 11299d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } 11309d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } 11319d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson 11329d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (c == 'e' || c == 'E') { 11339d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson c = chars[++i]; 11349d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (c == '+' || c == '-') { 11359d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson c = chars[++i]; 113676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 11379d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (c >= '0' && c <= '9') { 11389d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson c = chars[++i]; 11399d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson while (c >= '0' && c <= '9') { 11409d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson c = chars[++i]; 11419d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } 11429d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } else { 11439d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson return JsonToken.STRING; 11449d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } 11459d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } 11469d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson 11479d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson if (i == offset + length) { 11489d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson return JsonToken.NUMBER; 11499d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson } else { 11509d9b4e70a1e7f9ffb6cedd8a86fdd926f2a28202Jesse Wilson return JsonToken.STRING; 115176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 115276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 115376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 115476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 115576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Throws a new IO exception with the given message and a context snippet 115676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * with this reader's content. 115776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 1158eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson private IOException syntaxError(String message) throws IOException { 1159febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson throw new MalformedJsonException(message 1160febae4ed18953098fec2126c7f883213e14469c9Jesse Wilson + " at line " + getLineNumber() + " column " + getColumnNumber()); 116176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 116276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 116376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private CharSequence getSnippet() { 116476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson StringBuilder snippet = new StringBuilder(); 116576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson int beforePos = Math.min(pos, 20); 116676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson snippet.append(buffer, pos - beforePos, beforePos); 116776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson int afterPos = Math.min(limit - pos, 20); 116876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson snippet.append(buffer, pos, afterPos); 116976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return snippet; 117076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 117176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson} 1172