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