* This class performed a lenient parse. For a strict parse, use * {@link JSONStrictTokener}. *
* @author JSON.org * @version 2012-02-16 */ public class JSONTokener extends Scanner { /** * The maximum number of keys in the key pool. */ private static final int keyPoolSize = 100; /** * Key pooling is like string interning, but without permanently tying up * memory. To help conserve memory, storage of duplicated key strings in * JSONObjects will be avoided by using a key pool to manage unique key * string objects. This is used by {@link #nextKey()}. */ private final Map" (double quote) or
* ' (single quote).
* @return A String.
* @throws JSONException Unterminated string.
*/
public String nextString(char quote) throws JSONException {
char c;
StringBuilder sb = new StringBuilder();
if((quote != '"') && (quote != '\'')) {
throw new JSONException("Unexpected string delimiter");
}
for (;;) {
c = this.next();
switch (c) {
case 0:
case '\n':
case '\r':
throw this.syntaxError("Unterminated string");
case '\\':
c = this.next();
switch (c) {
case 'b':
sb.append('\b');
break;
case 't':
sb.append('\t');
break;
case 'n':
sb.append('\n');
break;
case 'f':
sb.append('\f');
break;
case 'r':
sb.append('\r');
break;
case 'u':
try {
sb.append((char)Integer.parseInt(this.next(4), 16));
} catch (NumberFormatException e) {
throw this.syntaxError("Illegal escape.", e);
}
break;
case '"':
case '\'':
case '\\':
case '/':
sb.append(c);
break;
default:
throw this.syntaxError("Illegal escape.");
}
break;
case '"':
case '\'':
if (c == quote) {
return sb.toString();
} else {
sb.append(c);
break;
}
default:
sb.append(c);
}
}
}
/**
* Get the next key. This would usually be a quoted String value.
* Unquoted forms are also allowed.
*
* @throws JSONException If syntax error.
*
* @return An object.
*/
public String nextKey() throws JSONException {
char c = this.nextClean();
String string;
switch (c) {
case '"':
case '\'':
return pooledKey(this.nextString(c));
case '{':
this.back();
throw this.syntaxError("Expected key, found object");
case '[':
this.back();
throw this.syntaxError("Expected key, found array");
}
/*
* Handle unquoted text. This could be the values true, false, or
* null, or it can be a number. An implementation (such as this one)
* is allowed to also accept non-standard forms.
*
* Accumulate characters until we reach the end of the text or a
* formatting character.
*/
StringBuilder sb = new StringBuilder();
while ((c >= ' ') && (UNQUOTED_DELIMITERS.indexOf(c) < 0)) {
sb.append(c);
c = this.next();
}
this.back();
string = sb.toString().trim();
if (string.length() == 0) {
throw this.syntaxError("Missing value");
}
return pooledKey(string);
}
/**
* Add the given key to the key pool, if necessary, and return the
* canonical key.
*
* @param key the key to be pooled
* @return the canonical key
*/
protected String pooledKey(String key) {
String pooled = keyPool.get(key);
if (pooled == null) {
// canonical form, if required, eg. new String(key)
pooled = key;
keyPool.put(pooled, pooled);
}
return pooled;
}
/**
* Get the next value. The value can be a Boolean, Double, Integer,
* JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
* @throws JSONException If syntax error.
*
* @return An object.
*/
public Object nextValue() throws JSONException {
char c = this.nextClean();
String string;
switch (c) {
case '"':
case '\'':
return this.nextString(c);
case '{':
this.back();
return new JSONObject(this);
case '[':
this.back();
return new JSONArray(this);
}
/*
* Handle unquoted text. This could be the values true, false, or
* null, or it can be a number. An implementation (such as this one)
* is allowed to also accept non-standard forms.
*
* Accumulate characters until we reach the end of the text or a
* formatting character.
*/
StringBuilder sb = new StringBuilder();
while (c >= ' ' && UNQUOTED_DELIMITERS.indexOf(c) < 0) {
sb.append(c);
c = this.next();
}
this.back();
string = sb.toString().trim();
if ("".equals(string)) {
throw this.syntaxError("Missing value");
}
return JSONObject.stringToValue(string);
}
/**
* Make a printable string of this Scanner.
*
* @return "JSONTokener at {index} [character {character} line {line}]"
*/
@Override
public String toString() {
return "JSONTokener" + super.toString();
}
/**
* Subclass of LinkedHashMap that acts as a simple LRU cache.
*/
private static final class LRUHashMap