/*
* Copyright 1999-2017 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 java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashSet;
import java.util.TimeZone;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.util.ASMUtils;
import com.alibaba.fastjson.util.IOUtils;
//这个类,为了性能优化做了很多特别处理,一切都是为了性能!!!
/**
* @author wenshao[szujobs@hotmail.com]
*/
public final class JSONScanner extends JSONLexerBase {
private final String text;
private final int len;
public JSONScanner(String input){
this(input, JSON.DEFAULT_PARSER_FEATURE);
}
public JSONScanner(String input, int features){
super(features);
text = input;
len = text.length();
bp = -1;
next();
if (ch == 65279) { // utf-8 bom
next();
}
}
public final char charAt(int index) {
if (index >= len) {
return EOI;
}
return text.charAt(index);
}
public final char next() {
int index = ++bp;
return ch = (index >= this.len ? //
EOI //
: text.charAt(index));
}
public JSONScanner(char[] input, int inputLength){
this(input, inputLength, JSON.DEFAULT_PARSER_FEATURE);
}
public JSONScanner(char[] input, int inputLength, int features){
this(new String(input, 0, inputLength), features);
}
protected final void copyTo(int offset, int count, char[] dest) {
text.getChars(offset, offset + count, dest, 0);
}
static boolean charArrayCompare(String src, int offset, char[] dest) {
final int destLen = dest.length;
if (destLen + offset > src.length()) {
return false;
}
for (int i = 0; i < destLen; ++i) {
if (dest[i] != src.charAt(offset + i)) {
return false;
}
}
return true;
}
public final boolean charArrayCompare(char[] chars) {
return charArrayCompare(text, bp, chars);
}
public final int indexOf(char ch, int startIndex) {
return text.indexOf(ch, startIndex);
}
public final String addSymbol(int offset, int len, int hash, final SymbolTable symbolTable) {
return symbolTable.addSymbol(text, offset, len, hash);
}
public byte[] bytesValue() {
return IOUtils.decodeBase64(text, np + 1, sp);
}
/**
* The value of a literal token, recorded as a string. For integers, leading 0x and 'l' suffixes are suppressed.
*/
public final String stringVal() {
if (!hasSpecial) {
return this.subString(np + 1, sp);
} else {
return new String(sbuf, 0, sp);
}
}
public final String subString(int offset, int count) {
if (ASMUtils.IS_ANDROID) {
if (count < sbuf.length) {
text.getChars(offset, offset + count, sbuf, 0);
return new String(sbuf, 0, count);
} else {
char[] chars = new char[count];
text.getChars(offset, offset + count, chars, 0);
return new String(chars);
}
} else {
return text.substring(offset, offset + count);
}
}
public final char[] sub_chars(int offset, int count) {
if (ASMUtils.IS_ANDROID && count < sbuf.length) {
text.getChars(offset, offset + count, sbuf, 0);
return sbuf;
} else {
char[] chars = new char[count];
text.getChars(offset, offset + count, chars, 0);
return chars;
}
}
public final String numberString() {
char chLocal = charAt(np + sp - 1);
int sp = this.sp;
if (chLocal == 'L' || chLocal == 'S' || chLocal == 'B' || chLocal == 'F' || chLocal == 'D') {
sp--;
}
return this.subString(np, sp);
}
public final BigDecimal decimalValue() {
char chLocal = charAt(np + sp - 1);
int sp = this.sp;
if (chLocal == 'L' || chLocal == 'S' || chLocal == 'B' || chLocal == 'F' || chLocal == 'D') {
sp--;
}
int offset = np, count = sp;
if (count < sbuf.length) {
text.getChars(offset, offset + count, sbuf, 0);
return new BigDecimal(sbuf, 0, count);
} else {
char[] chars = new char[count];
text.getChars(offset, offset + count, chars, 0);
return new BigDecimal(chars);
}
}
public boolean scanISO8601DateIfMatch() {
return scanISO8601DateIfMatch(true);
}
public boolean scanISO8601DateIfMatch(boolean strict) {
int rest = len - bp;
if ((!strict) && rest > 13) {
char c0 = charAt(bp);
char c1 = charAt(bp + 1);
char c2 = charAt(bp + 2);
char c3 = charAt(bp + 3);
char c4 = charAt(bp + 4);
char c5 = charAt(bp + 5);
char c_r0 = charAt(bp + rest - 1);
char c_r1 = charAt(bp + rest - 2);
if (c0 == '/' && c1 == 'D' && c2 == 'a' && c3 == 't' && c4 == 'e' && c5 == '(' && c_r0 == '/'
&& c_r1 == ')') {
int plusIndex = -1;
for (int i = 6; i < rest; ++i) {
char c = charAt(bp + i);
if (c == '+') {
plusIndex = i;
} else if (c < '0' || c > '9') {
break;
}
}
if (plusIndex == -1) {
return false;
}
int offset = bp + 6;
String numberText = this.subString(offset, plusIndex - offset);
long millis = Long.parseLong(numberText);
calendar = Calendar.getInstance(timeZone, locale);
calendar.setTimeInMillis(millis);
token = JSONToken.LITERAL_ISO8601_DATE;
return true;
}
}
if (rest == 8 || rest == 14 || (rest == 17 && charAt(bp + 6) != '-')) {
if (strict) {
return false;
}
char y0 = charAt(bp);
char y1 = charAt(bp + 1);
char y2 = charAt(bp + 2);
char y3 = charAt(bp + 3);
char M0 = charAt(bp + 4);
char M1 = charAt(bp + 5);
char d0 = charAt(bp + 6);
char d1 = charAt(bp + 7);
if (!checkDate(y0, y1, y2, y3, M0, M1, d0, d1)) {
return false;
}
setCalendar(y0, y1, y2, y3, M0, M1, d0, d1);
int hour, minute, seconds, millis;
if (rest != 8) {
char h0 = charAt(bp + 8);
char h1 = charAt(bp + 9);
char m0 = charAt(bp + 10);
char m1 = charAt(bp + 11);
char s0 = charAt(bp + 12);
char s1 = charAt(bp + 13);
if (!checkTime(h0, h1, m0, m1, s0, s1)) {
return false;
}
if (rest == 17) {
char S0 = charAt(bp + 14);
char S1 = charAt(bp + 15);
char S2 = charAt(bp + 16);
if (S0 < '0' || S0 > '9') {
return false;
}
if (S1 < '0' || S1 > '9') {
return false;
}
if (S2 < '0' || S2 > '9') {
return false;
}
millis = (S0 - '0') * 100 + (S1 - '0') * 10 + (S2 - '0');
} else {
millis = 0;
}
hour = (h0 - '0') * 10 + (h1 - '0');
minute = (m0 - '0') * 10 + (m1 - '0');
seconds = (s0 - '0') * 10 + (s1 - '0');
} else {
hour = 0;
minute = 0;
seconds = 0;
millis = 0;
}
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, seconds);
calendar.set(Calendar.MILLISECOND, millis);
token = JSONToken.LITERAL_ISO8601_DATE;
return true;
}
if (rest < 9) {
return false;
}
char c0 = charAt(bp);
char c1 = charAt(bp + 1);
char c2 = charAt(bp + 2);
char c3 = charAt(bp + 3);
char c4 = charAt(bp + 4);
char c5 = charAt(bp + 5);
char c6 = charAt(bp + 6);
char c7 = charAt(bp + 7);
char c8 = charAt(bp + 8);
char c9 = charAt(bp + 9);
int date_len = 10;
char y0, y1, y2, y3, M0, M1, d0, d1;
if ((c4 == '-' && c7 == '-') // cn
|| (c4 == '/' && c7 == '/') // tw yyyy/mm/dd
) {
y0 = c0;
y1 = c1;
y2 = c2;
y3 = c3;
M0 = c5;
M1 = c6;
d0 = c8;
d1 = c9;
} else if ((c4 == '-' && c6 == '-') // cn yyyy-m-dd
) {
y0 = c0;
y1 = c1;
y2 = c2;
y3 = c3;
M0 = '0';
M1 = c5;
if (c8 == ' ') {
d0 = '0';
d1 = c7;
date_len = 8;
} else {
d0 = c7;
d1 = c8;
date_len = 9;
}
} else if ((c2 == '.' && c5 == '.') // de dd.mm.yyyy
|| (c2 == '-' && c5 == '-') // in dd-mm-yyyy
) {
d0 = c0;
d1 = c1;
M0 = c3;
M1 = c4;
y0 = c6;
y1 = c7;
y2 = c8;
y3 = c9;
} else {
if (c4 == '年' || c4 == '년') {
y0 = c0;
y1 = c1;
y2 = c2;
y3 = c3;
if (c7 == '月' || c7 == '월') {
M0 = c5;
M1 = c6;
if (c9 == '日' || c9 == '일') {
d0 = '0';
d1 = c8;
} else if (charAt(bp + 10) == '日' || charAt(bp + 10) == '일'){
d0 = c8;
d1 = c9;
date_len = 11;
} else {
return false;
}
} else if (c6 == '月' || c6 == '월') {
M0 = '0';
M1 = c5;
if (c8 == '日' || c8 == '일') {
d0 = '0';
d1 = c7;
} else if (c9 == '日' || c9 == '일'){
d0 = c7;
d1 = c8;
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
}
if (!checkDate(y0, y1, y2, y3, M0, M1, d0, d1)) {
return false;
}
setCalendar(y0, y1, y2, y3, M0, M1, d0, d1);
char t = charAt(bp + date_len);
if (t == 'T' || (t == ' ' && !strict)) {
if (rest < date_len + 9) { // "0000-00-00T00:00:00".length()
return false;
}
} else if (t == '"' || t == EOI || t == '日' || t == '일') {
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
ch = charAt(bp += date_len);
token = JSONToken.LITERAL_ISO8601_DATE;
return true;
} else if (t == '+' || t == '-') {
if (len == date_len + 6) {
if (charAt(bp + date_len + 3) != ':' //
|| charAt(bp + date_len + 4) != '0' //
|| charAt(bp + date_len + 5) != '0') {
return false;
}
setTime('0', '0', '0', '0', '0', '0');
calendar.set(Calendar.MILLISECOND, 0);
setTimeZone(t, charAt(bp + date_len + 1), charAt(bp + date_len + 2));
return true;
}
return false;
} else {
return false;
}
if (charAt(bp + date_len + 3) != ':') {
return false;
}
if (charAt(bp + date_len + 6) != ':') {
return false;
}
char h0 = charAt(bp + date_len + 1);
char h1 = charAt(bp + date_len + 2);
char m0 = charAt(bp + date_len + 4);
char m1 = charAt(bp + date_len + 5);
char s0 = charAt(bp + date_len + 7);
char s1 = charAt(bp + date_len + 8);
if (!checkTime(h0, h1, m0, m1, s0, s1)) {
return false;
}
setTime(h0, h1, m0, m1, s0, s1);
char dot = charAt(bp + date_len + 9);
if (dot == '.') {
if (rest < date_len + 11) { // // 0000-00-00T00:00:00.000
return false;
}
} else {
calendar.set(Calendar.MILLISECOND, 0);
ch = charAt(bp += (date_len + 9));
token = JSONToken.LITERAL_ISO8601_DATE;
if (dot == 'Z') {// UTC
// bugfix https://github.com/alibaba/fastjson/issues/376
if (calendar.getTimeZone().getRawOffset() != 0) {
String[] timeZoneIDs = TimeZone.getAvailableIDs(0);// 没有+ 和 - 默认相对0
if (timeZoneIDs.length > 0) {
TimeZone timeZone = TimeZone.getTimeZone(timeZoneIDs[0]);
calendar.setTimeZone(timeZone);
}
}
}
return true;
}
char S0 = charAt(bp + date_len + 10);
if (S0 < '0' || S0 > '9') {
return false;
}
int millis = S0 - '0';
int millisLen = 1;
if (rest > date_len + 11) {
char S1 = charAt(bp + date_len + 11);
if (S1 >= '0' && S1 <= '9') {
millis = millis * 10 + (S1 - '0');
millisLen = 2;
}
}
if (millisLen == 2) {
char S2 = charAt(bp + date_len + 12);
if (S2 >= '0' && S2 <= '9') {
millis = millis * 10 + (S2 - '0');
millisLen = 3;
}
}
calendar.set(Calendar.MILLISECOND, millis);
int timzeZoneLength = 0;
char timeZoneFlag = charAt(bp + date_len + 10 + millisLen);
if (timeZoneFlag == '+' || timeZoneFlag == '-') {
char t0 = charAt(bp + date_len + 10 + millisLen + 1);
if (t0 < '0' || t0 > '1') {
return false;
}
char t1 = charAt(bp + date_len + 10 + millisLen + 2);
if (t1 < '0' || t1 > '9') {
return false;
}
char t2 = charAt(bp + date_len + 10 + millisLen + 3);
if (t2 == ':') { // ThreeLetterISO8601TimeZone
char t3 = charAt(bp + date_len + 10 + millisLen + 4);
if (t3 != '0') {
return false;
}
char t4 = charAt(bp + date_len + 10 + millisLen + 5);
if (t4 != '0') {
return false;
}
timzeZoneLength = 6;
} else if (t2 == '0') { // TwoLetterISO8601TimeZone
char t3 = charAt(bp + date_len + 10 + millisLen + 4);
if (t3 != '0') {
return false;
}
timzeZoneLength = 5;
} else {
timzeZoneLength = 3;
}
setTimeZone(timeZoneFlag, t0, t1);
} else if (timeZoneFlag == 'Z') {// UTC
timzeZoneLength = 1;
if (calendar.getTimeZone().getRawOffset() != 0) {
String[] timeZoneIDs = TimeZone.getAvailableIDs(0);
if (timeZoneIDs.length > 0) {
TimeZone timeZone = TimeZone.getTimeZone(timeZoneIDs[0]);
calendar.setTimeZone(timeZone);
}
}
}
char end = charAt(bp + (date_len + 10 + millisLen + timzeZoneLength));
if (end != EOI && end != '"') {
return false;
}
ch = charAt(bp += (date_len + 10 + millisLen + timzeZoneLength));
token = JSONToken.LITERAL_ISO8601_DATE;
return true;
}
protected void setTime(char h0, char h1, char m0, char m1, char s0, char s1) {
int hour = (h0 - '0') * 10 + (h1 - '0');
int minute = (m0 - '0') * 10 + (m1 - '0');
int seconds = (s0 - '0') * 10 + (s1 - '0');
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, seconds);
}
protected void setTimeZone(char timeZoneFlag, char t0, char t1) {
int timeZoneOffset = ((t0 - '0') * 10 + (t1 - '0')) * 3600 * 1000;
if (timeZoneFlag == '-') {
timeZoneOffset = -timeZoneOffset;
}
if (calendar.getTimeZone().getRawOffset() != timeZoneOffset) {
String[] timeZoneIDs = TimeZone.getAvailableIDs(timeZoneOffset);
if (timeZoneIDs.length > 0) {
TimeZone timeZone = TimeZone.getTimeZone(timeZoneIDs[0]);
calendar.setTimeZone(timeZone);
}
}
}
private boolean checkTime(char h0, char h1, char m0, char m1, char s0, char s1) {
if (h0 == '0') {
if (h1 < '0' || h1 > '9') {
return false;
}
} else if (h0 == '1') {
if (h1 < '0' || h1 > '9') {
return false;
}
} else if (h0 == '2') {
if (h1 < '0' || h1 > '4') {
return false;
}
} else {
return false;
}
if (m0 >= '0' && m0 <= '5') {
if (m1 < '0' || m1 > '9') {
return false;
}
} else if (m0 == '6') {
if (m1 != '0') {
return false;
}
} else {
return false;
}
if (s0 >= '0' && s0 <= '5') {
if (s1 < '0' || s1 > '9') {
return false;
}
} else if (s0 == '6') {
if (s1 != '0') {
return false;
}
} else {
return false;
}
return true;
}
private void setCalendar(char y0, char y1, char y2, char y3, char M0, char M1, char d0, char d1) {
calendar = Calendar.getInstance(timeZone, locale);
int year = (y0 - '0') * 1000 + (y1 - '0') * 100 + (y2 - '0') * 10 + (y3 - '0');
int month = (M0 - '0') * 10 + (M1 - '0') - 1;
int day = (d0 - '0') * 10 + (d1 - '0');
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month);
calendar.set(Calendar.DAY_OF_MONTH, day);
}
static boolean checkDate(char y0, char y1, char y2, char y3, char M0, char M1, int d0, int d1) {
if (y0 != '1' && y0 != '2') {
return false;
}
if (y1 < '0' || y1 > '9') {
return false;
}
if (y2 < '0' || y2 > '9') {
return false;
}
if (y3 < '0' || y3 > '9') {
return false;
}
if (M0 == '0') {
if (M1 < '1' || M1 > '9') {
return false;
}
} else if (M0 == '1') {
if (M1 != '0' && M1 != '1' && M1 != '2') {
return false;
}
} else {
return false;
}
if (d0 == '0') {
if (d1 < '1' || d1 > '9') {
return false;
}
} else if (d0 == '1' || d0 == '2') {
if (d1 < '0' || d1 > '9') {
return false;
}
} else if (d0 == '3') {
if (d1 != '0' && d1 != '1') {
return false;
}
} else {
return false;
}
return true;
}
@Override
public boolean isEOF() {
return bp == len || ch == EOI && bp + 1 == len;
}
public int scanFieldInt(char[] fieldName) {
matchStat = UNKNOWN;
int startPos = this.bp;
char startChar = this.ch;
if (!charArrayCompare(text, bp, fieldName)) {
matchStat = NOT_MATCH_NAME;
return 0;
}
int index = bp + fieldName.length;
char ch = charAt(index++);
boolean negative = false;
if (ch == '-') {
ch = charAt(index++);
negative = true;
}
int value;
if (ch >= '0' && ch <= '9') {
value = ch - '0';
for (;;) {
ch = charAt(index++);
if (ch >= '0' && ch <= '9') {
value = value * 10 + (ch - '0');
} else if (ch == '.') {
matchStat = NOT_MATCH;
return 0;
} else {
break;
}
}
if (value < 0) {
matchStat = NOT_MATCH;
return 0;
}
for (;;) {
if (ch == ',' || ch == '}') {
bp = index - 1;
break;
} else if(isWhitespace(ch)) {
ch = charAt(index++);
continue;
} else {
matchStat = NOT_MATCH;
return 0;
}
}
} else {
matchStat = NOT_MATCH;
return 0;
}
if (ch == ',') {
this.ch = charAt(++bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return negative ? -value : value;
}
if (ch == '}') {
bp = index - 1;
ch = charAt(++bp);
for (; ; ) {
if (ch == ',') {
token = JSONToken.COMMA;
this.ch = charAt(++bp);
break;
} else if (ch == ']') {
token = JSONToken.RBRACKET;
this.ch = charAt(++bp);
break;
} else if (ch == '}') {
token = JSONToken.RBRACE;
this.ch = charAt(++bp);
break;
} else if (ch == EOI) {
token = JSONToken.EOF;
break;
} else if (isWhitespace(ch)) {
ch = charAt(++bp);
continue;
} else {
this.bp = startPos;
this.ch = startChar;
matchStat = NOT_MATCH;
return 0;
}
}
matchStat = END;
}
return negative ? -value : value;
}
public String scanFieldString(char[] fieldName) {
matchStat = UNKNOWN;
int startPos = this.bp;
char startChar = this.ch;
if (!charArrayCompare(text, bp, fieldName)) {
matchStat = NOT_MATCH_NAME;
return stringDefaultValue();
}
int index = bp + fieldName.length;
char ch = charAt(index++);
if (ch != '"') {
matchStat = NOT_MATCH;
return stringDefaultValue();
}
final String strVal;
{
int startIndex = index;
int endIndex = indexOf('"', startIndex);
if (endIndex == -1) {
throw new JSONException("unclosed str");
}
String stringVal = subString(startIndex, endIndex - startIndex);
if (stringVal.indexOf('\\') != -1) {
for (;;) {
int slashCount = 0;
for (int i = endIndex - 1; i >= 0; --i) {
if (charAt(i) == '\\') {
slashCount++;
} else {
break;
}
}
if (slashCount % 2 == 0) {
break;
}
endIndex = indexOf('"', endIndex + 1);
}
int chars_len = endIndex - (bp + fieldName.length + 1);
char[] chars = sub_chars(bp + fieldName.length + 1, chars_len);
stringVal = readString(chars, chars_len);
}
ch = charAt(endIndex + 1);
for (;;) {
if (ch == ',' || ch == '}') {
bp = endIndex + 1;
this.ch = ch;
strVal = stringVal;
break;
} else if (isWhitespace(ch)) {
endIndex++;
ch = charAt(endIndex + 1);
} else {
matchStat = NOT_MATCH;
return stringDefaultValue();
}
}
}
if (ch == ',') {
this.ch = charAt(++bp);
matchStat = VALUE;
return strVal;
} else {
//condition ch == '}' is always 'true'
ch = charAt(++bp);
if (ch == ',') {
token = JSONToken.COMMA;
this.ch = charAt(++bp);
} else if (ch == ']') {
token = JSONToken.RBRACKET;
this.ch = charAt(++bp);
} else if (ch == '}') {
token = JSONToken.RBRACE;
this.ch = charAt(++bp);
} else if (ch == EOI) {
token = JSONToken.EOF;
} else {
this.bp = startPos;
this.ch = startChar;
matchStat = NOT_MATCH;
return stringDefaultValue();
}
matchStat = END;
}
return strVal;
}
public long scanFieldSymbol(char[] fieldName) {
matchStat = UNKNOWN;
if (!charArrayCompare(text, bp, fieldName)) {
matchStat = NOT_MATCH_NAME;
return 0;
}
int index = bp + fieldName.length;
char ch = charAt(index++);
if (ch != '"') {
matchStat = NOT_MATCH;
return 0;
}
long hash = 0x811c9dc5;
for (;;) {
ch = charAt(index++);
if (ch == '\"') {
bp = index;
this.ch = ch = charAt(bp);
break;
} else if (index > len) {
matchStat = NOT_MATCH;
return 0;
}
hash ^= ch;
hash *= 0x1000193;
}
for (;;) {
if (ch == ',') {
this.ch = charAt(++bp);
matchStat = VALUE;
return hash;
} else if (ch == '}') {
next();
skipWhitespace();
ch = getCurrent();
if (ch == ',') {
token = JSONToken.COMMA;
this.ch = charAt(++bp);
} else if (ch == ']') {
token = JSONToken.RBRACKET;
this.ch = charAt(++bp);
} else if (ch == '}') {
token = JSONToken.RBRACE;
this.ch = charAt(++bp);
} else if (ch == EOI) {
token = JSONToken.EOF;
} else {
matchStat = NOT_MATCH;
return 0;
}
matchStat = END;
break;
} else if (isWhitespace(ch)) {
ch = charAt(++bp);
continue;
} else {
matchStat = NOT_MATCH;
return 0;
}
}
return hash;
}
public Collection<String> newCollectionByType(Class<?> type){
if (type.isAssignableFrom(HashSet.class)) {
HashSet<String> list = new HashSet<String>();
return list;
} else if (type.isAssignableFrom(ArrayList.class)) {
ArrayList<String> list2 = new ArrayList<String>();
return list2;
} else {
try {
Collection<String> list = (Collection<String>) type.newInstance();
return list;
} catch (Exception e) {
throw new JSONException(e.getMessage(), e);
}
}
}
@SuppressWarnings("unchecked")
public Collection<String> scanFieldStringArray(char[] fieldName, Class<?> type) {
matchStat = UNKNOWN;
if (!charArrayCompare(text, bp, fieldName)) {
matchStat = NOT_MATCH_NAME;
return null;
}
Collection<String> list = newCollectionByType(type);
// 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;
char ch = charAt(index++);
if (ch == '[') {
ch = charAt(index++);
for (;;) {
if (ch == '"') {
int startIndex = index;
int endIndex = indexOf('"', startIndex);
if (endIndex == -1) {
throw new JSONException("unclosed str");
}
String stringVal = subString(startIndex, endIndex - startIndex);
if (stringVal.indexOf('\\') != -1) {
for (;;) {
int slashCount = 0;
for (int i = endIndex - 1; i >= 0; --i) {
if (charAt(i) == '\\') {
slashCount++;
} else {
break;
}
}
if (slashCount % 2 == 0) {
break;
}
endIndex = indexOf('"', endIndex + 1);
}
int chars_len = endIndex - startIndex;
char[] chars = sub_chars(startIndex, chars_len);
stringVal = readString(chars, chars_len);
}
index = endIndex + 1;
ch = charAt(index++);
list.add(stringVal);
} else if (ch == 'n' && text.startsWith("ull", index)) {
index += 3;
ch = charAt(index++);
list.add(null);
} else if (ch == ']' && list.size() == 0) {
ch = charAt(index++);
break;
} else {
matchStat = NOT_MATCH;
return null;
}
if (ch == ',') {
ch = charAt(index++);
continue;
}
if (ch == ']') {
ch = charAt(index++);
while (isWhitespace(ch)) {
ch = charAt(index++);
}
break;
}
matchStat = NOT_MATCH;
return null;
}
} else if (text.startsWith("ull", index)) {
index += 3;
ch = charAt(index++);
list = null;
} else {
matchStat = NOT_MATCH;
return null;
}
bp = index;
if (ch == ',') {
this.ch = charAt(bp);
matchStat = VALUE;
return list;
} else if (ch == '}') {
ch = charAt(bp);
for (;;) {
if (ch == ',') {
token = JSONToken.COMMA;
this.ch = charAt(++bp);
break;
} else if (ch == ']') {
token = JSONToken.RBRACKET;
this.ch = charAt(++bp);
break;
} else if (ch == '}') {
token = JSONToken.RBRACE;
this.ch = charAt(++bp);
break;
} else if (ch == EOI) {
token = JSONToken.EOF;
this.ch = ch;
break;
} else {
boolean space = false;
while (isWhitespace(ch)) {
ch = charAt(index++);
bp = index;
space = true;
}
if (space) {
continue;
}
matchStat = NOT_MATCH;
return null;
}
}
matchStat = END;
} else {
matchStat = NOT_MATCH;
return null;
}
return list;
}
public long scanFieldLong(char[] fieldName) {
matchStat = UNKNOWN;
int startPos = this.bp;
char startChar = this.ch;
if (!charArrayCompare(text, bp, fieldName)) {
matchStat = NOT_MATCH_NAME;
return 0;
}
int index = bp + fieldName.length;
char ch = charAt(index++);
boolean negative = false;
if (ch == '-') {
ch = charAt(index++);
negative = true;
}
long value;
if (ch >= '0' && ch <= '9') {
value = ch - '0';
for (;;) {
ch = charAt(index++);
if (ch >= '0' && ch <= '9') {
value = value * 10 + (ch - '0');
} else if (ch == '.') {
matchStat = NOT_MATCH;
return 0;
} else {
if (ch == ',' || ch == '}') {
bp = index - 1;
}
break;
}
}
if (value < 0) {
this.bp = startPos;
this.ch = startChar;
matchStat = NOT_MATCH;
return 0;
}
} else {
this.bp = startPos;
this.ch = startChar;
matchStat = NOT_MATCH;
return 0;
}
for (;;) {
if (ch == ',') {
this.ch = charAt(++bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return negative ? -value : value;
} else if (ch == '}') {
ch = charAt(++bp);
for (;;) {
if (ch == ',') {
token = JSONToken.COMMA;
this.ch = charAt(++bp);
break;
} else if (ch == ']') {
token = JSONToken.RBRACKET;
this.ch = charAt(++bp);
break;
} else if (ch == '}') {
token = JSONToken.RBRACE;
this.ch = charAt(++bp);
break;
} else if (ch == EOI) {
token = JSONToken.EOF;
break;
} else if (isWhitespace(ch)) {
ch = charAt(++bp);
} else {
this.bp = startPos;
this.ch = startChar;
matchStat = NOT_MATCH;
return 0;
}
}
matchStat = END;
break;
} else if (isWhitespace(ch)) {
bp = index;
ch = charAt(index++);
continue;
} else {
matchStat = NOT_MATCH;
return 0;
}
}
return negative ? -value : value;
}
public boolean scanFieldBoolean(char[] fieldName) {
matchStat = UNKNOWN;
if (!charArrayCompare(text, bp, fieldName)) {
matchStat = NOT_MATCH_NAME;
return false;
}
int index = bp + fieldName.length;
char ch = charAt(index++);
boolean value;
if (ch == 't') {
if (charAt(index++) != 'r') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(index++) != 'u') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(index++) != 'e') {
matchStat = NOT_MATCH;
return false;
}
bp = index;
ch = charAt(bp);
value = true;
} else if (ch == 'f') {
if (charAt(index++) != 'a') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(index++) != 'l') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(index++) != 's') {
matchStat = NOT_MATCH;
return false;
}
if (charAt(index++) != 'e') {
matchStat = NOT_MATCH;
return false;
}
bp = index;
ch = charAt(bp);
value = false;
} else {
matchStat = NOT_MATCH;
return false;
}
for (;;) {
if (ch == ',') {
this.ch = charAt(++bp);
matchStat = VALUE;
token = JSONToken.COMMA;
break;
} else if (ch == '}') {
ch = charAt(++bp);
for (;;) {
if (ch == ',') {
token = JSONToken.COMMA;
this.ch = charAt(++bp);
} else if (ch == ']') {
token = JSONToken.RBRACKET;
this.ch = charAt(++bp);
} else if (ch == '}') {
token = JSONToken.RBRACE;
this.ch = charAt(++bp);
} else if (ch == EOI) {
token = JSONToken.EOF;
} else if (isWhitespace(ch)) {
ch = charAt(++bp);
continue;
} else {
matchStat = NOT_MATCH;
return false;
}
break;
}
matchStat = END;
break;
} else if (isWhitespace(ch)) {
ch = charAt(++bp);
} else {
matchStat = NOT_MATCH;
return false;
}
}
return value;
}
public final int scanInt(char expectNext) {
matchStat = UNKNOWN;
int offset = bp;
char chLocal = charAt(offset++);
final boolean negative = chLocal == '-';
if (negative) {
chLocal = charAt(offset++);
}
int value;
if (chLocal >= '0' && chLocal <= '9') {
value = chLocal - '0';
for (;;) {
chLocal = charAt(offset++);
if (chLocal >= '0' && chLocal <= '9') {
value = value * 10 + (chLocal - '0');
} else if (chLocal == '.') {
matchStat = NOT_MATCH;
return 0;
} else {
break;
}
}
if (value < 0) {
matchStat = NOT_MATCH;
return 0;
}
} else {
matchStat = NOT_MATCH;
return 0;
}
for (;;) {
if (chLocal == expectNext) {
bp = offset;
this.ch = charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return negative ? -value : value;
} else {
if (isWhitespace(chLocal)) {
chLocal = charAt(offset++);
continue;
}
matchStat = NOT_MATCH;
return negative ? -value : value;
}
}
}
public long scanLong(char expectNextChar) {
matchStat = UNKNOWN;
int offset = bp;
char chLocal = charAt(offset++);
final boolean negative = chLocal == '-';
if (negative) {
chLocal = charAt(offset++);
}
long value;
if (chLocal >= '0' && chLocal <= '9') {
value = chLocal - '0';
for (;;) {
chLocal = charAt(offset++);
if (chLocal >= '0' && chLocal <= '9') {
value = value * 10 + (chLocal - '0');
} else if (chLocal == '.') {
matchStat = NOT_MATCH;
return 0;
} else {
break;
}
}
if (value < 0) {
matchStat = NOT_MATCH;
return 0;
}
} else {
matchStat = NOT_MATCH;
return 0;
}
for (;;) {
if (chLocal == expectNextChar) {
bp = offset;
this.ch = charAt(bp);
matchStat = VALUE;
token = JSONToken.COMMA;
return negative ? -value : value;
} else {
if (isWhitespace(chLocal)) {
chLocal = charAt(offset++);
continue;
}
matchStat = NOT_MATCH;
return value;
}
}
}
protected final void arrayCopy(int srcPos, char[] dest, int destPos, int length) {
text.getChars(srcPos, srcPos + length, dest, destPos);
}
public String info() {
return "pos " + bp //
+ ", json : " //
+ (text.length() < 65536 //
? text //
: text.substring(0, 65536));
}
}