/*
* Copyright 2011 JSON-SMART authors
*
* 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.zkoss.zats.common.json.parser;
import java.io.IOException;
import java.io.Reader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import org.zkoss.zats.common.json.parser.ContainerFactory;
import org.zkoss.zats.common.json.parser.ParseException;
import static org.zkoss.zats.common.json.parser.ParseException.*;
import static org.zkoss.zats.common.json.parser.JSONParser.*;
/**
* Parser for JSON text. Please note that JSONParser is NOT thread-safe.
*
* @author Uriel Chemouni <uchemouni@gmail.com>
* @since 1.1.0
*/
public class JSONParserStream {
public final static int EOI = -1;
private int c;
private ContainerFactory containerFactory;
private Reader in;
private int pos;
private StringBuilder sb = new StringBuilder();
private String xs;
//
//
//
//
//
//
//
//
/**
* use to return Primitive Type, or String, Or JsonObject or JsonArray
* generated by a ContainerFactory
*/
public Object parse(Reader in) throws ParseException, IOException {
return parse(in, ContainerFactory.FACTORY);
}
/**
* use to return Primitive Type, or String, Or JsonObject or JsonArray
* generated by a ContainerFactory
*/
public Object parse(Reader in, ContainerFactory containerFactory) throws ParseException, IOException {
this.in = in;
this.containerFactory = containerFactory;
this.c = in.read();
this.pos = 0;
//
//
//
Object result = readMain(stopX);
xs = null;
return result;
}
final private void read() throws IOException {
c = in.read();
pos++;
}
private List<Object> readArray() throws ParseException, IOException {
List<Object> obj = containerFactory.creatArrayContainer();
if (c != '[')
throw new RuntimeException("Internal Error");
read();
for (;;) {
switch (c) {
case ' ':
case '\r':
case '\n':
case '\t':
read();
continue;
case ']':
read(); /* unstack */
return obj;
case ':':
case '}':
throw new ParseException(pos, ERROR_UNEXPECTED_CHAR, (char) c);
case ',':
read();
continue;
case EOI:
throw new ParseException(pos - 1, ERROR_UNEXPECTED_EOF, "EOF");
default:
obj.add(readMain(stopArray));
continue;
}
}
}
/**
* use to return Primitive Type, or String, Or JsonObject or JsonArray
* generated by a ContainerFactory
*/
private Object readMain(boolean stop[]) throws ParseException, IOException {
for (;;) {
switch (c) {
// skip spaces
case ' ':
case '\r':
case '\n':
case '\t':
read();
continue;
// invalid stats
case ':':
case '}':
case ']':
throw new ParseException(pos, ERROR_UNEXPECTED_CHAR, c);
// start object
case '{':
return readObject();
// start Array
case '[':
return readArray();
// start string
case '"':
case '\'':
//
//
return readString();
// string or null
case 'n':
xs = readNQString(stop);
if ("null".equals(xs)) {
return null;
}
//
//
//
return xs;
// string or false
case 'f':
xs = readNQString(stop);
if ("false".equals(xs)) {
return Boolean.FALSE;
}
//
//
//
return xs;
// string or true
case 't':
xs = readNQString(stop);
if ("true".equals(xs)) {
return Boolean.TRUE;
}
//
//
//
return xs;
// string or NaN
case 'N':
xs = readNQString(stop);
//
//
if ("NaN".equals(xs)) {
return Float.valueOf(Float.NaN);
}
//
//
//
return xs;
// digits
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
//
//
return readNumber(stop);
default:
//
//
//
//
return readNQString(stop);
}
}
}
private String readNQString(boolean[] stop) throws IOException {
sb.delete(0, sb.length());
skipNQString(stop);
return sb.toString().trim();
}
private Object readNumber(boolean[] stop) throws ParseException, IOException {
sb.delete(0, sb.length());
sb.append((char) c);// skip first char digit or -
read();
skipDigits();
if (c != '.' && c != 'E' && c != 'e') {
skipSpace();
if (!stop[c]) {
// convert string
skipNQString(stop);
//
//
//
return sb.toString().trim();
}
xs = sb.toString().trim();
if (xs.length() > 20)
return new BigInteger(xs);
try {
long v = Long.parseLong(xs);
if (v >= (long) Integer.MIN_VALUE && v <= (long) Integer.MAX_VALUE)
return Integer.valueOf((int) v);
return Long.valueOf(v);
} catch (NumberFormatException e) {
return new BigInteger(xs);
}
}
if (c == '.') {
sb.append((char) c);
read();
skipDigits();
}
if (c != 'E' && c != 'e') {
skipSpace();
if (!stop[c]) {
// convert string
skipNQString(stop);
//
//
//
return sb.toString().trim();
}
String num = sb.toString().trim();
if (num.length() > 18) // follow JSjonIJ parssing methode
return new BigDecimal(num);
return Double.parseDouble(num);
}
sb.append('E');
read();
if (c == '+' || c == '-' || c >= '0' && c <= '9') {
sb.append((char) c);
read(); // skip first char
skipDigits();
skipSpace();
if (!stop[c]) {
// convert string
skipNQString(stop);
return sb.toString().trim();
}
return Double.parseDouble(sb.toString().trim());
} else {
skipNQString(stop);
return sb.toString().trim();
//
//
//
//
//
//
}
// throw new ParseException(pos - 1, ERROR_UNEXPECTED_CHAR, null);
}
private Map<String, Object> readObject() throws ParseException, IOException {
Map<String, Object> obj = this.containerFactory.createObjectContainer();
if (c != '{')
throw new RuntimeException("Internal Error");
for (;;) {
read();
switch (c) {
case ' ':
case '\r':
case '\t':
case '\n':
continue;
case ':':
case ']':
case '[':
case '{':
throw new ParseException(pos, ERROR_UNEXPECTED_CHAR, c);
case '}':
read(); /* unstack */
return obj;
case ',':
continue;
case '"':
case '\'':
default:
String key;
if (c == '\"' || c == '\'')
key = readString();
else {
key = readNQString(stopKey);
}
//
//
//
while (c != ':' && c != EOI) {
read();
}
if (c == EOI)
throw new ParseException(pos - 1, ERROR_UNEXPECTED_EOF, null);
read(); /* skip : */
obj.put(key, readMain(stopValue));
if (c == '}') {
read(); /* unstack */
return obj;
} /* if c==, confinue */
continue;
}
}
}
private String readString() throws ParseException, IOException {
//
//
sb.delete(0, sb.length());
/* assert (c == '\"' || c == '\'') */
//
//
//
//
//
//
//
//
//
//
char sep = (char) c;
for (;;) {
read();
switch (c) {
case EOI:
throw new ParseException(pos - 1, ERROR_UNEXPECTED_EOF, null);
case '"':
case '\'':
if (sep == c) {
read();
return sb.toString();
}
sb.append((char) c);
break;
case '\\':
read();
switch (c) {
case 't':
sb.append('\t');
break;
case 'n':
sb.append('\n');
break;
case 'r':
sb.append('\r');
break;
case 'f':
sb.append('\f');
break;
case 'b':
sb.append('\b');
break;
case '\\':
sb.append('\\');
break;
case '/':
sb.append('/');
break;
case '\'':
sb.append('\'');
break;
case '"':
sb.append('"');
break;
case 'u':
sb.append(readUnicode());
break;
default:
break;
}
break;
case '\b':
case '\t':
case '\f':
case '\r':
case '\n':
continue;
default:
sb.append((char) c);
}
}
}
private char readUnicode() throws ParseException, IOException {
int value = 0;
for (int i = 0; i < 4; i++) {
value = value * 16;
read();
if (c >= '0' && c <= '9')
value += c - '0';
else if (c >= 'A' && c <= 'F')
value += (c - 'A') + 10;
else if (c >= 'a' && c <= 'f')
value += (c - 'a') + 10;
else if (c == EOI)
throw new ParseException(pos, ERROR_UNEXPECTED_EOF, "EOF");
else
throw new ParseException(pos, ERROR_UNEXPECTED_UNICODE, c);
}
return (char) value;
}
private void skipDigits() throws IOException {
for (;;) {
if (c == EOI)
return;
if (c < '0' || c > '9')
return;
sb.append((char) c);
read();
}
}
private void skipNQString(boolean[] stop) throws IOException {
for (;;) {
if (c == EOI)
return;
if (c >= 0 && c <= 125 && stop[c])
return;
sb.append((char) c);
read();
}
}
private void skipSpace() throws IOException {
for (;;) {
if (c == EOI)
return;
if (c != ' ' && c != '\r' && c != '\t' && c != '\n')
return;
sb.append((char) c);
read();
}
}
}