com.landawn.abacus.parser.JSONStreamReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of abacus-common Show documentation
Show all versions of abacus-common Show documentation
A general programming library in Java/Android. It's easy to learn and simple to use with concise and powerful APIs.
/*
* Copyright (C) 2015 HaiYang Li
*
* 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.landawn.abacus.parser;
import java.io.IOException;
import java.io.Reader;
import com.landawn.abacus.exception.ParseException;
import com.landawn.abacus.util.IOUtil;
import com.landawn.abacus.util.N;
import com.landawn.abacus.util.WD;
/**
*
* @author Haiyang Li
* @since 0.8
*/
class JSONStreamReader extends JSONStringReader {
JSONStreamReader(Reader reader, char[] rbuf, char[] cbuf) {
super(rbuf, 0, 0, cbuf, reader);
}
JSONStreamReader(Reader reader, char[] rbuf, int beginIndex, int endIndex, char[] cbuf) {
super(rbuf, beginIndex, endIndex, cbuf, reader);
}
/**
*
* @param reader
* @param rbuf
* @param cbuf
* @return
*/
public static JSONReader parse(Reader reader, char[] rbuf, char[] cbuf) {
// Warning. There is a bug in below code ---> empty value is returned if input source is InputStream/Reader.
// int n = 0;
//
// try {
// n = IOUtil.read(reader, rbuf, 0, rbuf.length);
// } catch (IOException e) {
// throw new UncheckedIOException(e);
// }
//
// if (n <= 0) {
// return new JSONStringReader(rbuf, 0, 0, cbuf, reader);
// } else if (n < rbuf.length) {
// return new JSONStringReader(rbuf, 0, n, cbuf, reader);
// } else {
// return new JSONStreamReader(reader, rbuf, 0, n, cbuf);
// }
return new JSONStreamReader(reader, rbuf, cbuf);
}
/**
*
*
* @return
* @throws IOException
*/
@Override
public int nextToken() throws IOException {
if (strBeginIndex >= strEndIndex) {
refill();
}
text = null;
numValue = null;
nextChar = 0;
startIndexForText = strBeginIndex;
if (nextEvent == START_QUOTATION_D || nextEvent == START_QUOTATION_S) {
final char quoteChar = nextEvent == START_QUOTATION_D ? WD._QUOTATION_D : WD._QUOTATION_S;
for (int ch = 0; strBeginIndex < strEndIndex;) {
ch = strValue[strBeginIndex++];
if (ch == quoteChar) {
endIndexForText = strBeginIndex - 1;
nextEvent = quoteChar == WD._QUOTATION_D ? END_QUOTATION_D : END_QUOTATION_S;
return nextEvent;
}
if (nextChar > 0) {
if (nextChar >= cbufLen) {
enlargeCharBuffer();
}
cbuf[nextChar++] = (ch == WD._BACKSLASH) ? readEscapeCharacter() : (char) ch;
} else {
if (ch == WD._BACKSLASH) {
saveToBuffer();
// strStart++;
cbuf[nextChar++] = readEscapeCharacter();
}
}
if (strBeginIndex >= strEndIndex) {
refill();
}
}
} else {
for (int ch = 0; strBeginIndex < strEndIndex;) {
ch = strValue[strBeginIndex++];
if (ch < 128 && (nextEvent = charEvents[ch]) > 0) {
if (nextEvent < 32) { //
endIndexForText = strBeginIndex - 1;
return nextEvent;
}
saveChar(ch);
if (nextChar == 0 && strBeginIndex - startIndexForText == 1) {
boolean isNumber = false;
if (nextEvent == 'f') { // false
if (saveChar(nextChar()) == 'a' && saveChar(nextChar()) == 'l' && saveChar(nextChar()) == 's' && saveChar(nextChar()) == 'e') {
text = FALSE;
}
} else if (nextEvent == 't') { // true
if (saveChar(nextChar()) == 'r' && saveChar(nextChar()) == 'u' && saveChar(nextChar()) == 'e') {
text = TRUE;
}
} else if (nextEvent == 'n') { // null
if (saveChar(nextChar()) == 'u' && saveChar(nextChar()) == 'l' && saveChar(nextChar()) == 'l') { //NOSONAR
text = NULL;
}
} else if ((nextEvent >= '0' && nextEvent <= '9') || nextEvent == '-' || nextEvent == '+') { // number.
isNumber = true;
readNumber(ch);
}
// } else if (nextEvent == 'F') { // "False", "FALSE" // possible? TODO
// } else if (nextEvent == 'T') { // "True", "TRUE" // possible? TODO
// } else if (nextEvent == 'N') { // "Null", "NULL" // possible? TODO
// }
if (isNumber) {
// done in readNumber...
} else {
if (strBeginIndex >= strEndIndex) {
refill();
}
while (strBeginIndex < strEndIndex) {
ch = strValue[strBeginIndex++];
if (ch < 128) {
nextEvent = charEvents[ch];
if (nextEvent > 0 && nextEvent < 32) {
endIndexForText = strBeginIndex - 1;
return nextEvent;
}
}
if (saveChar(ch) > 32) {
text = null;
}
if (strBeginIndex >= strEndIndex) {
refill();
}
}
endIndexForText = strBeginIndex;
nextEvent = -1;
}
return nextEvent;
}
} else {
saveChar(ch);
}
if (strBeginIndex >= strEndIndex) {
refill();
}
}
}
endIndexForText = strBeginIndex;
nextEvent = -1;
return nextEvent;
}
@Override
protected void readNumber(final int firstChar) throws IOException {
if (strBeginIndex >= strEndIndex) {
refill();
}
final boolean negative = firstChar == '-';
long ret = firstChar == '-' || firstChar == '+' ? 0 : (firstChar - '0');
int pointPositoin = -1;
int cnt = ret == 0 ? 0 : 1;
int ch = 0;
int typeFlag = 0;
while ((ch = nextChar()) >= 0) {
if (ch >= '0' && ch <= '9') {
if (cnt < MAX_PARSABLE_NUM_LEN || (cnt == MAX_PARSABLE_NUM_LEN && ret <= (Long.MAX_VALUE - (ch - '0')) / 10)) {
ret = ret * 10 + (ch - '0');
if (ret > 0 || pointPositoin > 0) {
cnt++;
}
} else {
cnt += 2; // So cnt will > MAX_PARSABLE_NUM_LEN + 1 to skip result.
}
// why need this? For example: "1233993E323" or "8888..." (a very long big integer with 100_000 digits, longer than buffer size),
// what will happen to prefix "1233993" before "E"? They won't be saved if nextChar > 0
if (nextChar > 0) {
// saveChar(ch);
// for better performance
if (nextChar >= cbufLen) {
enlargeCharBuffer();
}
cbuf[nextChar++] = (char) ch;
}
} else if (ch == '.' && pointPositoin < 0) {
if (cnt == 0) {
cnt = 1;
}
pointPositoin = cnt;
// why need this? For example: "1233993E323" or "8888..." (a very long big integer with 100_000 digits, longer than buffer size),
// what will happen to prefix "1233993" before "E"? They won't be saved if nextChar > 0
if (nextChar > 0) {
// saveChar(ch);
// for better performance
if (nextChar >= cbufLen) {
enlargeCharBuffer();
}
cbuf[nextChar++] = (char) ch;
}
} else {
do {
if (ch < 128) {
nextEvent = charEvents[ch];
if (nextEvent > 0 && nextEvent < 32) {
break;
}
}
ch = saveChar(ch);
if (nextEvent > 0 && typeFlag == 0 && (ch == 'l' || ch == 'L' || ch == 'f' || ch == 'F' || ch == 'd' || ch == 'D')) {
typeFlag = ch;
} else if (ch > 32) { // ignore <= 32 whitespace chars.
cnt = -1; // TODO can't parse here. leave it Numbers.createNumber(...).
}
} while ((ch = nextChar()) >= 0);
break;
}
}
if (nextEvent > 0 && nextEvent < 32) {
endIndexForText = strBeginIndex - 1;
} else {
endIndexForText = strBeginIndex;
nextEvent = -1;
}
if (cnt >= 0 && cnt <= MAX_PARSABLE_NUM_LEN + 1 && pointPositoin != cnt) {
if (negative) {
ret = -ret;
}
if (typeFlag > 0) {
if (pointPositoin > 0) {
if (typeFlag == 'f' || typeFlag == 'F') {
numValue = (float) (((double) ret) / POWERS_OF_TEN[cnt - pointPositoin]);
} else { // ignore 'l' or 'L' if it's specified.
numValue = ((double) ret) / POWERS_OF_TEN[cnt - pointPositoin];
}
} else if (typeFlag == 'f' || typeFlag == 'F') {
numValue = (float) ret;
} else if (typeFlag == 'd' || typeFlag == 'D') {
numValue = (double) ret;
} else { // typeFlag == 'l' or 'L'.
numValue = ret;
}
} else {
if (pointPositoin > 0) {
numValue = ((double) ret) / POWERS_OF_TEN[cnt - pointPositoin];
} else if (ret >= Integer.MIN_VALUE && ret <= Integer.MAX_VALUE) {
numValue = (int) ret;
} else {
numValue = ret;
}
}
}
// else { // for debug
// logger.warn("#######: " + getText());
// System.out.println("#######: " + getText());
// }
}
@Override
protected int saveChar(int ch) throws IOException {
if (ch < 0) {
return ch;
}
return super.saveChar(ch);
}
protected int nextChar() throws IOException {
if (strBeginIndex >= strEndIndex) {
refill();
}
if (strBeginIndex >= strEndIndex) {
return -1;
}
return strValue[strBeginIndex++];
}
@Override
protected char readEscapeCharacter() throws IOException {
if (strBeginIndex >= strEndIndex) {
refill();
}
int escaped = strValue[strBeginIndex++];
switch (escaped) {
case 'u':
// Equivalent to Integer.parseInt(stringPool.get(buffer, pos,
// 4), 16);
char result = 0;
for (int i = 0, c = 0; i < 4; i++) {
if (strBeginIndex >= strEndIndex) {
refill();
}
c = strValue[strBeginIndex++];
result <<= 4;
if ((c >= '0') && (c <= '9')) {
result += (c - '0');
} else if ((c >= 'a') && (c <= 'f')) {
result += (c - 'a' + 10);
} else if ((c >= 'A') && (c <= 'F')) {
result += (c - 'A' + 10);
} else {
throw new ParseException("Number format fxception: \\u" + String.valueOf(cbuf));
}
}
return result;
case 't':
return '\t';
case 'b':
return '\b';
case 'n':
return '\n';
case 'r':
return '\r';
case 'f':
return '\f';
// // fall-through
// case '\'':
// case '"':
// case '\\':
default:
return (char) escaped;
}
}
/**
*
* @throws IOException Signals that an I/O exception has occurred.
*/
protected void refill() throws IOException {
if (strBeginIndex >= strEndIndex) {
if (nextChar == 0) {
endIndexForText = strBeginIndex;
if (endIndexForText > startIndexForText) {
N.copy(strValue, startIndexForText, cbuf, 0, endIndexForText - startIndexForText);
nextChar = endIndexForText - startIndexForText;
} else {
startIndexForText = 0;
}
}
int n = IOUtil.read(reader, strValue, 0, strValue.length);
if (n > 0) {
strBeginIndex = 0;
strEndIndex = n;
}
}
}
}