/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores
* CA 94065 USA or visit www.oracle.com if you need additional information or
* have any questions.
*/
package com.sun.lwuit.io.util;
import java.io.IOException;
import java.io.Reader;
import java.util.Hashtable;
import java.util.Vector;
/**
* Fast and dirty parser for JSON content on the web, it essentially returns
* a Hashtable object mapping the object fields to their values. If the value is
* a nester object a nested hashtable is returned.
*
* @author Shai Almog
*/
public class JSONParser implements JSONParseCallback {
private Hashtable state;
private Vector parseStack;
private String currentKey;
/**
* Static method! Parses the given input stream and fires the data into the given callback.
*
* @param i the reader
* @param callback a generic callback to receive the parse events
* @throws IOException if thrown by the stream
*/
public static void parse(Reader i, JSONParseCallback callback) throws IOException {
boolean quoteMode = false;
StringBuffer currentToken = new StringBuffer();
Vector blocks = new Vector();
String currentBlock = "";
String lastKey = null;
try {
while (callback.isAlive()) {
int currentChar = i.read();
if (currentChar < 0) {
return;
}
char c = (char) currentChar;
if (quoteMode) {
switch (c) {
case '"':
String v = currentToken.toString();
callback.stringToken(v);
if (lastKey != null) {
callback.keyValue(lastKey, v);
lastKey = null;
} else {
lastKey = v;
}
currentToken.setLength(0);
quoteMode = false;
continue;
case '\\':
c = (char) i.read();
if (c == 'u') {
String unicode = "" + ((char) i.read()) + ((char) i.read()) + ((char) i.read()) + ((char) i.read());
try {
c = (char) Integer.parseInt(unicode, 16);
} catch (NumberFormatException err) {
// problem in parsing the u notation!
err.printStackTrace();
System.out.println("Error in parsing \\u" + unicode);
}
}
currentToken.append(c);
continue;
}
currentToken.append(c);
} else {
switch (c) {
case 'n':
// check for null
char u = (char) i.read();
char l = (char) i.read();
char l2 = (char) i.read();
if (u == 'u' && l == 'l' && l2 == 'l') {
// this is null
callback.stringToken(null);
if (lastKey != null) {
callback.keyValue(lastKey, null);
lastKey = null;
}
} else {
// parsing error....
System.out.println("Expected null for key value!");
}
continue;
case 't':
// check for true
char a1 = (char) i.read();
char a2 = (char) i.read();
char a3 = (char) i.read();
if (a1 == 'r' && a2 == 'u' && a3 == 'e') {
callback.stringToken("true");
if (lastKey != null) {
callback.keyValue(lastKey, "true");
lastKey = null;
}
} else {
// parsing error....
System.out.println("Expected true for key value!");
}
continue;
case 'f':
// this can either be the start of "false" or the end of a
// fraction number...
if (currentToken.length() > 0) {
currentToken.append('f');
continue;
}
// check for false
char b1 = (char) i.read();
char b2 = (char) i.read();
char b3 = (char) i.read();
char b4 = (char) i.read();
if (b1 == 'a' && b2 == 'l' && b3 == 's' && b4 == 'e') {
callback.stringToken("false");
if (lastKey != null) {
callback.keyValue(lastKey, "false");
lastKey = null;
}
} else {
// parsing error....
System.out.println("Expected false for key value!");
}
continue;
case '{':
currentBlock = getCurrentBlock(lastKey, blocks);
callback.startBlock(currentBlock);
lastKey = null;
continue;
case '}':
if (currentToken.length() > 0) {
try {
callback.numericToken(Double.parseDouble(currentToken.toString()));
if (lastKey != null) {
callback.keyValue(lastKey, currentToken.toString());
lastKey = null;
currentToken.setLength(0);
}
} catch (NumberFormatException err) {
err.printStackTrace();
// this isn't a number!
}
}
if (blocks.size() > 0) {
blocks.removeElementAt(blocks.size() - 1);
}
callback.endBlock(currentBlock);
lastKey = null;
continue;
case '[':
currentBlock = getCurrentBlock(lastKey, blocks);
callback.startArray(currentBlock);
lastKey = null;
continue;
case ']':
if (currentToken.length() > 0) {
try {
callback.numericToken(Double.parseDouble(currentToken.toString()));
if (lastKey != null) {
callback.keyValue(lastKey, currentToken.toString());
lastKey = null;
}
} catch (NumberFormatException err) {
// this isn't a number!
}
}
currentToken.setLength(0);
if (blocks.size() > 0) {
blocks.removeElementAt(blocks.size() - 1);
}
callback.endArray(currentBlock);
lastKey = null;
continue;
case ' ':
case '\r':
case '\t':
case '\n':
// whitespace
continue;
case '"':
quoteMode = true;
continue;
case ':':
case ',':
if (currentToken.length() > 0) {
try {
callback.numericToken(Double.parseDouble(currentToken.toString()));
if (lastKey != null) {
callback.keyValue(lastKey, currentToken.toString());
lastKey = null;
}
} catch (NumberFormatException err) {
// this isn't a number!
}
}
currentToken.setLength(0);
continue;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
case 'x':
case 'd':
case 'l':
currentToken.append(c);
continue;
}
}
}
} catch (Exception err) {
err.printStackTrace();
/*System.out.println();
int current = i.read();
while(current >= 0) {
System.out.print((char)current);
current = i.read();
}*/
i.close();
}
}
private static String getCurrentBlock(String lastKey, Vector blocks) {
String currentBlock = "";
if (blocks.size() > 0) {
currentBlock = (String) blocks.elementAt(blocks.size() - 1);
}
if (lastKey != null && !lastKey.equals(currentBlock)) {
currentBlock = lastKey;
blocks.insertElementAt(currentBlock, blocks.size());
}
return currentBlock;
}
/**
* Parses the given input stream into this object and returns the parse tree
*
* @param i the reader
* @return the parse tree as a hashtable
* @throws IOException if thrown by the stream
*/
public Hashtable parse(Reader i) throws IOException {
state = new Hashtable();
parseStack = new Vector();
currentKey = null;
parse(i, this);
return state;
}
private boolean isStackHash() {
return parseStack.elementAt(parseStack.size() - 1) instanceof Hashtable;
}
private Hashtable getStackHash() {
return (Hashtable) parseStack.elementAt(parseStack.size() - 1);
}
private Vector getStackVec() {
return (Vector) parseStack.elementAt(parseStack.size() - 1);
}
/**
* @inheritDoc
*/
public void startBlock(String blockName) {
if (parseStack.size() == 0) {
parseStack.addElement(state);
} else {
Hashtable newOne = new Hashtable();
if (isStackHash()) {
getStackHash().put(currentKey, newOne);
currentKey = null;
} else {
getStackVec().addElement(newOne);
}
parseStack.addElement(newOne);
}
}
/**
* @inheritDoc
*/
public void endBlock(String blockName) {
parseStack.removeElementAt(parseStack.size() - 1);
}
/**
* @inheritDoc
*/
public void startArray(String arrayName) {
Vector currentVector = new Vector();
// the root of the JSON is an array, we need to wrap it in an assignment
if (parseStack.size() == 0) {
parseStack.addElement(state);
currentKey = "root";
}
if (isStackHash()) {
getStackHash().put(currentKey, currentVector);
currentKey = null;
} else {
getStackVec().addElement(currentVector);
}
parseStack.addElement(currentVector);
}
/**
* @inheritDoc
*/
public void endArray(String arrayName) {
parseStack.removeElementAt(parseStack.size() - 1);
}
/**
* @inheritDoc
*/
public void stringToken(String tok) {
if (isStackHash()) {
if (currentKey == null) {
currentKey = tok;
} else {
if (tok != null) {
getStackHash().put(currentKey, tok);
}
currentKey = null;
}
} else {
getStackVec().addElement(tok);
}
}
/**
* @inheritDoc
*/
public void numericToken(double tok) {
if (isStackHash()) {
getStackHash().put(currentKey, new Double(tok));
currentKey = null;
} else {
getStackVec().addElement(new Double(tok));
}
}
/**
* @inheritDoc
*/
public void keyValue(String key, String value) {
}
/**
* @inheritDoc
*/
public boolean isAlive() {
return true;
}
}