/* * 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; } } }