/* ------------------------------------------------------------ */ protected Object parseArray(Source source) { if (source.next() != '[') throw new IllegalStateException(); int size = 0; ArrayList list = null; Object item = null; boolean coma = true; while (source.hasNext()) { char c = source.peek(); switch (c) { case ']': source.next(); switch (size) { case 0: return newArray(0); case 1: Object array = newArray(1); Array.set(array, 0, item); return array; default: return list.toArray(newArray(list.size())); } case ',': if (coma) throw new IllegalStateException(); coma = true; source.next(); break; default: if (Character.isWhitespace(c)) source.next(); else { coma = false; if (size++ == 0) item = contextForArray().parse(source); else if (list == null) { list = new ArrayList(); list.add(item); item = contextForArray().parse(source); list.add(item); item = null; } else { item = contextForArray().parse(source); list.add(item); item = null; } } } } throw new IllegalStateException("unexpected end of array"); }
/* ------------------------------------------------------------ */ protected static void complete(String seek, Source source) { int i = 0; while (source.hasNext() && i < seek.length()) { char c = source.next(); if (c != seek.charAt(i++)) throw new IllegalStateException("Unexpected '" + c + " while seeking \"" + seek + "\""); } if (i < seek.length()) throw new IllegalStateException("Expected \"" + seek + "\""); }
/* ------------------------------------------------------------ */ protected void seekTo(char seek, Source source) { while (source.hasNext()) { char c = source.peek(); if (c == seek) return; if (!Character.isWhitespace(c)) throw new IllegalStateException("Unexpected '" + c + " while seeking '" + seek + "'"); source.next(); } throw new IllegalStateException("Expected '" + seek + "'"); }
/* ------------------------------------------------------------ */ protected Object parseObject(Source source) { if (source.next() != '{') throw new IllegalStateException(); Map<String, Object> map = newMap(); char next = seekTo("\"}", source); while (source.hasNext()) { if (next == '}') { source.next(); break; } String name = parseString(source); seekTo(':', source); source.next(); Object value = contextFor(name).parse(source); map.put(name, value); seekTo(",}", source); next = source.next(); if (next == '}') break; else next = seekTo("\"}", source); } String classname = (String) map.get("class"); if (classname != null) { try { Class c = Loader.loadClass(JSON.class, classname); return convertTo(c, map); } catch (ClassNotFoundException e) { LOG.warn(e); } } return map; }
/* ------------------------------------------------------------ */ protected char seekTo(String seek, Source source) { while (source.hasNext()) { char c = source.peek(); if (seek.indexOf(c) >= 0) { return c; } if (!Character.isWhitespace(c)) throw new IllegalStateException( "Unexpected '" + c + "' while seeking one of '" + seek + "'"); source.next(); } throw new IllegalStateException("Expected one of '" + seek + "'"); }
/* ------------------------------------------------------------ */ public Number parseNumber(Source source) { boolean minus = false; long number = 0; StringBuilder buffer = null; longLoop: while (source.hasNext()) { char c = source.peek(); switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': number = number * 10 + (c - '0'); source.next(); break; case '-': case '+': if (number != 0) throw new IllegalStateException("bad number"); minus = true; source.next(); break; case '.': case 'e': case 'E': buffer = new StringBuilder(16); if (minus) buffer.append('-'); buffer.append(number); buffer.append(c); source.next(); break longLoop; default: break longLoop; } } if (buffer == null) return minus ? -1 * number : number; doubleLoop: while (source.hasNext()) { char c = source.peek(); switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': case '.': case '+': case 'e': case 'E': buffer.append(c); source.next(); break; default: break doubleLoop; } } return new Double(buffer.toString()); }
/* ------------------------------------------------------------ */ protected String parseString(Source source) { if (source.next() != '"') throw new IllegalStateException(); boolean escape = false; StringBuilder b = null; final char[] scratch = source.scratchBuffer(); if (scratch != null) { int i = 0; while (source.hasNext()) { if (i >= scratch.length) { // we have filled the scratch buffer, so we must // use the StringBuffer for a large string b = new StringBuilder(scratch.length * 2); b.append(scratch, 0, i); break; } char c = source.next(); if (escape) { escape = false; switch (c) { case '"': scratch[i++] = '"'; break; case '\\': scratch[i++] = '\\'; break; case '/': scratch[i++] = '/'; break; case 'b': scratch[i++] = '\b'; break; case 'f': scratch[i++] = '\f'; break; case 'n': scratch[i++] = '\n'; break; case 'r': scratch[i++] = '\r'; break; case 't': scratch[i++] = '\t'; break; case 'u': char uc = (char) ((TypeUtil.convertHexDigit((byte) source.next()) << 12) + (TypeUtil.convertHexDigit((byte) source.next()) << 8) + (TypeUtil.convertHexDigit((byte) source.next()) << 4) + (TypeUtil.convertHexDigit((byte) source.next()))); scratch[i++] = uc; break; default: scratch[i++] = c; } } else if (c == '\\') { escape = true; continue; } else if (c == '\"') { // Return string that fits within scratch buffer return toString(scratch, 0, i); } else scratch[i++] = c; } // Missing end quote, but return string anyway ? if (b == null) return toString(scratch, 0, i); } else b = new StringBuilder(getStringBufferSize()); // parse large string into string buffer final StringBuilder builder = b; while (source.hasNext()) { char c = source.next(); if (escape) { escape = false; switch (c) { case '"': builder.append('"'); break; case '\\': builder.append('\\'); break; case '/': builder.append('/'); break; case 'b': builder.append('\b'); break; case 'f': builder.append('\f'); break; case 'n': builder.append('\n'); break; case 'r': builder.append('\r'); break; case 't': builder.append('\t'); break; case 'u': char uc = (char) ((TypeUtil.convertHexDigit((byte) source.next()) << 12) + (TypeUtil.convertHexDigit((byte) source.next()) << 8) + (TypeUtil.convertHexDigit((byte) source.next()) << 4) + (TypeUtil.convertHexDigit((byte) source.next()))); builder.append(uc); break; default: builder.append(c); } } else if (c == '\\') { escape = true; continue; } else if (c == '\"') break; else builder.append(c); } return builder.toString(); }
/* ------------------------------------------------------------ */ public Object parse(Source source) { int comment_state = 0; // 0=no comment, 1="/", 2="/*", 3="/* *" -1="//" while (source.hasNext()) { char c = source.peek(); // handle // or /* comment if (comment_state == 1) { switch (c) { case '/': comment_state = -1; break; case '*': comment_state = 2; } } // handle /* */ comment else if (comment_state > 1) { switch (c) { case '*': comment_state = 3; break; case '/': if (comment_state == 3) comment_state = 0; else comment_state = 2; break; default: comment_state = 2; } } // handle // comment else if (comment_state < 0) { switch (c) { case '\r': case '\n': comment_state = 0; break; default: break; } } // handle unknown else { switch (c) { case '{': return parseObject(source); case '[': return parseArray(source); case '"': return parseString(source); case '-': return parseNumber(source); case 'n': complete("null", source); return null; case 't': complete("true", source); return Boolean.TRUE; case 'f': complete("false", source); return Boolean.FALSE; case 'u': complete("undefined", source); return null; case 'N': complete("NaN", source); return null; case '/': comment_state = 1; break; default: if (Character.isDigit(c)) return parseNumber(source); else if (Character.isWhitespace(c)) break; return handleUnknown(source, c); } } source.next(); } return null; }
/* ------------------------------------------------------------ */ public Object parse(Source source, boolean stripOuterComment) { int comment_state = 0; // 0=no comment, 1="/", 2="/*", 3="/* *" -1="//" if (!stripOuterComment) return parse(source); int strip_state = 1; // 0=no strip, 1=wait for /*, 2= wait for */ Object o = null; while (source.hasNext()) { char c = source.peek(); // handle // or /* comment if (comment_state == 1) { switch (c) { case '/': comment_state = -1; break; case '*': comment_state = 2; if (strip_state == 1) { comment_state = 0; strip_state = 2; } } } // handle /* */ comment else if (comment_state > 1) { switch (c) { case '*': comment_state = 3; break; case '/': if (comment_state == 3) { comment_state = 0; if (strip_state == 2) return o; } else comment_state = 2; break; default: comment_state = 2; } } // handle // comment else if (comment_state < 0) { switch (c) { case '\r': case '\n': comment_state = 0; default: break; } } // handle unknown else { if (!Character.isWhitespace(c)) { if (c == '/') comment_state = 1; else if (c == '*') comment_state = 3; else if (o == null) { o = parse(source); continue; } } } source.next(); } return o; }