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