source: other-projects/tipple-android/tipple-lib/src/org/json1/JSONTokener.java@ 26899

Last change on this file since 26899 was 26899, checked in by davidb, 11 years ago

Tipple reborn after Chris's Summer of Code 2013

File size: 12.6 KB
Line 
1package org.json1;
2
3import java.io.BufferedReader;
4import java.io.IOException;
5import java.io.InputStream;
6import java.io.InputStreamReader;
7import java.io.Reader;
8import java.io.StringReader;
9
10/*
11Copyright (c) 2002 JSON.org
12
13Permission is hereby granted, free of charge, to any person obtaining a copy
14of this software and associated documentation files (the "Software"), to deal
15in the Software without restriction, including without limitation the rights
16to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17copies of the Software, and to permit persons to whom the Software is
18furnished to do so, subject to the following conditions:
19
20The above copyright notice and this permission notice shall be included in all
21copies or substantial portions of the Software.
22
23The Software shall be used for Good, not Evil.
24
25THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31SOFTWARE.
32*/
33
34/**
35 * A JSONTokener takes a source string and extracts characters and tokens from
36 * it. It is used by the JSONObject and JSONArray constructors to parse
37 * JSON source strings.
38 * @author JSON.org
39 * @version 2012-02-16
40 */
41public class JSONTokener {
42
43 private long character;
44 private boolean eof;
45 private long index;
46 private long line;
47 private char previous;
48 private Reader reader;
49 private boolean usePrevious;
50
51
52 /**
53 * Construct a JSONTokener from a Reader.
54 *
55 * @param reader A reader.
56 */
57 public JSONTokener(Reader reader) {
58 this.reader = reader.markSupported()
59 ? reader
60 : new BufferedReader(reader);
61 this.eof = false;
62 this.usePrevious = false;
63 this.previous = 0;
64 this.index = 0;
65 this.character = 1;
66 this.line = 1;
67 }
68
69
70 /**
71 * Construct a JSONTokener from an InputStream.
72 */
73 public JSONTokener(InputStream inputStream) throws JSONException {
74 this(new InputStreamReader(inputStream));
75 }
76
77
78 /**
79 * Construct a JSONTokener from a string.
80 *
81 * @param s A source string.
82 */
83 public JSONTokener(String s) {
84 this(new StringReader(s));
85 }
86
87
88 /**
89 * Back up one character. This provides a sort of lookahead capability,
90 * so that you can test for a digit or letter before attempting to parse
91 * the next number or identifier.
92 */
93 public void back() throws JSONException {
94 if (this.usePrevious || this.index <= 0) {
95 throw new JSONException("Stepping back two steps is not supported");
96 }
97 this.index -= 1;
98 this.character -= 1;
99 this.usePrevious = true;
100 this.eof = false;
101 }
102
103
104 /**
105 * Get the hex value of a character (base16).
106 * @param c A character between '0' and '9' or between 'A' and 'F' or
107 * between 'a' and 'f'.
108 * @return An int between 0 and 15, or -1 if c was not a hex digit.
109 */
110 public static int dehexchar(char c) {
111 if (c >= '0' && c <= '9') {
112 return c - '0';
113 }
114 if (c >= 'A' && c <= 'F') {
115 return c - ('A' - 10);
116 }
117 if (c >= 'a' && c <= 'f') {
118 return c - ('a' - 10);
119 }
120 return -1;
121 }
122
123 public boolean end() {
124 return this.eof && !this.usePrevious;
125 }
126
127
128 /**
129 * Determine if the source string still contains characters that next()
130 * can consume.
131 * @return true if not yet at the end of the source.
132 */
133 public boolean more() throws JSONException {
134 this.next();
135 if (this.end()) {
136 return false;
137 }
138 this.back();
139 return true;
140 }
141
142
143 /**
144 * Get the next character in the source string.
145 *
146 * @return The next character, or 0 if past the end of the source string.
147 */
148 public char next() throws JSONException {
149 int c;
150 if (this.usePrevious) {
151 this.usePrevious = false;
152 c = this.previous;
153 } else {
154 try {
155 c = this.reader.read();
156 } catch (IOException exception) {
157 throw new JSONException(exception);
158 }
159
160 if (c <= 0) { // End of stream
161 this.eof = true;
162 c = 0;
163 }
164 }
165 this.index += 1;
166 if (this.previous == '\r') {
167 this.line += 1;
168 this.character = c == '\n' ? 0 : 1;
169 } else if (c == '\n') {
170 this.line += 1;
171 this.character = 0;
172 } else {
173 this.character += 1;
174 }
175 this.previous = (char) c;
176 return this.previous;
177 }
178
179
180 /**
181 * Consume the next character, and check that it matches a specified
182 * character.
183 * @param c The character to match.
184 * @return The character.
185 * @throws JSONException if the character does not match.
186 */
187 public char next(char c) throws JSONException {
188 char n = this.next();
189 if (n != c) {
190 throw this.syntaxError("Expected '" + c + "' and instead saw '" +
191 n + "'");
192 }
193 return n;
194 }
195
196
197 /**
198 * Get the next n characters.
199 *
200 * @param n The number of characters to take.
201 * @return A string of n characters.
202 * @throws JSONException
203 * Substring bounds error if there are not
204 * n characters remaining in the source string.
205 */
206 public String next(int n) throws JSONException {
207 if (n == 0) {
208 return "";
209 }
210
211 char[] chars = new char[n];
212 int pos = 0;
213
214 while (pos < n) {
215 chars[pos] = this.next();
216 if (this.end()) {
217 throw this.syntaxError("Substring bounds error");
218 }
219 pos += 1;
220 }
221 return new String(chars);
222 }
223
224
225 /**
226 * Get the next char in the string, skipping whitespace.
227 * @throws JSONException
228 * @return A character, or 0 if there are no more characters.
229 */
230 public char nextClean() throws JSONException {
231 for (;;) {
232 char c = this.next();
233 if (c == 0 || c > ' ') {
234 return c;
235 }
236 }
237 }
238
239
240 /**
241 * Return the characters up to the next close quote character.
242 * Backslash processing is done. The formal JSON format does not
243 * allow strings in single quotes, but an implementation is allowed to
244 * accept them.
245 * @param quote The quoting character, either
246 * <code>"</code>&nbsp;<small>(double quote)</small> or
247 * <code>'</code>&nbsp;<small>(single quote)</small>.
248 * @return A String.
249 * @throws JSONException Unterminated string.
250 */
251 public String nextString(char quote) throws JSONException {
252 char c;
253 StringBuffer sb = new StringBuffer();
254 for (;;) {
255 c = this.next();
256 switch (c) {
257 case 0:
258 case '\n':
259 case '\r':
260 throw this.syntaxError("Unterminated string");
261 case '\\':
262 c = this.next();
263 switch (c) {
264 case 'b':
265 sb.append('\b');
266 break;
267 case 't':
268 sb.append('\t');
269 break;
270 case 'n':
271 sb.append('\n');
272 break;
273 case 'f':
274 sb.append('\f');
275 break;
276 case 'r':
277 sb.append('\r');
278 break;
279 case 'u':
280 sb.append((char)Integer.parseInt(this.next(4), 16));
281 break;
282 case '"':
283 case '\'':
284 case '\\':
285 case '/':
286 sb.append(c);
287 break;
288 default:
289 throw this.syntaxError("Illegal escape.");
290 }
291 break;
292 default:
293 if (c == quote) {
294 return sb.toString();
295 }
296 sb.append(c);
297 }
298 }
299 }
300
301
302 /**
303 * Get the text up but not including the specified character or the
304 * end of line, whichever comes first.
305 * @param delimiter A delimiter character.
306 * @return A string.
307 */
308 public String nextTo(char delimiter) throws JSONException {
309 StringBuffer sb = new StringBuffer();
310 for (;;) {
311 char c = this.next();
312 if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
313 if (c != 0) {
314 this.back();
315 }
316 return sb.toString().trim();
317 }
318 sb.append(c);
319 }
320 }
321
322
323 /**
324 * Get the text up but not including one of the specified delimiter
325 * characters or the end of line, whichever comes first.
326 * @param delimiters A set of delimiter characters.
327 * @return A string, trimmed.
328 */
329 public String nextTo(String delimiters) throws JSONException {
330 char c;
331 StringBuffer sb = new StringBuffer();
332 for (;;) {
333 c = this.next();
334 if (delimiters.indexOf(c) >= 0 || c == 0 ||
335 c == '\n' || c == '\r') {
336 if (c != 0) {
337 this.back();
338 }
339 return sb.toString().trim();
340 }
341 sb.append(c);
342 }
343 }
344
345
346 /**
347 * Get the next value. The value can be a Boolean, Double, Integer,
348 * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
349 * @throws JSONException If syntax error.
350 *
351 * @return An object.
352 */
353 public Object nextValue() throws JSONException {
354 char c = this.nextClean();
355 String string;
356
357 switch (c) {
358 case '"':
359 case '\'':
360 return this.nextString(c);
361 case '{':
362 this.back();
363 return new JSONObject(this);
364 case '[':
365 this.back();
366 return new JSONArray(this);
367 }
368
369 /*
370 * Handle unquoted text. This could be the values true, false, or
371 * null, or it can be a number. An implementation (such as this one)
372 * is allowed to also accept non-standard forms.
373 *
374 * Accumulate characters until we reach the end of the text or a
375 * formatting character.
376 */
377
378 StringBuffer sb = new StringBuffer();
379 while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
380 sb.append(c);
381 c = this.next();
382 }
383 this.back();
384
385 string = sb.toString().trim();
386 if ("".equals(string)) {
387 throw this.syntaxError("Missing value");
388 }
389 return JSONObject.stringToValue(string);
390 }
391
392
393 /**
394 * Skip characters until the next character is the requested character.
395 * If the requested character is not found, no characters are skipped.
396 * @param to A character to skip to.
397 * @return The requested character, or zero if the requested character
398 * is not found.
399 */
400 public char skipTo(char to) throws JSONException {
401 char c;
402 try {
403 long startIndex = this.index;
404 long startCharacter = this.character;
405 long startLine = this.line;
406 this.reader.mark(1000000);
407 do {
408 c = this.next();
409 if (c == 0) {
410 this.reader.reset();
411 this.index = startIndex;
412 this.character = startCharacter;
413 this.line = startLine;
414 return c;
415 }
416 } while (c != to);
417 } catch (IOException exc) {
418 throw new JSONException(exc);
419 }
420
421 this.back();
422 return c;
423 }
424
425
426 /**
427 * Make a JSONException to signal a syntax error.
428 *
429 * @param message The error message.
430 * @return A JSONException object, suitable for throwing
431 */
432 public JSONException syntaxError(String message) {
433 return new JSONException(message + this.toString());
434 }
435
436
437 /**
438 * Make a printable string of this JSONTokener.
439 *
440 * @return " at {index} [character {character} line {line}]"
441 */
442 public String toString() {
443 return " at " + this.index + " [character " + this.character + " line " +
444 this.line + "]";
445 }
446}
Note: See TracBrowser for help on using the repository browser.