1 /*******************************************************************************
\r
2 * ============LICENSE_START==================================================
\r
4 * * ===========================================================================
\r
5 * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
\r
6 * * ===========================================================================
\r
7 * * Licensed under the Apache License, Version 2.0 (the "License");
\r
8 * * you may not use this file except in compliance with the License.
\r
9 * * You may obtain a copy of the License at
\r
11 * * http://www.apache.org/licenses/LICENSE-2.0
\r
13 * * Unless required by applicable law or agreed to in writing, software
\r
14 * * distributed under the License is distributed on an "AS IS" BASIS,
\r
15 * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
16 * * See the License for the specific language governing permissions and
\r
17 * * limitations under the License.
\r
18 * * ============LICENSE_END====================================================
\r
20 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
\r
22 ******************************************************************************/
\r
25 import java.io.BufferedReader;
\r
26 import java.io.IOException;
\r
27 import java.io.InputStream;
\r
28 import java.io.InputStreamReader;
\r
29 import java.io.Reader;
\r
30 import java.io.StringReader;
\r
34 public class JSONTokener {
\r
36 private long character;
\r
37 private boolean eof;
\r
40 private char previous;
\r
41 private Reader reader;
\r
42 private boolean usePrevious;
\r
46 * Construct a JSONTokener from a Reader.
\r
48 * @param reader A reader.
\r
50 public JSONTokener(Reader reader) {
\r
51 this.reader = reader.markSupported()
\r
53 : new BufferedReader(reader);
\r
55 this.usePrevious = false;
\r
64 * Construct a JSONTokener from an InputStream.
\r
66 public JSONTokener(InputStream inputStream) throws JSONException {
\r
67 this(new InputStreamReader(inputStream));
\r
72 * Construct a JSONTokener from a string.
\r
74 * @param s A source string.
\r
76 public JSONTokener(String s) {
\r
77 this(new StringReader(s));
\r
82 * Back up one character. This provides a sort of lookahead capability,
\r
83 * so that you can test for a digit or letter before attempting to parse
\r
84 * the next number or identifier.
\r
86 public void back() throws JSONException {
\r
87 if (this.usePrevious || this.index <= 0) {
\r
88 throw new JSONException("Stepping back two steps is not supported");
\r
91 this.character -= 1;
\r
92 this.usePrevious = true;
\r
98 * Get the hex value of a character (base16).
\r
99 * @param c A character between '0' and '9' or between 'A' and 'F' or
\r
100 * between 'a' and 'f'.
\r
101 * @return An int between 0 and 15, or -1 if c was not a hex digit.
\r
103 public static int dehexchar(char c) {
\r
104 if (c >= '0' && c <= '9') {
\r
107 if (c >= 'A' && c <= 'F') {
\r
108 return c - ('A' - 10);
\r
110 if (c >= 'a' && c <= 'f') {
\r
111 return c - ('a' - 10);
\r
116 public boolean end() {
\r
117 return this.eof && !this.usePrevious;
\r
122 * Determine if the source string still contains characters that next()
\r
124 * @return true if not yet at the end of the source.
\r
126 public boolean more() throws JSONException {
\r
137 * Get the next character in the source string.
\r
139 * @return The next character, or 0 if past the end of the source string.
\r
141 public char next() throws JSONException {
\r
143 if (this.usePrevious) {
\r
144 this.usePrevious = false;
\r
148 c = this.reader.read();
\r
149 } catch (IOException exception) {
\r
150 throw new JSONException(exception);
\r
153 if (c <= 0) { // End of stream
\r
159 if (this.previous == '\r') {
\r
161 this.character = c == '\n' ? 0 : 1;
\r
162 } else if (c == '\n') {
\r
164 this.character = 0;
\r
166 this.character += 1;
\r
168 this.previous = (char) c;
\r
169 return this.previous;
\r
174 * Consume the next character, and check that it matches a specified
\r
176 * @param c The character to match.
\r
177 * @return The character.
\r
178 * @throws JSONException if the character does not match.
\r
180 public char next(char c) throws JSONException {
\r
181 char n = this.next();
\r
183 throw this.syntaxError("Expected '" + c + "' and instead saw '" +
\r
191 * Get the next n characters.
\r
193 * @param n The number of characters to take.
\r
194 * @return A string of n characters.
\r
195 * @throws JSONException
\r
196 * Substring bounds error if there are not
\r
197 * n characters remaining in the source string.
\r
199 public String next(int n) throws JSONException {
\r
204 char[] chars = new char[n];
\r
208 chars[pos] = this.next();
\r
210 throw this.syntaxError("Substring bounds error");
\r
214 return new String(chars);
\r
219 * Get the next char in the string, skipping whitespace.
\r
220 * @throws JSONException
\r
221 * @return A character, or 0 if there are no more characters.
\r
223 public char nextClean() throws JSONException {
\r
225 char c = this.next();
\r
226 if (c == 0 || c > ' ') {
\r
234 * Return the characters up to the next close quote character.
\r
235 * Backslash processing is done. The formal JSON format does not
\r
236 * allow strings in single quotes, but an implementation is allowed to
\r
238 * @param quote The quoting character, either
\r
239 * <code>"</code> <small>(double quote)</small> or
\r
240 * <code>'</code> <small>(single quote)</small>.
\r
241 * @return A String.
\r
242 * @throws JSONException Unterminated string.
\r
244 public String nextString(char quote) throws JSONException {
\r
246 StringBuffer sb = new StringBuffer();
\r
253 throw this.syntaxError("Unterminated string");
\r
273 sb.append((char)Integer.parseInt(this.next(4), 16));
\r
282 throw this.syntaxError("Illegal escape.");
\r
287 return sb.toString();
\r
296 * Get the text up but not including the specified character or the
\r
297 * end of line, whichever comes first.
\r
298 * @param delimiter A delimiter character.
\r
299 * @return A string.
\r
301 public String nextTo(char delimiter) throws JSONException {
\r
302 StringBuffer sb = new StringBuffer();
\r
304 char c = this.next();
\r
305 if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
\r
309 return sb.toString().trim();
\r
317 * Get the text up but not including one of the specified delimiter
\r
318 * characters or the end of line, whichever comes first.
\r
319 * @param delimiters A set of delimiter characters.
\r
320 * @return A string, trimmed.
\r
322 public String nextTo(String delimiters) throws JSONException {
\r
324 StringBuffer sb = new StringBuffer();
\r
327 if (delimiters.indexOf(c) >= 0 || c == 0 ||
\r
328 c == '\n' || c == '\r') {
\r
332 return sb.toString().trim();
\r
340 * Get the next value. The value can be a Boolean, Double, Integer,
\r
341 * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
\r
342 * @throws JSONException If syntax error.
\r
344 * @return An object.
\r
346 public Object nextValue() throws JSONException {
\r
347 char c = this.nextClean();
\r
353 return this.nextString(c);
\r
356 return new JSONObject(this);
\r
359 return new JSONArray(this);
\r
363 * Handle unquoted text. This could be the values true, false, or
\r
364 * null, or it can be a number. An implementation (such as this one)
\r
365 * is allowed to also accept non-standard forms.
\r
367 * Accumulate characters until we reach the end of the text or a
\r
368 * formatting character.
\r
371 StringBuffer sb = new StringBuffer();
\r
372 while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
\r
378 string = sb.toString().trim();
\r
379 if ("".equals(string)) {
\r
380 throw this.syntaxError("Missing value");
\r
382 return JSONObject.stringToValue(string);
\r
387 * Skip characters until the next character is the requested character.
\r
388 * If the requested character is not found, no characters are skipped.
\r
389 * @param to A character to skip to.
\r
390 * @return The requested character, or zero if the requested character
\r
393 public char skipTo(char to) throws JSONException {
\r
396 long startIndex = this.index;
\r
397 long startCharacter = this.character;
\r
398 long startLine = this.line;
\r
399 this.reader.mark(1000000);
\r
403 this.reader.reset();
\r
404 this.index = startIndex;
\r
405 this.character = startCharacter;
\r
406 this.line = startLine;
\r
410 } catch (IOException exc) {
\r
411 throw new JSONException(exc);
\r
420 * Make a JSONException to signal a syntax error.
\r
422 * @param message The error message.
\r
423 * @return A JSONException object, suitable for throwing
\r
425 public JSONException syntaxError(String message) {
\r
426 return new JSONException(message + this.toString());
\r
431 * Make a printable string of this JSONTokener.
\r
433 * @return " at {index} [character {character} line {line}]"
\r
435 public String toString() {
\r
436 return " at " + this.index + " [character " + this.character + " line " +
\r