/*
* Copyright 1999-2101 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.zbus.common.json.parser;
import static org.zbus.common.json.parser.JSONToken.COLON;
import static org.zbus.common.json.parser.JSONToken.COMMA;
import static org.zbus.common.json.parser.JSONToken.EOF;
import static org.zbus.common.json.parser.JSONToken.ERROR;
import static org.zbus.common.json.parser.JSONToken.LBRACE;
import static org.zbus.common.json.parser.JSONToken.LBRACKET;
import static org.zbus.common.json.parser.JSONToken.LITERAL_STRING;
import static org.zbus.common.json.parser.JSONToken.LPAREN;
import static org.zbus.common.json.parser.JSONToken.RBRACE;
import static org.zbus.common.json.parser.JSONToken.RBRACKET;
import static org.zbus.common.json.parser.JSONToken.RPAREN;
import java.io.Closeable;
import java.lang.ref.SoftReference;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashSet;
import org.zbus.common.json.JSON;
import org.zbus.common.json.JSONException;
/**
* @author wenshao<szujobs@hotmail.com>
*/
public abstract class JSONLexerBase implements JSONLexer, Closeable {
protected void lexError(String key, Object... args) {
token = ERROR;
}
protected int token;
protected int pos;
protected int features = JSON.DEFAULT_PARSER_FEATURE;
protected char ch;
protected int bp;
protected int eofPos;
/**
* A character buffer for literals.
*/
protected char[] sbuf;
protected int sp;
/**
* number start position
*/
protected int np;
protected boolean hasSpecial;
protected Calendar calendar = null;
public int matchStat = UNKOWN;
private final static ThreadLocal<SoftReference<char[]>> SBUF_REF_LOCAL = new ThreadLocal<SoftReference<char[]>>();
protected Keywords keywods = Keywords.DEFAULT_KEYWORDS;
public JSONLexerBase(){
SoftReference<char[]> sbufRef = SBUF_REF_LOCAL.get();
if (sbufRef != null) {
sbuf = sbufRef.get();
SBUF_REF_LOCAL.set(null);
}
if (sbuf == null) {
sbuf = new char[64];
}
}
public final int matchStat() {
return matchStat;
}
public final void nextToken() {
sp = 0;
for (;;) {
pos = bp;
if (ch == '"') {
scanString();
return;
}
if (ch == ',') {
next();
token = COMMA;
return;
}
if (ch >= '0' && ch <= '9') {
scanNumber();
return;
}
if (ch == '-') {
scanNumber();
return;
}
switch (ch) {
case '\'':
if (!isEnabled(Feature.AllowSingleQuotes)) {
throw new JSONException("Feature.AllowSingleQuotes is false");
}
scanStringSingleQuote();
return;
case ' ':
case '\t':
case '\b':
case '\f':
case '\n':
case '\r':
next();
break;
case 't': // true
scanTrue();
return;
case 'T': // true
scanTreeSet();
return;
case 'S': // set
scanSet();
return;
case 'f': // false
scanFalse();
return;
case 'n': // new,null
scanNullOrNew();
return;
case 'u': // new,null
scanUndefined();
return;
case '(':
next();
token = LPAREN;
return;
case ')':
next();
token = RPAREN;
return;
case '[':
next();
token = LBRACKET;
return;
case ']':
next();
token = RBRACKET;
return;
case '{':
next();
token = LBRACE;
return;
case '}':
next();
token = RBRACE;
return;
case ':':
next();
token = COLON;
return;
default:
if (isEOF()) { // JLS
if (token == EOF) {
throw new JSONException("EOF error");
}
token = EOF;
pos = bp = eofPos;
} else {
lexError("illegal.char", String.valueOf((int) ch));
next();
}
return;
}
}
}
public final void nextToken(int expect) {
sp = 0;
for (;;) {
switch (expect) {
case JSONToken.LBRACE:
if (ch == '{') {
token = JSONToken.LBRACE;
next();
return;
}
if (ch == '[') {
token = JSONToken.LBRACKET;
next();
return;
}
break;
case JSONToken.COMMA:
if (ch == ',') {
token = JSONToken.COMMA;
next();
return;
}
if (ch == '}') {
token = JSONToken.RBRACE;
next();
return;
}
if (ch == ']') {
token = JSONToken.RBRACKET;
next();
return;
}
if (ch == EOI) {
token = JSONToken.EOF;
return;
}
break;
case JSONToken.LITERAL_INT:
if (ch >= '0' && ch <= '9') {
pos = bp;
scanNumber();
return;
}
if (ch == '"') {
pos = bp;
scanString();
return;
}
if (ch == '[') {
token = JSONToken.LBRACKET;
next();
return;
}
if (ch == '{') {
token = JSONToken.LBRACE;
next();
return;
}
break;
case JSONToken.LITERAL_STRING:
if (ch == '"') {
pos = bp;
scanString();
return;
}
if (ch >= '0' && ch <= '9') {
pos = bp;
scanNumber();
return;
}
if (ch == '[') {
token = JSONToken.LBRACKET;
next();
return;
}
if (ch == '{') {
token = JSONToken.LBRACE;
next();
return;
}
break;
case JSONToken.LBRACKET:
if (ch == '[') {
token = JSONToken.LBRACKET;
next();
return;
}
if (ch == '{') {
token = JSONToken.LBRACE;
next();
return;
}
break;
case JSONToken.RBRACKET:
if (ch == ']') {
token = JSONToken.RBRACKET;
next();
return;
}
case JSONToken.EOF:
if (ch == EOI) {
token = JSONToken.EOF;
return;
}
break;
case JSONToken.IDENTIFIER:
nextIdent();
return;
default:
break;
}
if (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f' || ch == '\b') {
next();
continue;
}
nextToken();
break;
}
}
public final void nextIdent() {
while (isWhitespace(ch)) {
next();
}
if (ch == '_' || Character.isLetter(ch)) {
scanIdent();
} else {
nextToken();
}
}
public final void nextTokenWithColon() {
nextTokenWithChar(':');
}
public final void nextTokenWithComma() {
nextTokenWithChar(':');
}
public final void nextTokenWithChar(char expect) {
sp = 0;
for (;;) {
if (ch == expect) {
next();
nextToken();
return;
}
if (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f' || ch == '\b') {
next();
continue;
}
throw new JSONException("not match " + expect + " - " + ch);
}
}
public final int token() {
return token;
}
public final String tokenName() {
return JSONToken.name(token);
}
public final int pos() {
return pos;
}
public final int getBufferPosition() {
return bp;
}
public final String stringDefaultValue() {
if (this.isEnabled(Feature.InitStringFieldAsEmpty)) {
return "";
}
return null;
}
public final Number integerValue() throws NumberFormatException {
long result = 0;
boolean negative = false;
if (np == -1) {
np = 0;
}
int i = np, max = np + sp;
long limit;
long multmin;
int digit;
char type = ' ';
switch (charAt(max - 1)) {
case 'L':
max--;
type = 'L';
break;
case 'S':
max--;
type = 'S';
break;
case 'B':
max--;
type = 'B';
break;
default:
break;
}
if (charAt(np) == '-') {
negative = true;
limit = Long.MIN_VALUE;
i++;
} else {
limit = -Long.MAX_VALUE;
}
multmin = negative ? MULTMIN_RADIX_TEN : N_MULTMAX_RADIX_TEN;
if (i < max) {
digit = digits[charAt(i++)];
result = -digit;
}
while (i < max) {
// Accumulating negatively avoids surprises near MAX_VALUE
digit = digits[charAt(i++)];
if (result < multmin) {
return new BigInteger(numberString());
}
result *= 10;
if (result < limit + digit) {
return new BigInteger(numberString());
}
result -= digit;
}
if (negative) {
if (i > np + 1) {
if (result >= Integer.MIN_VALUE && type != 'L') {
if (type == 'S') {
return (short) result;
}
if (type == 'B') {
return (byte) result;
}
return (int) result;
}
return result;
} else { /* Only got "-" */
throw new NumberFormatException(numberString());
}
} else {
result = -result;
if (result <= Integer.MAX_VALUE && type != 'L') {
if (type == 'S') {
return (short) result;
}
if (type == 'B') {
return (byte) result;
}
return (int) result;
}
return result;
}
}
public final void nextTokenWithColon(int expect) {
nextTokenWithChar(':');
}
public final void nextTokenWithComma(int expect) {
nextTokenWithChar(',');
}
public final void nextTokenWithChar(char seperator, int expect) {
sp = 0;
for (;;) {
if (ch == seperator) {
next();
break;
}
if (isWhitespace(ch)) {
next();
continue;
}
throw new JSONException("not match " + expect + " - " + ch);
}
for (;;) {
if (expect == JSONToken.LITERAL_INT) {
if (ch >= '0' && ch <= '9') {
pos = bp;
scanNumber();
return;
}
if (ch == '"') {
pos = bp;
scanString();
return;
}
} else if (expect == JSONToken.LITERAL_STRING) {
if (ch == '"') {
pos = bp;
scanString();
return;
}
if (ch >= '0' && ch <= '9') {
pos = bp;
scanNumber();
return;
}
} else if (expect == JSONToken.LBRACE) {
if (ch == '{') {
token = JSONToken.LBRACE;
next();
return;
}
if (ch == '[') {
token = JSONToken.LBRACKET;
next();
return;
}
} else if (expect == JSONToken.LBRACKET) {
if (ch == '[') {
token = JSONToken.LBRACKET;
next();
return;
}
if (ch == '{') {
token = JSONToken.LBRACE;
next();
return;
}
}
if (isWhitespace(ch)) {
next();
continue;
}
nextToken();
break;
}
}
public float floatValue() {
return Float.parseFloat(numberString());
}
public double doubleValue() {
return Double.parseDouble(numberString());
}
public void config(Feature feature, boolean state) {
features = Feature.config(features, feature, state);
}
public final boolean isEnabled(Feature feature) {
return Feature.isEnabled(this.features, feature);
}
public abstract String numberString();
public abstract boolean isEOF();
public final char getCurrent() {
return ch;
}
public abstract char charAt(int index);
public abstract char next();
public final String scanSymbol(final SymbolTable symbolTable) {
skipWhitespace();
if (ch == '"') {
return scanSymbol(symbolTable, '"');
}
if (ch == '\'') {
if (!isEnabled(Feature.AllowSingleQuotes)) {
throw new JSONException("syntax error");
}
return scanSymbol(symbolTable, '\'');
}
if (ch == '}') {
next();
token = JSONToken.RBRACE;
return null;
}
if (ch == ',') {
next();
token = JSONToken.COMMA;
return null;
}
if (ch == EOI) {
token = JSONToken.EOF;
return null;
}
if (!isEnabled(Feature.AllowUnQuotedFieldNames)) {
throw new JSONException("syntax error");
}
return scanSymbolUnQuoted(symbolTable);
}
// public abstract String scanSymbol(final SymbolTable symbolTable, final char quote);
protected abstract void arrayCopy(int srcPos, char[] dest, int destPos, int length);
public final String scanSymbol(final SymbolTable symbolTable, final char quote) {
int hash = 0;
np = bp;
sp = 0;
boolean hasSpecial = false;
char chLocal;
for (;;) {
chLocal = next();
if (chLocal == quote) {
break;
}
if (chLocal == EOI) {
throw new JSONException("unclosed.str");
}
if (chLocal == '\\') {
if (!hasSpecial) {
hasSpecial = true;
if (sp >= sbuf.length) {
int newCapcity = sbuf.length * 2;
if (sp > newCapcity) {
newCapcity = sp;
}
char[] newsbuf = new char[newCapcity];
System.arraycopy(sbuf, 0, newsbuf, 0, sbuf.length);
sbuf = newsbuf;
}
// text.getChars(np + 1, np + 1 + sp, sbuf, 0);
// System.arraycopy(this.buf, np + 1, sbuf, 0, sp);
arrayCopy(np + 1, sbuf, 0, sp);
}
chLocal = next();
switch (chLocal) {
case '0':
hash = 31 * hash + (int) chLocal;
putChar('\0');
break;
case '1':
hash = 31 * hash + (int) chLocal;
putChar('\1');
break;
case '2':
hash = 31 * hash + (int) chLocal;
putChar('\2');
break;
case '3':
hash = 31 * hash + (int) chLocal;
putChar('\3');
break;
case '4':
hash = 31 * hash + (int) chLocal;
putChar('\4');
break;
case '5':
hash = 31 * hash + (int) chLocal;
putChar('\5');
break;
case '6':
hash = 31 * hash + (int) chLocal;
putChar('\6');
break;
case '7':
hash = 31 * hash + (int) chLocal;
putChar('\7');
break;
case 'b': // 8
hash = 31 * hash + (int) '\b';
putChar('\b');
break;
case 't': // 9
hash = 31 * hash + (int) '\t';
putChar('\t');
break;
case 'n': // 10
hash = 31 * hash + (int) '\n';
putChar('\n');
break;
case 'v': // 11
hash = 31 * hash + (int) '\u000B';
putChar('\u000B');
break;
case 'f': // 12
case 'F':
hash = 31 * hash + (int) '\f';
putChar('\f');
break;
case 'r': // 13
hash = 31 * hash + (int) '\r';
putChar('\r');
break;
case '"': // 34
hash = 31 * hash + (int) '"';
putChar('"');
break;
case '\'': // 39
hash = 31 * hash + (int) '\'';
putChar('\'');
break;
case '/': // 47
hash = 31 * hash + (int) '/';
putChar('/');
break;
case '\\': // 92
hash = 31 * hash + (int) '\\';
putChar('\\');
break;
case 'x':
char x1 = ch = next();
char x2 = ch = next();
int x_val = digits[x1] * 16 + digits[x2];
char x_char = (char) x_val;
hash = 31 * hash + (int) x_char;
putChar(x_char);
break;
case 'u':
char c1 = chLocal = next();
char c2 = chLocal = next();
char c3 = chLocal = next();
char c4 = chLocal = next();
int val = Integer.parseInt(new String(new char[] { c1, c2, c3, c4 }), 16);
hash = 31 * hash + val;
putChar((char) val);
break;
default:
this.ch = chLocal;
throw new JSONException("unclosed.str.lit");
}
continue;
}
hash = 31 * hash + chLocal;
if (!hasSpecial) {
sp++;
continue;
}
if (sp == sbuf.length) {
putChar(chLocal);
} else {
sbuf[sp++] = chLocal;
}
}
token = LITERAL_STRING;
String value;
if (!hasSpecial) {
// return this.text.substring(np + 1, np + 1 + sp).intern();
int offset;
if (np == -1) {
offset = 0;
} else {
offset = np + 1;
}
value = addSymbol(offset, sp, hash, symbolTable);
} else {
value = symbolTable.addSymbol(sbuf, 0, sp, hash);
}
sp = 0;
this.next();
return value;
}
public final void resetStringPosition() {
this.sp = 0;
}
public final String scanSymbolUnQuoted(final SymbolTable symbolTable) {
final boolean[] firstIdentifierFlags = CharTypes.firstIdentifierFlags;
final char first = ch;
final boolean firstFlag = ch >= firstIdentifierFlags.length || firstIdentifierFlags[first];
if (!firstFlag) {
throw new JSONException("illegal identifier : " + ch);
}
final boolean[] identifierFlags = CharTypes.identifierFlags;
int hash = first;
np = bp;
sp = 1;
char chLocal;
for (;;) {
chLocal = next();
if (chLocal < identifierFlags.length) {
if (!identifierFlags[chLocal]) {
break;
}
}
hash = 31 * hash + chLocal;
sp++;
continue;
}
this.ch = charAt(bp);
token = JSONToken.IDENTIFIER;
final int NULL_HASH = 3392903;
if (sp == 4 && hash == NULL_HASH && charAt(np) == 'n' && charAt(np + 1) == 'u' && charAt(np + 2) == 'l'
&& charAt(np + 3) == 'l') {
return null;
}
// return text.substring(np, np + sp).intern();
return this.addSymbol(np, sp, hash, symbolTable);
// return symbolTable.addSymbol(buf, np, sp, hash);
}
protected abstract void copyTo(int offset, int count, char[] dest);
public final void scanString() {
np = bp;
hasSpecial = false;
char ch;
for (;;) {
ch = next();
if (ch == '\"') {
break;
}
if (ch == EOI) {
throw new JSONException("unclosed string : " + ch);
}
if (ch == '\\') {
if (!hasSpecial) {
hasSpecial = true;
if (sp >= sbuf.length) {
int newCapcity = sbuf.length * 2;
if (sp > newCapcity) {
newCapcity = sp;
}
char[] newsbuf = new char[newCapcity];
System.arraycopy(sbuf, 0, newsbuf, 0, sbuf.length);
sbuf = newsbuf;
}
copyTo(np + 1, sp, sbuf);
// text.getChars(np + 1, np + 1 + sp, sbuf, 0);
// System.arraycopy(buf, np + 1, sbuf, 0, sp);
}
ch = next();
switch (ch) {
case '0':
putChar('\0');
break;
case '1':
putChar('\1');
break;
case '2':
putChar('\2');
break;
case '3':
putChar('\3');
break;
case '4':
putChar('\4');
break;
case '5':
putChar('\5');
break;
case '6':
putChar('\6');
break;
case '7':
putChar('\7');
break;
case 'b': // 8
putChar('\b');
break;
case 't': // 9
putChar('\t');
break;
case 'n': // 10
putChar('\n');
break;
case 'v': // 11
putChar('\u000B');
break;
case 'f': // 12
case 'F':
putChar('\f');
break;
case 'r': // 13
putChar('\r');
break;
case '"': // 34
putChar('"');
break;
case '\'': // 39
putChar('\'');
break;
case '/': // 47
putChar('/');
break;
case '\\': // 92
putChar('\\');
break;
case 'x':
char x1 = ch = next();
char x2 = ch = next();
int x_val = digits[x1] * 16 + digits[x2];
char x_char = (char) x_val;
putChar(x_char);
break;
case 'u':
char u1 = ch = next();
char u2 = ch = next();
char u3 = ch = next();
char u4 = ch = next();
int val = Integer.parseInt(new String(new char[] { u1, u2, u3, u4 }), 16);
putChar((char) val);
break;
default:
this.ch = ch;
throw new JSONException("unclosed string : " + ch);
}
continue;
}
if (!hasSpecial) {
sp++;
continue;
}
if (sp == sbuf.length) {
putChar(ch);
} else {
sbuf[sp++] = ch;
}
}
token = JSONToken.LITERAL_STRING;
this.ch = next();
}
public Calendar getCalendar() {
return this.calendar;
}
public final int intValue() {
int result = 0;
boolean negative = false;
int i = np, max = np + sp;
int limit;
int multmin;
int digit;
if (charAt(np) == '-') {
negative = true;
limit = Integer.MIN_VALUE;
i++;
} else {
limit = -Integer.MAX_VALUE;
}
multmin = negative ? INT_MULTMIN_RADIX_TEN : INT_N_MULTMAX_RADIX_TEN;
if (i < max) {
digit = digits[charAt(i++)];
result = -digit;
}
while (i < max) {
// Accumulating negatively avoids surprises near MAX_VALUE
char chLocal = charAt(i++);
if (chLocal == 'L' || chLocal == 'S' || chLocal == 'B') {
break;
}
digit = digits[chLocal];
if (result < multmin) {
throw new NumberFormatException(numberString());
}
result *= 10;
if (result < limit + digit) {
throw new NumberFormatException(numberString());
}
result -= digit;
}
if (negative) {
if (i > np + 1) {
return result;
} else { /* Only got "-" */
throw new NumberFormatException(numberString());
}
} else {
return -result;
}
}
public abstract byte[] bytesValue();
public void close() {
if (sbuf.length <= 1024 * 8) {
SBUF_REF_LOCAL.set(new SoftReference<char[]>(sbuf));
}
this.sbuf = null;
}
public final boolean isRef() {
if (sp != 4) {
return false;
}
return charAt(np + 1) == '$' && charAt(np + 2) == 'r' && charAt(np + 3) == 'e' && charAt(np + 4) == 'f';
}
protected final static char[] typeFieldName = ("\"" + JSON.DEFAULT_TYPE_KEY + "\":\"").toCharArray();
public int scanType(String type) {
matchStat = UNKOWN;
if (!charArrayCompare(typeFieldName)) {
return NOT_MATCH_NAME;
}
int bpLocal = this.bp + typeFieldName.length;
final int typeLength = type.length();
for (int i = 0; i < typeLength; ++i) {
if (type.charAt(i) != charAt(bpLocal + i)) {
return NOT_MATCH;
}
}
bpLocal += typeLength;
if (charAt(bpLocal) != '"') {
return NOT_MATCH;
}
this.ch = charAt(++bpLocal);
if (ch == ',') {
this.ch = charAt(++bpLocal);
this.bp = bpLocal;
token = JSONToken.COMMA;
return VALUE;
} else if (ch == '}') {
ch = charAt(++bpLocal);
if (ch == ',') {
token = JSONToken.COMMA;
this.ch = charAt(++bpLocal);
} else if (ch == ']') {
token = JSONToken.RBRACKET;
this.ch = charAt(++bpLocal);
} else if (ch == '}') {
token = JSONToken.RBRACE;
this.ch = charAt(++bpLocal);
} else if (ch == EOI) {
token = JSONToken.EOF;
} else {
return NOT_MATCH;
}
matchStat = END;
}
this.bp = bpLocal;
return matchStat;
}
public final boolean matchField(char[] fieldName) {
if (!charArrayCompare(fieldName)) {
return false;
}
bp = bp + fieldName.length;
ch = charAt(bp);
if (ch == '{') {
next();
token = JSONToken.LBRACE;
} else if (ch == '[') {
next();
token = JSONToken.LBRACKET;
} else {
nextToken();
}
return true;
}
public abstract int indexOf(char ch, int startIndex);
public abstract String addSymbol(int offset, int len, int hash, final SymbolTable symbolTable);
public String scanFieldString(char[] fieldName) {
matchStat = UNKOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return stringDefaultValue();
}
// int index = bp + fieldName.length;
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
if (chLocal != '"') {
matchStat = NOT_MATCH;
return stringDefaultValue();
}
boolean hasSpecial = false;
final String strVal;
{
int startIndex = bp + fieldName.length + 1;
int endIndex = indexOf('"', startIndex);
if (endIndex == -1) {
throw new JSONException("unclosed str");
}
int startIndex2 = bp + fieldName.length + 1; // must re compute
String stringVal = subString(startIndex2, endIndex - startIndex2);
for (int i = bp + fieldName.length + 1; i < endIndex; ++i) {
if (charAt(i) == '\\') {
hasSpecial = true;
break;
}
}
if (hasSpecial) {
matchStat = NOT_MATCH;
return stringDefaultValue();
}
offset += (endIndex - (bp + fieldName.length + 1) + 1);
chLocal = charAt(bp + (offset++));
strVal = stringVal;
}
if (chLocal == ',') {
bp += (offset - 1);
this.next();
matchStat = VALUE;
return strVal;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += (offset - 1);
this.next();
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += (offset - 1);
this.next();
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += (offset - 1);
this.next();
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return stringDefaultValue();
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return stringDefaultValue();
}
return strVal;
}
public String scanString(char expectNextChar) {
matchStat = UNKOWN;
int offset = 0;
char chLocal = charAt(bp + (offset++));
if (chLocal == 'n') {
if (charAt(bp + offset) == 'u' && charAt(bp + offset + 1) == 'l' && charAt(bp + offset + 2) == 'l') {
offset += 3;
chLocal = charAt(bp + (offset++));
} else {
matchStat = NOT_MATCH;
return null;
}
if (chLocal == expectNextChar) {
bp += (offset - 1);
this.next();
matchStat = VALUE;
return null;
} else {
matchStat = NOT_MATCH;
return null;
}
}
if (chLocal != '"') {
matchStat = NOT_MATCH;
return stringDefaultValue();
}
boolean hasSpecial = false;
final String strVal;
{
int startIndex = bp + 1;
int endIndex = indexOf('"', startIndex);
if (endIndex == -1) {
throw new JSONException("unclosed str");
}
String stringVal = subString(bp + 1, endIndex - startIndex);
for (int i = bp + 1; i < endIndex; ++i) {
if (charAt(i) == '\\') {
hasSpecial = true;
break;
}
}
if (hasSpecial) {
matchStat = NOT_MATCH;
return stringDefaultValue();
}
offset += (endIndex - (bp + 1) + 1);
chLocal = charAt(bp + (offset++));
strVal = stringVal;
}
if (chLocal == expectNextChar) {
bp += (offset - 1);
this.next();
matchStat = VALUE;
return strVal;
} else {
matchStat = NOT_MATCH;
return strVal;
}
}
public String scanFieldSymbol(char[] fieldName, final SymbolTable symbolTable) {
matchStat = UNKOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return null;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
if (chLocal != '"') {
matchStat = NOT_MATCH;
return null;
}
String strVal;
// int start = index;
int hash = 0;
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal == '\"') {
// bp = index;
// this.ch = chLocal = charAt(bp);
int start = bp + fieldName.length + 1;
int len = bp + offset - start - 1;
strVal = addSymbol(start, len, hash, symbolTable);
chLocal = charAt(bp + (offset++));
break;
}
hash = 31 * hash + chLocal;
if (chLocal == '\\') {
matchStat = NOT_MATCH;
return null;
}
}
if (chLocal == ',') {
bp += (offset - 1);
this.next();
matchStat = VALUE;
return strVal;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += (offset - 1);
this.next();
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += (offset - 1);
this.next();
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += (offset - 1);
this.next();
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return null;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return null;
}
return strVal;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public Enum<?> scanEnum(Class<?> enumClass, final SymbolTable symbolTable, char serperator) {
String name = scanSymbolWithSeperator(symbolTable, serperator);
if (name == null) {
return null;
}
return Enum.valueOf((Class<? extends Enum>) enumClass, name);
}
public String scanSymbolWithSeperator(final SymbolTable symbolTable, char serperator) {
matchStat = UNKOWN;
int offset = 0;
char chLocal = charAt(bp + (offset++));
if (chLocal == 'n') {
if (charAt(bp + offset) == 'u' && charAt(bp + offset + 1) == 'l' && charAt(bp + offset + 2) == 'l') {
offset += 3;
chLocal = charAt(bp + (offset++));
} else {
matchStat = NOT_MATCH;
return null;
}
if (chLocal == serperator) {
bp += (offset - 1);
this.next();
matchStat = VALUE;
return null;
} else {
matchStat = NOT_MATCH;
return null;
}
}
if (chLocal != '"') {
matchStat = NOT_MATCH;
return null;
}
String strVal;
// int start = index;
int hash = 0;
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal == '\"') {
// bp = index;
// this.ch = chLocal = charAt(bp);
int start = bp + 0 + 1;
int len = bp + offset - start - 1;
strVal = addSymbol(start, len, hash, symbolTable);
chLocal = charAt(bp + (offset++));
break;
}
hash = 31 * hash + chLocal;
if (chLocal == '\\') {
matchStat = NOT_MATCH;
return null;
}
}
if (chLocal == serperator) {
bp += (offset - 1);
this.next();
matchStat = VALUE;
return strVal;
} else {
matchStat = NOT_MATCH;
return strVal;
}
}
@SuppressWarnings("unchecked")
public Collection<String> scanFieldStringArray(char[] fieldName, Class<?> type) {
matchStat = UNKOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return null;
}
Collection<String> list;
if (type.isAssignableFrom(HashSet.class)) {
list = new HashSet<String>();
} else if (type.isAssignableFrom(ArrayList.class)) {
list = new ArrayList<String>();
} else {
try {
list = (Collection<String>) type.newInstance();
} catch (Exception e) {
throw new JSONException(e.getMessage(), e);
}
}
// int index = bp + fieldName.length;
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
if (chLocal != '[') {
matchStat = NOT_MATCH;
return null;
}
chLocal = charAt(bp + (offset++));
for (;;) {
if (chLocal != '"') {
matchStat = NOT_MATCH;
return null;
}
String strVal;
// int start = index;
int startOffset = offset;
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal == '\"') {
int start = bp + startOffset;
int len = bp + offset - start - 1;
strVal = subString(start, len);
list.add(strVal);
chLocal = charAt(bp + (offset++));
break;
}
if (chLocal == '\\') {
matchStat = NOT_MATCH;
return null;
}
}
if (chLocal == ',') {
chLocal = charAt(bp + (offset++));
continue;
}
if (chLocal == ']') {
chLocal = charAt(bp + (offset++));
break;
}
matchStat = NOT_MATCH;
return null;
}
if (chLocal == ',') {
bp += (offset - 1);
this.next();
matchStat = VALUE;
return list;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += (offset - 1);
this.next();
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += (offset - 1);
this.next();
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += (offset - 1);
this.next();
} else if (chLocal == EOI) {
bp += (offset - 1);
token = JSONToken.EOF;
this.ch = EOI;
} else {
matchStat = NOT_MATCH;
return null;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return null;
}
return list;
}
@SuppressWarnings("unchecked")
public Collection<String> scanStringArray(Class<?> type, char seperator) {
matchStat = UNKOWN;
Collection<String> list;
if (type.isAssignableFrom(HashSet.class)) {
list = new HashSet<String>();
} else if (type.isAssignableFrom(ArrayList.class)) {
list = new ArrayList<String>();
} else {
try {
list = (Collection<String>) type.newInstance();
} catch (Exception e) {
throw new JSONException(e.getMessage(), e);
}
}
int offset = 0;
char chLocal = charAt(bp + (offset++));
if (chLocal == 'n') {
if (charAt(bp + offset) == 'u' && charAt(bp + offset + 1) == 'l' && charAt(bp + offset + 2) == 'l') {
offset += 3;
chLocal = charAt(bp + (offset++));
} else {
matchStat = NOT_MATCH;
return null;
}
if (chLocal == seperator) {
bp += (offset - 1);
this.next();
matchStat = VALUE;
return null;
} else {
matchStat = NOT_MATCH;
return null;
}
}
if (chLocal != '[') {
matchStat = NOT_MATCH;
return null;
}
chLocal = charAt(bp + (offset++));
for (;;) {
if (chLocal == 'n' //
&& charAt(bp + offset) == 'u' //
&& charAt(bp + offset + 1) == 'l' //
&& charAt(bp + offset + 2) == 'l') {
offset += 3;
chLocal = charAt(bp + (offset++));
} else if (chLocal != '"') {
matchStat = NOT_MATCH;
return null;
} else {
String strVal;
// int start = index;
int startOffset = offset;
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal == '\"') {
int start = bp + startOffset;
int len = bp + offset - start - 1;
strVal = subString(start, len);
list.add(strVal);
chLocal = charAt(bp + (offset++));
break;
}
if (chLocal == '\\') {
matchStat = NOT_MATCH;
return null;
}
}
}
if (chLocal == ',') {
chLocal = charAt(bp + (offset++));
continue;
}
if (chLocal == ']') {
chLocal = charAt(bp + (offset++));
break;
}
matchStat = NOT_MATCH;
return null;
}
if (chLocal == seperator) {
bp += (offset - 1);
this.next();
matchStat = VALUE;
return list;
} else {
matchStat = NOT_MATCH;
return list;
}
}
public int scanFieldInt(char[] fieldName) {
matchStat = UNKOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return 0;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
int value;
if (chLocal >= '0' && chLocal <= '9') {
value = digits[chLocal];
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
value = value * 10 + digits[chLocal];
} else if (chLocal == '.') {
matchStat = NOT_MATCH;
return 0;
} else {
break;
}
}
if (value < 0) {
matchStat = NOT_MATCH;
return 0;
}
} else {
matchStat = NOT_MATCH;
return 0;
}
if (chLocal == ',') {
bp += (offset - 1);
this.next();
matchStat = VALUE;
token = JSONToken.COMMA;
return value;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += (offset - 1);
this.next();
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += (offset - 1);
this.next();
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += (offset - 1);
this.next();
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return 0;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return 0;
}
return value;
}
public boolean scanBoolean(char expectNext) {
matchStat = UNKOWN;
int offset = 0;
char chLocal = charAt(bp + (offset++));
boolean value = false;
if (chLocal == 't') {
if (charAt(bp + offset) == 'r' //
&& charAt(bp + offset + 1) == 'u' //
&& charAt(bp + offset + 2) == 'e') {
offset += 3;
chLocal = charAt(bp + (offset++));
value = true;
} else {
matchStat = NOT_MATCH;
return false;
}
} else if (chLocal == 'f') {
if (charAt(bp + offset) == 'a' //
&& charAt(bp + offset + 1) == 'l' //
&& charAt(bp + offset + 2) == 's' //
&& charAt(bp + offset + 3) == 'e') {
offset += 4;
chLocal = charAt(bp + (offset++));
value = false;
} else {
matchStat = NOT_MATCH;
return false;
}
}
if (chLocal == expectNext) {
bp += (offset - 1);
this.next();
matchStat = VALUE;
return value;
} else {
matchStat = NOT_MATCH;
return value;
}
}
public int scanInt(char expectNext) {
matchStat = UNKOWN;
int offset = 0;
char chLocal = charAt(bp + (offset++));
int value;
if (chLocal >= '0' && chLocal <= '9') {
value = digits[chLocal];
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
value = value * 10 + digits[chLocal];
} else if (chLocal == '.') {
matchStat = NOT_MATCH;
return 0;
} else {
break;
}
}
if (value < 0) {
matchStat = NOT_MATCH;
return 0;
}
} else {
matchStat = NOT_MATCH;
return 0;
}
if (chLocal == expectNext) {
bp += (offset - 1);
this.next();
matchStat = VALUE;
token = JSONToken.COMMA;
return value;
} else {
matchStat = NOT_MATCH;
return value;
}
}
public boolean scanFieldBoolean(char[] fieldName) {
matchStat = UNKOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return false;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
boolean value;
if (chLocal == 't') {
if (charAt(bp + (offset++)) != 'r') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(bp + (offset++)) != 'u') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(bp + (offset++)) != 'e') {
matchStat = NOT_MATCH;
return false;
}
value = true;
} else if (chLocal == 'f') {
if (charAt(bp + (offset++)) != 'a') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(bp + (offset++)) != 'l') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(bp + (offset++)) != 's') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(bp + (offset++)) != 'e') {
matchStat = NOT_MATCH;
return false;
}
value = false;
} else {
matchStat = NOT_MATCH;
return false;
}
chLocal = charAt(bp + offset++);
if (chLocal == ',') {
bp += (offset - 1);
this.next();
matchStat = VALUE;
token = JSONToken.COMMA;
return value;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += (offset - 1);
this.next();
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += (offset - 1);
this.next();
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += (offset - 1);
this.next();
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return false;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return false;
}
return value;
}
public long scanFieldLong(char[] fieldName) {
matchStat = UNKOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return 0;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
long value;
if (chLocal >= '0' && chLocal <= '9') {
value = digits[chLocal];
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
value = value * 10 + digits[chLocal];
} else if (chLocal == '.') {
matchStat = NOT_MATCH;
return 0;
} else {
break;
}
}
if (value < 0) {
matchStat = NOT_MATCH;
return 0;
}
} else {
matchStat = NOT_MATCH;
return 0;
}
if (chLocal == ',') {
bp += (offset - 1);
this.next();
matchStat = VALUE;
token = JSONToken.COMMA;
return value;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += (offset - 1);
this.next();
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += (offset - 1);
this.next();
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += (offset - 1);
this.next();
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return 0;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return 0;
}
return value;
}
public long scanLong(char expectNextChar) {
matchStat = UNKOWN;
int offset = 0;
char chLocal = charAt(bp + (offset++));
long value;
if (chLocal >= '0' && chLocal <= '9') {
value = digits[chLocal];
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
value = value * 10 + digits[chLocal];
} else if (chLocal == '.') {
matchStat = NOT_MATCH;
return 0;
} else {
break;
}
}
if (value < 0) {
matchStat = NOT_MATCH;
return 0;
}
} else {
matchStat = NOT_MATCH;
return 0;
}
if (chLocal == expectNextChar) {
bp += (offset - 1);
this.next();
matchStat = VALUE;
token = JSONToken.COMMA;
return value;
} else {
matchStat = NOT_MATCH;
return value;
}
}
public final float scanFieldFloat(char[] fieldName) {
matchStat = UNKOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return 0;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
float value;
if (chLocal >= '0' && chLocal <= '9') {
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
continue;
} else {
break;
}
}
if (chLocal == '.') {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
continue;
} else {
break;
}
}
} else {
matchStat = NOT_MATCH;
return 0;
}
}
int start = bp + fieldName.length;
int count = bp + offset - start - 1;
String text = this.subString(start, count);
value = Float.parseFloat(text);
} else {
matchStat = NOT_MATCH;
return 0;
}
if (chLocal == ',') {
bp += (offset - 1);
this.next();
matchStat = VALUE;
token = JSONToken.COMMA;
return value;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += (offset - 1);
this.next();
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += (offset - 1);
this.next();
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += (offset - 1);
this.next();
} else if (chLocal == EOI) {
bp += (offset - 1);
token = JSONToken.EOF;
ch = EOI;
} else {
matchStat = NOT_MATCH;
return 0;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return 0;
}
return value;
}
public final float scanFloat(char seperator) {
matchStat = UNKOWN;
int offset = 0;
char chLocal = charAt(bp + (offset++));
float value;
if (chLocal >= '0' && chLocal <= '9') {
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
continue;
} else {
break;
}
}
if (chLocal == '.') {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
continue;
} else {
break;
}
}
} else {
matchStat = NOT_MATCH;
return 0;
}
}
int start = bp;
int count = bp + offset - start - 1;
String text = this.subString(start, count);
value = Float.parseFloat(text);
} else {
matchStat = NOT_MATCH;
return 0;
}
if (chLocal == seperator) {
bp += (offset - 1);
this.next();
matchStat = VALUE;
token = JSONToken.COMMA;
return value;
} else {
matchStat = NOT_MATCH;
return value;
}
}
public final double scanFieldDouble(char[] fieldName) {
matchStat = UNKOWN;
if (!charArrayCompare(fieldName)) {
matchStat = NOT_MATCH_NAME;
return 0;
}
int offset = fieldName.length;
char chLocal = charAt(bp + (offset++));
double value;
if (chLocal >= '0' && chLocal <= '9') {
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
continue;
} else {
break;
}
}
if (chLocal == '.') {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
continue;
} else {
break;
}
}
} else {
matchStat = NOT_MATCH;
return 0;
}
}
if (chLocal == 'e' || chLocal == 'E') {
chLocal = charAt(bp + (offset++));
if (chLocal == '+' || chLocal == '-') {
chLocal = charAt(bp + (offset++));
}
for (;;) {
if (chLocal >= '0' && chLocal <= '9') {
chLocal = charAt(bp + (offset++));
} else {
break;
}
}
}
int start = bp + fieldName.length;
int count = bp + offset - start - 1;
String text = this.subString(start, count);
value = Double.parseDouble(text);
} else {
matchStat = NOT_MATCH;
return 0;
}
if (chLocal == ',') {
bp += (offset - 1);
this.next();
matchStat = VALUE;
token = JSONToken.COMMA;
return value;
}
if (chLocal == '}') {
chLocal = charAt(bp + (offset++));
if (chLocal == ',') {
token = JSONToken.COMMA;
bp += (offset - 1);
this.next();
} else if (chLocal == ']') {
token = JSONToken.RBRACKET;
bp += (offset - 1);
this.next();
} else if (chLocal == '}') {
token = JSONToken.RBRACE;
bp += (offset - 1);
this.next();
} else if (chLocal == EOI) {
token = JSONToken.EOF;
bp += (offset - 1);
ch = EOI;
} else {
matchStat = NOT_MATCH;
return 0;
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return 0;
}
return value;
}
public final double scanFieldDouble(char seperator) {
matchStat = UNKOWN;
int offset = 0;
char chLocal = charAt(bp + (offset++));
double value;
if (chLocal >= '0' && chLocal <= '9') {
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
continue;
} else {
break;
}
}
if (chLocal == '.') {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
for (;;) {
chLocal = charAt(bp + (offset++));
if (chLocal >= '0' && chLocal <= '9') {
continue;
} else {
break;
}
}
} else {
matchStat = NOT_MATCH;
return 0;
}
}
if (chLocal == 'e' || chLocal == 'E') {
chLocal = charAt(bp + (offset++));
if (chLocal == '+' || chLocal == '-') {
chLocal = charAt(bp + (offset++));
}
for (;;) {
if (chLocal >= '0' && chLocal <= '9') {
chLocal = charAt(bp + (offset++));
} else {
break;
}
}
}
int start = bp;
int count = bp + offset - start - 1;
String text = this.subString(start, count);
value = Double.parseDouble(text);
} else {
matchStat = NOT_MATCH;
return 0;
}
if (chLocal == seperator) {
bp += (offset - 1);
this.next();
matchStat = VALUE;
token = JSONToken.COMMA;
return value;
} else {
matchStat = NOT_MATCH;
return value;
}
}
public final void scanTrue() {
if (ch != 't') {
throw new JSONException("error parse true");
}
next();
if (ch != 'r') {
throw new JSONException("error parse true");
}
next();
if (ch != 'u') {
throw new JSONException("error parse true");
}
next();
if (ch != 'e') {
throw new JSONException("error parse true");
}
next();
if (ch == ' ' || ch == ',' || ch == '}' || ch == ']' || ch == '\n' || ch == '\r' || ch == '\t' || ch == EOI
|| ch == '\f' || ch == '\b') {
token = JSONToken.TRUE;
} else {
throw new JSONException("scan true error");
}
}
public final void scanTreeSet() {
if (ch != 'T') {
throw new JSONException("error parse true");
}
next();
if (ch != 'r') {
throw new JSONException("error parse true");
}
next();
if (ch != 'e') {
throw new JSONException("error parse true");
}
next();
if (ch != 'e') {
throw new JSONException("error parse true");
}
next();
if (ch != 'S') {
throw new JSONException("error parse true");
}
next();
if (ch != 'e') {
throw new JSONException("error parse true");
}
next();
if (ch != 't') {
throw new JSONException("error parse true");
}
next();
if (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f' || ch == '\b' || ch == '[' || ch == '(') {
token = JSONToken.TREE_SET;
} else {
throw new JSONException("scan set error");
}
}
public final void scanNullOrNew() {
if (ch != 'n') {
throw new JSONException("error parse null or new");
}
next();
if (ch == 'u') {
next();
if (ch != 'l') {
throw new JSONException("error parse true");
}
next();
if (ch != 'l') {
throw new JSONException("error parse true");
}
next();
if (ch == ' ' || ch == ',' || ch == '}' || ch == ']' || ch == '\n' || ch == '\r' || ch == '\t' || ch == EOI
|| ch == '\f' || ch == '\b') {
token = JSONToken.NULL;
} else {
throw new JSONException("scan true error");
}
return;
}
if (ch != 'e') {
throw new JSONException("error parse e");
}
next();
if (ch != 'w') {
throw new JSONException("error parse w");
}
next();
if (ch == ' ' || ch == ',' || ch == '}' || ch == ']' || ch == '\n' || ch == '\r' || ch == '\t' || ch == EOI
|| ch == '\f' || ch == '\b') {
token = JSONToken.NEW;
} else {
throw new JSONException("scan true error");
}
}
public final void scanUndefined() {
if (ch != 'u') {
throw new JSONException("error parse false");
}
next();
if (ch != 'n') {
throw new JSONException("error parse false");
}
next();
if (ch != 'd') {
throw new JSONException("error parse false");
}
next();
if (ch != 'e') {
throw new JSONException("error parse false");
}
next();
if (ch != 'f') {
throw new JSONException("error parse false");
}
next();
if (ch != 'i') {
throw new JSONException("error parse false");
}
next();
if (ch != 'n') {
throw new JSONException("error parse false");
}
next();
if (ch != 'e') {
throw new JSONException("error parse false");
}
next();
if (ch != 'd') {
throw new JSONException("error parse false");
}
next();
if (ch == ' ' || ch == ',' || ch == '}' || ch == ']' || ch == '\n' || ch == '\r' || ch == '\t' || ch == EOI
|| ch == '\f' || ch == '\b') {
token = JSONToken.UNDEFINED;
} else {
throw new JSONException("scan false error");
}
}
public final void scanFalse() {
if (ch != 'f') {
throw new JSONException("error parse false");
}
next();
if (ch != 'a') {
throw new JSONException("error parse false");
}
next();
if (ch != 'l') {
throw new JSONException("error parse false");
}
next();
if (ch != 's') {
throw new JSONException("error parse false");
}
next();
if (ch != 'e') {
throw new JSONException("error parse false");
}
next();
if (ch == ' ' || ch == ',' || ch == '}' || ch == ']' || ch == '\n' || ch == '\r' || ch == '\t' || ch == EOI
|| ch == '\f' || ch == '\b') {
token = JSONToken.FALSE;
} else {
throw new JSONException("scan false error");
}
}
public final void scanIdent() {
np = bp - 1;
hasSpecial = false;
for (;;) {
sp++;
next();
if (Character.isLetterOrDigit(ch)) {
continue;
}
String ident = stringVal();
Integer tok = keywods.getKeyword(ident);
if (tok != null) {
token = tok;
} else {
token = JSONToken.IDENTIFIER;
}
return;
}
}
public abstract String stringVal();
public abstract String subString(int offset, int count);
protected abstract boolean charArrayCompare(char[] chars);
public final boolean isBlankInput() {
for (int i = 0;; ++i) {
char chLocal = charAt(i);
if (chLocal == EOI) {
break;
}
if (!isWhitespace(chLocal)) {
return false;
}
}
return true;
}
public final void skipWhitespace() {
for (;;) {
if (ch < whitespaceFlags.length && whitespaceFlags[ch]) {
next();
continue;
} else {
break;
}
}
}
private final void scanStringSingleQuote() {
np = bp;
hasSpecial = false;
char chLocal;
for (;;) {
chLocal = next();
if (chLocal == '\'') {
break;
}
if (chLocal == EOI) {
throw new JSONException("unclosed single-quote string");
}
if (chLocal == '\\') {
if (!hasSpecial) {
hasSpecial = true;
if (sp > sbuf.length) {
char[] newsbuf = new char[sp * 2];
System.arraycopy(sbuf, 0, newsbuf, 0, sbuf.length);
sbuf = newsbuf;
}
// text.getChars(offset, offset + count, dest, 0);
this.copyTo(np + 1, sp, sbuf);
// System.arraycopy(buf, np + 1, sbuf, 0, sp);
}
chLocal = next();
switch (chLocal) {
case '0':
putChar('\0');
break;
case '1':
putChar('\1');
break;
case '2':
putChar('\2');
break;
case '3':
putChar('\3');
break;
case '4':
putChar('\4');
break;
case '5':
putChar('\5');
break;
case '6':
putChar('\6');
break;
case '7':
putChar('\7');
break;
case 'b': // 8
putChar('\b');
break;
case 't': // 9
putChar('\t');
break;
case 'n': // 10
putChar('\n');
break;
case 'v': // 11
putChar('\u000B');
break;
case 'f': // 12
case 'F':
putChar('\f');
break;
case 'r': // 13
putChar('\r');
break;
case '"': // 34
putChar('"');
break;
case '\'': // 39
putChar('\'');
break;
case '/': // 47
putChar('/');
break;
case '\\': // 92
putChar('\\');
break;
case 'x':
char x1 = chLocal = next();
char x2 = chLocal = next();
int x_val = digits[x1] * 16 + digits[x2];
char x_char = (char) x_val;
putChar(x_char);
break;
case 'u':
char c1 = chLocal = next();
char c2 = chLocal = next();
char c3 = chLocal = next();
char c4 = chLocal = next();
int val = Integer.parseInt(new String(new char[] { c1, c2, c3, c4 }), 16);
putChar((char) val);
break;
default:
this.ch = chLocal;
throw new JSONException("unclosed single-quote string");
}
continue;
}
if (!hasSpecial) {
sp++;
continue;
}
if (sp == sbuf.length) {
putChar(chLocal);
} else {
sbuf[sp++] = chLocal;
}
}
token = LITERAL_STRING;
this.next();
}
public final void scanSet() {
if (ch != 'S') {
throw new JSONException("error parse true");
}
next();
if (ch != 'e') {
throw new JSONException("error parse true");
}
next();
if (ch != 't') {
throw new JSONException("error parse true");
}
next();
if (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f' || ch == '\b' || ch == '[' || ch == '(') {
token = JSONToken.SET;
} else {
throw new JSONException("scan set error");
}
}
/**
* Append a character to sbuf.
*/
protected final void putChar(char ch) {
if (sp == sbuf.length) {
char[] newsbuf = new char[sbuf.length * 2];
System.arraycopy(sbuf, 0, newsbuf, 0, sbuf.length);
sbuf = newsbuf;
}
sbuf[sp++] = ch;
}
public final void scanNumber() {
np = bp;
if (ch == '-') {
sp++;
next();
}
for (;;) {
if (ch >= '0' && ch <= '9') {
sp++;
} else {
break;
}
next();
}
boolean isDouble = false;
if (ch == '.') {
sp++;
next();
isDouble = true;
for (;;) {
if (ch >= '0' && ch <= '9') {
sp++;
} else {
break;
}
next();
}
}
if (ch == 'L') {
sp++;
next();
} else if (ch == 'S') {
sp++;
next();
} else if (ch == 'B') {
sp++;
next();
} else if (ch == 'F') {
sp++;
next();
isDouble = true;
} else if (ch == 'D') {
sp++;
next();
isDouble = true;
} else if (ch == 'e' || ch == 'E') {
sp++;
next();
if (ch == '+' || ch == '-') {
sp++;
next();
}
for (;;) {
if (ch >= '0' && ch <= '9') {
sp++;
} else {
break;
}
next();
}
if (ch == 'D' || ch == 'F') {
sp++;
next();
}
isDouble = true;
}
if (isDouble) {
token = JSONToken.LITERAL_FLOAT;
} else {
token = JSONToken.LITERAL_INT;
}
}
public final long longValue() throws NumberFormatException {
long result = 0;
boolean negative = false;
int i = np, max = np + sp;
long limit;
long multmin;
int digit;
if (charAt(np) == '-') {
negative = true;
limit = Long.MIN_VALUE;
i++;
} else {
limit = -Long.MAX_VALUE;
}
multmin = negative ? MULTMIN_RADIX_TEN : N_MULTMAX_RADIX_TEN;
if (i < max) {
digit = digits[charAt(i++)];
result = -digit;
}
while (i < max) {
// Accumulating negatively avoids surprises near MAX_VALUE
char chLocal = charAt(i++);
if (chLocal == 'L' || chLocal == 'S' || chLocal == 'B') {
break;
}
digit = digits[chLocal];
if (result < multmin) {
throw new NumberFormatException(numberString());
}
result *= 10;
if (result < limit + digit) {
throw new NumberFormatException(numberString());
}
result -= digit;
}
if (negative) {
if (i > np + 1) {
return result;
} else { /* Only got "-" */
throw new NumberFormatException(numberString());
}
} else {
return -result;
}
}
public final Number decimalValue(boolean decimal) {
char chLocal = charAt(np + sp - 1);
if (chLocal == 'F') {
return Float.parseFloat(numberString());
// return Float.parseFloat(new String(buf, np, sp - 1));
}
if (chLocal == 'D') {
return Double.parseDouble(numberString());
// return Double.parseDouble(new String(buf, np, sp - 1));
}
if (decimal) {
return decimalValue();
} else {
return doubleValue();
}
}
public final BigDecimal decimalValue() {
return new BigDecimal(numberString());
}
public final Number numberValue() {
char type = charAt(np + sp - 1);
String str = this.numberString();
switch (type) {
case 'D':
return Double.parseDouble(str);
case 'F':
return Float.parseFloat(str);
default:
return new BigDecimal(str);
}
}
public static final boolean isWhitespace(char ch) {
// 专门调整了判断顺序
return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f' || ch == '\b';
}
protected static boolean[] whitespaceFlags = new boolean[256];
static {
whitespaceFlags[' '] = true;
whitespaceFlags['\n'] = true;
whitespaceFlags['\r'] = true;
whitespaceFlags['\t'] = true;
whitespaceFlags['\f'] = true;
whitespaceFlags['\b'] = true;
}
protected static final long MULTMIN_RADIX_TEN = Long.MIN_VALUE / 10;
protected static final long N_MULTMAX_RADIX_TEN = -Long.MAX_VALUE / 10;
protected static final int INT_MULTMIN_RADIX_TEN = Integer.MIN_VALUE / 10;
protected static final int INT_N_MULTMAX_RADIX_TEN = -Integer.MAX_VALUE / 10;
protected final static int[] digits = new int[(int) 'f' + 1];
static {
for (int i = '0'; i <= '9'; ++i) {
digits[i] = i - '0';
}
for (int i = 'a'; i <= 'f'; ++i) {
digits[i] = (i - 'a') + 10;
}
for (int i = 'A'; i <= 'F'; ++i) {
digits[i] = (i - 'A') + 10;
}
}
}