comparison lib/org.json_2.0/src/org/json/JSONTokener.java @ 0:db87c1b7eb6d

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