/**
*
*/
package com.trendrr.json.stream;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.trendrr.oss.DynMap;
import com.trendrr.oss.DynMapFactory;
import com.trendrr.oss.FileHelper;
import com.trendrr.oss.exceptions.TrendrrException;
import com.trendrr.oss.exceptions.TrendrrParseException;
/**
*
*
*
* @author Dustin Norlander
* @created May 4, 2012
*
*/
public class JSONStreamParser {
protected static Log log = LogFactory.getLog(JSONStreamParser.class);
protected StringBuilder builder = null;
protected int openBrackets = 0;
protected boolean isQuote = false;
protected char previous = ' ';
protected long numRead = 0;
protected long maxBufferedChars; //max size to write before we assume this is an invalid stream.
/**
* creates a new stream parser. max buffer is the largest allowable buffer for a single json packet.
* @param maxBuffer
*/
public JSONStreamParser(long maxBuffer) {
this.maxBufferedChars = maxBuffer;
}
/**
* creates a new stream parser with a 4mb max buffer
*/
public JSONStreamParser() {
this(FileHelper.megsToBytes(4));
}
public synchronized void reset() {
builder = null;
openBrackets = 0;
isQuote = false;
previous = ' ';
numRead = 0;
}
/**
* parses the passed in string. Will return an empty list if no complete json packet is available. else will
* return all the complete packets possible.
* @param string
* @return
* @throws TrendrrParseException
*/
public synchronized List<DynMap> addString(String string) throws TrendrrParseException {
List<DynMap> retList = new ArrayList<DynMap>();
StringCharacterIterator iter = new StringCharacterIterator(string);
for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
DynMap res = this.addChar(c);
if (res != null)
retList.add(res);
}
return retList;
}
/**
* Add a char to the stream. will return the parsed DynMap when ready, otherwise null.
* throws parse exception if this is not a parseable json stream.
* @param nextChar
* @return
*/
public synchronized DynMap addChar(char nextChar) throws TrendrrParseException {
if (builder == null) {
if (nextChar == '{') {
builder = new StringBuilder("{");
previous = '{';
openBrackets = 1;
} else if (!Character.isWhitespace(nextChar)) {
throw new TrendrrParseException("unable to parse json string, didn't start with {");
}
return null;
}
builder.append(nextChar);
if (!isQuote) {
if (nextChar == '{') {
openBrackets++;
} else if (nextChar == '}') {
openBrackets--;
}
}
if (nextChar == '"' && previous != '\\') {
isQuote = !isQuote;
}
previous = nextChar;
numRead++;
if (numRead > this.maxBufferedChars) {
throw new TrendrrParseException("Read " + this.maxBufferedChars + " chars without a valid json dict");
}
if (openBrackets == 0) {
DynMap dm = DynMapFactory.instanceFromJSON(builder.toString());
if (dm == null) {
throw new TrendrrParseException("unable to parse json string");
}
this.reset();
return dm;
}
return null;
}
}