All Downloads are FREE. Search and download functionalities are using the official Maven repository.

base.json.JsonParser Maven / Gradle / Ivy

/**
 * Creative commons Attribution-NonCommercial license.
 *
 * http://creativecommons.org/licenses/by-nc/2.5/au/deed.en_GB
 *
 * NO WARRANTY IS GIVEN OR IMPLIED, USE AT YOUR OWN RISK.
 */
package base.json;

import java.io.BufferedReader;
import java.io.CharArrayReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

public class JsonParser {

	private static final int MAX_TOKEN_LENGTH = 1024*200;

	private List tokens;
	private BufferedReader stream;
	int tokenindex;

	public JsonParser(String jsonString) throws IOException {
		stream = new BufferedReader(new CharArrayReader(jsonString.toCharArray()));
		tokens = new ArrayList();
	}

	public JsonParser(BufferedReader in) {
		tokens = new ArrayList();
		stream = in;
	}

	private Integer wasPutBack;

	private int streamNext() throws IOException {
		if(wasPutBack != null) {
			int i = wasPutBack;
			wasPutBack = null;
			return i;
		}
		return stream.read();
	}

	private void streamPutBack(int i) {
		if(wasPutBack != null) {
			throw new IllegalStateException("Cant put back more than one character");
		}
		wasPutBack = i;
	}

	/**
	 * Reads a JSON array or dictionary.
	 *
	 * @return Either a List, Map, or NULL if a problem occured.
	 * @throws IOException Error occured reading stream.
	 */
	public Object parse() throws IOException {
		tokenindex = 0;

		String token = null;
		while((token = getToken()) != null) {
			tokens.add(token);
		}

		// If no tokens were found, null must be returned.
		if(tokens.size() == 0) {
			return null;
		}

		if(peekToken().equals("{")) {
			return getDictionary();
		}
		if(peekToken().equals("[")) {
			return getArray();
		}

		return null;
	}

	/**
	 * state table:
	 *   1 = no state
	 *   2 = string
	 *   3 = string escape sequence
	 *   4 = true, false or null
	 */
	private String getToken() throws IOException {
		int tokenlength = 0;
		char[] token = new char[MAX_TOKEN_LENGTH];
		int state = 1;

		while(true) {
			int i = streamNext();
			if(i == -1) {
				if(tokenlength == 0) {
					return null;
				}
				return String.copyValueOf(token, 0, tokenlength);
			}
			char c = (char)i;

			switch(state) {
			case 1 :
				if(c == ' ' || c == '\t' || c == '\n' || c == '\r') {
					continue;
				}
				if(c == '{' || c == '}' || c == '[' || c == ']' || c == ',' || c == ':') {
					return ""+c;
				}
				if(c == '"') {
					state = 2;
					continue;
				}
				if(c == 't' || c == 'f' || c == 'n') {
					state = 4;
					token[tokenlength] = c;
					tokenlength++;
					continue;
				}
				if(c >= '0' && c <= '9') {
					state = 5;
					token[tokenlength] = c;
					tokenlength++;
					continue;
				}
				throw new IllegalArgumentException("Unexpected character: " + c);
			case 2 :
				if(c == '"') {
					return String.copyValueOf(token, 0, tokenlength);
				}
				if(tokenlength == MAX_TOKEN_LENGTH) {
					throw new IllegalArgumentException("Token exceeded maximum length of " + MAX_TOKEN_LENGTH + " characters.");
				}
				if(c == '\\') {
					state = 3;
					break;
				}
				token[tokenlength] = c;
				tokenlength++;
				break;
			case 3 :
				if(c == 'n') {
					token[tokenlength] = '\n';
					tokenlength++;
					state = 2;
				} else if(c == '"') {
					token[tokenlength] = '"';
					tokenlength++;
					state = 2;
				} else if(c == '\\') {
					token[tokenlength] = '\\';
					tokenlength++;
					state = 2;
				} else if(c == '/') {
					token[tokenlength] = '/';
					tokenlength++;
					state = 2;
				} else if(c == 'b') {
					token[tokenlength] = '\b';
					tokenlength++;
					state = 2;
				} else if(c == 'f') {
					token[tokenlength] = '\f';
					tokenlength++;
					state = 2;
				} else if(c == 'r') {
					token[tokenlength] = '\r';
					tokenlength++;
					state = 2;
				} else if(c == 't') {
					token[tokenlength] = '\t';
					tokenlength++;
					state = 2;
				} else {
					token[tokenlength] = '\\';
					tokenlength++;
					if(tokenlength == MAX_TOKEN_LENGTH) {
						throw new IllegalArgumentException("Token exceeded maximum length of " + MAX_TOKEN_LENGTH + " characters.");
					}
					token[tokenlength] = c;
					tokenlength++;
					state = 2;
				}
				break;
			case 4 :
				if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
					token[tokenlength] = c;
					tokenlength++;
				}
				String value = String.copyValueOf(token, 0, tokenlength).toLowerCase();
				if(value.equals("true")) {
					tokenlength = 0;
					state = 0;
					return "true";
				}
				if(value.equals("false")) {
					tokenlength = 0;
					state = 0;
					return "false";
				}
				if(value.equals("null")) {
					tokenlength = 0;
					state = 0;
					return "null";
				}
				if(value.length() >= 6) {
					throw new IOException("Invalid JSON, unescaped text in JSON data: " + value);
				}
				break;
			case 5 :
				if(c >= '0' && c <= '9') {
					token[tokenlength] = c;
					tokenlength++;
				} else {
					streamPutBack(i);
					String number = String.copyValueOf(token, 0, tokenlength).toLowerCase();
					tokenlength = 0;
					state = 0;
					return number;
				}
				break;
			default:
				throw new IOException("Unexpected character in JSON file");
			}
		}

		//TODO: huh?
		//if(index!=string.length()) System.out.println("Unexpected character in JSON file");
		//return null;
	}

	private Map getDictionary() {
		if(!peekToken().equals("{")) {
			throw new IllegalArgumentException("Expecting { but found " + peekToken());
		}
		nextToken();
		Map dictionary = new Hashtable<>();

		while(true) {

			// Get name
			String name = nextToken();
			if(name == null) {
				throw new IllegalArgumentException("Expecting name, but found nothing");
			}
			if(name.equals("}")) {
				break;
			}

			// Get separator
			String colon = nextToken();
			if(colon == null) {
				throw new IllegalArgumentException("Expecting colon, but found nothing");
			}
			if(!colon.equals(":")) {
				throw new IllegalArgumentException("Expecting colon, but found nothing");
			}

			// Get value
			String peek = peekToken();
			if(peek.equals("[")) {
				dictionary.put(name, getArray());
			} else {
				if(peek.equals("{")) {
					dictionary.put(name, getDictionary());
				} else {
					dictionary.put(name, nextToken());
				}
			}

			if(peekToken().equals(",")) {
				nextToken();
			} else if(!peekToken().equals("}")) {
				throw new IllegalArgumentException("Expecting , or } but saw " + peekToken());
			}
		}

		return dictionary;
	}

	private String nextToken() {
		if(tokenindex == tokens.size()) {
			return null;
		}
		String token = tokens.get(tokenindex);
		tokenindex++;
		return token;
	}

	private String peekToken() {
		if(tokenindex >= tokens.size()) {
			return null;
		}
		return tokens.get(tokenindex);
	}

	private List getArray() {
		if(!peekToken().equals("[")) {
			throw new IllegalArgumentException("Expecting [ but found " + peekToken());
		}
		nextToken();
		List array = new ArrayList();

		while(true) {
			// Get value
			String peek = peekToken();
			if(peek.equals("[")) {
				array.add(getArray());
			} else if(peek.equals("{")) {
				array.add(getDictionary());
			} else if(!peek.equals("]")) {
				array.add(nextToken());
			}

			if(peekToken().equals(",")) {
				nextToken();
			} else if(peekToken().equals("]")) {
				nextToken();
				break;
			}
		}

		return array;
	}

}