package com.sabdroidex.utils.json.impl; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.text.NumberFormat; import java.text.ParseException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; /** * Created by Marc on 14/12/13. */ public class JSONParser { private static final int OPEN_BRACE = '{'; private static final int CLOSE_BRACE = '}'; private static final int OPEN_BRACKET = '['; private static final int CLOSE_BRACKET = ']'; private static final int COMMA = ','; private static final int FIELD = '"'; private static final int BAD_FIELD = '\''; private static final int TRUE = 't'; private static final int TRUE_ = 'T'; private static final int FALSE = 'f'; private static final int FALSE_ = 'F'; private static final int NULL = 'n'; private static final int MINUS = '-'; private static final int PLUS = '+'; private static final int ZERO = '0'; private static final int ONE = '1'; private static final int TWO = '2'; private static final int TREE = '3'; private static final int FOUR = '4'; private static final int FIVE = '5'; private static final int SIX = '6'; private static final int SEVEN = '7'; private static final int EIGHT = '8'; private static final int NINE = '9'; private static final int DECIMAL = '.'; private static final int BACKSLASH = '\\'; private boolean badFormat = false; public boolean isBadFormat() { return badFormat; } public void setBadFormat(boolean badFormat) { this.badFormat = badFormat; } private static void setStreamAbsolutePosition(InputStream inputStream, AtomicInteger offset) throws IOException { inputStream.reset(); inputStream.skip(offset.get()); } public Object parse(InputStream inputStream) throws IOException, ParseException { return parse(inputStream, new AtomicInteger(0), null); } public Object parse(InputStream inputStream, AtomicInteger offset, JSONContext parentContext) throws IOException, ParseException { if (inputStream == null) { throw new InvalidJSONFormatException("Null parameter"); } if (parentContext == null) { parentContext = new JSONContext(); } JSONContext jsonContext = new JSONContext(); Map<String, Object> map = new HashMap<String, Object>(); int c = -1; while ((c = inputStream.read()) != -1) { offset.incrementAndGet(); switch (c) { case OPEN_BRACE: parse(inputStream, offset, jsonContext); break; case CLOSE_BRACE: map.put(jsonContext.getElementName(), jsonContext.getElementValue()); parentContext.setElementValue(map); return map; case OPEN_BRACKET: parseArray(inputStream, offset, jsonContext); break; case CLOSE_BRACKET: map.put(jsonContext.getElementName(), jsonContext.getElementValue()); jsonContext.clear(); break; case BAD_FIELD: case FIELD: parseString(inputStream, offset, jsonContext); break; case COMMA: map.put(jsonContext.getElementName(), jsonContext.getElementValue()); jsonContext.clear(); break; case MINUS: case ZERO: case ONE: case TWO: case TREE: case FOUR: case FIVE: case SIX: case SEVEN: case EIGHT: case NINE: case DECIMAL: parseNumber(inputStream, offset, jsonContext); break; case TRUE: case TRUE_: case FALSE: case FALSE_: parseBoolean(inputStream, offset, jsonContext); break; case NULL: parseNull(inputStream, offset, jsonContext); break; } } if (jsonContext.getElementName() == null) { return jsonContext.getElementValue(); } else { map.put(jsonContext.getElementName(), jsonContext.getElementValue()); } return map; } private void parseNumber(InputStream inputStream, AtomicInteger offset, JSONContext jsonContext) throws IOException, ParseException { offset.decrementAndGet(); setStreamAbsolutePosition(inputStream, offset); int c = -1; while ((c = inputStream.read()) != -1) { offset.incrementAndGet(); switch (c) { case MINUS: case ZERO: case ONE: case TWO: case TREE: case FOUR: case FIVE: case SIX: case SEVEN: case EIGHT: case NINE: case DECIMAL: if (jsonContext.getElementValue() == null) { jsonContext.setElementValue("" + (char) c); } else { jsonContext.setElementValue((String) jsonContext.getElementValue() + (char) c); } break; default: if (((String)jsonContext.getElementValue()).contains(".")) { jsonContext.setElementValue(Double.valueOf((String) jsonContext.getElementValue())); } else { NumberFormat format = NumberFormat.getIntegerInstance(); Number number = format.parse((String) jsonContext.getElementValue()); if (!(number.longValue() < Integer.MIN_VALUE || number.longValue() > Integer.MAX_VALUE)) { number = Integer.valueOf(number.intValue()); } jsonContext.setElementValue(number); } offset.decrementAndGet(); setStreamAbsolutePosition(inputStream, offset); return; } } } private void parseArray(InputStream inputStream, AtomicInteger offset, JSONContext jsonContext) throws IOException, ParseException { List elements = new ArrayList(); int c = -1; while ((c = inputStream.read()) != -1) { offset.incrementAndGet(); switch (c) { case OPEN_BRACE: parse(inputStream, offset, jsonContext); break; case BAD_FIELD: case FIELD: parseString(inputStream, offset, jsonContext); break; case MINUS: case ZERO: case ONE: case TWO: case TREE: case FOUR: case FIVE: case SIX: case SEVEN: case EIGHT: case NINE: case DECIMAL: parseNumber(inputStream, offset, jsonContext); break; case COMMA: elements.add(jsonContext.getElementValue()); jsonContext.clearValue(); break; case CLOSE_BRACKET: elements.add(jsonContext.getElementValue()); jsonContext.setElementValue(elements); return; } } } private void parseString(InputStream inputStream, AtomicInteger offset, JSONContext jsonContext) throws IOException { StringWriter stringWriter = new StringWriter(); boolean escaped = false; int c = -1; while ((c = inputStream.read()) != -1) { offset.incrementAndGet(); switch (c) { case BAD_FIELD: if (!badFormat) { escaped = false; stringWriter.append((char) c); break; } case FIELD: if (escaped) { escaped = false; stringWriter.append((char) c); break; } if (jsonContext.getElementName() == null) { jsonContext.setElementName(stringWriter.toString()); } else { jsonContext.setElementValue(stringWriter.toString()); } return; case BACKSLASH: escaped = true; stringWriter.append((char) c); break; default: escaped = false; stringWriter.append((char) c); break; } } } private void parseNull(InputStream inputStream, AtomicInteger offset, JSONContext jsonContext) throws java.io.IOException { int c = -1; while ((c = inputStream.read()) != -1) { offset.incrementAndGet(); switch (c) { default: jsonContext.setElementValue(null); offset.decrementAndGet(); setStreamAbsolutePosition(inputStream, offset); return; } } } private void parseBoolean(InputStream inputStream, AtomicInteger offset, JSONContext jsonContext) throws java.io.IOException { offset.decrementAndGet(); setStreamAbsolutePosition(inputStream, offset); int c = -1; while ((c = inputStream.read()) != -1) { offset.incrementAndGet(); switch (c) { case TRUE: jsonContext.setElementValue(true); break; case FALSE: jsonContext.setElementValue(false); break; default: return; } } } }