/*
* 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 com.alibaba.fastjson.parser;
import static com.alibaba.fastjson.parser.JSONToken.COLON;
import static com.alibaba.fastjson.parser.JSONToken.COMMA;
import static com.alibaba.fastjson.parser.JSONToken.EOF;
import static com.alibaba.fastjson.parser.JSONToken.ERROR;
import static com.alibaba.fastjson.parser.JSONToken.LBRACE;
import static com.alibaba.fastjson.parser.JSONToken.LBRACKET;
import static com.alibaba.fastjson.parser.JSONToken.LPAREN;
import static com.alibaba.fastjson.parser.JSONToken.RBRACE;
import static com.alibaba.fastjson.parser.JSONToken.RBRACKET;
import static com.alibaba.fastjson.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 com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
/**
* @author wenshao<szujobs@hotmail.com>
*/
public abstract class JSONLexer implements Closeable {
public final static byte EOI = 0x1A;
public final static int NOT_MATCH = -1;
public final static int NOT_MATCH_NAME = -2;
public final static int UNKOWN = 0;
public final static int OBJECT = 1;
public final static int ARRAY = 2;
public final static int VALUE = 3;
public final static int END = 4;
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 JSONLexer(){
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 'D': // Date
scanIdent();
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() {
sp = 0;
for (;;) {
if (ch == ':') {
next();
nextToken();
return;
}
if (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f' || ch == '\b') {
next();
continue;
}
throw new JSONException("not match ':' - " + 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;
int i = np, max = np + sp;
long limit;
long multmin;
int digit;
char type = ' ';
if (max > 0) {
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) {
sp = 0;
for (;;) {
if (ch == ':') {
next();
break;
}
if (isWhitespace(ch)) {
next();
continue;
}
throw new JSONException("not match ':', actual " + 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 abstract String symbol(SymbolTable symbolTable);
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);
public final void resetStringPosition() {
this.sp = 0;
}
public abstract String scanSymbolUnQuoted(final SymbolTable symbolTable);
protected abstract void copyTo(int offset, int count, char[] dest);
public abstract void scanString();
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 abstract boolean isRef();
public abstract int scanType(String type);
public abstract boolean matchField(char[] fieldName);
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");
}
String stringVal = subString(bp + fieldName.length + 1, endIndex - startIndex);
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 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;
}
public ArrayList<String> scanFieldStringArray(char[] fieldName) {
return (ArrayList<String>) scanFieldStringArray(fieldName, null);
}
@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;
}
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 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 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 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 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 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 abstract void scanIdent();
public abstract String stringVal();
public abstract String subString(int offset, int count);
public 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 (whitespaceFlags[ch]) {
next();
continue;
} else {
break;
}
}
}
public abstract void scanStringSingleQuote();
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') {
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 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;
}
}
}