JsonParser.java
package zserio.runtime.json;
import java.io.Reader;
/**
* Json Parser.
*
* Parses the JSON on the fly and calls an observer.
*/
class JsonParser
{
/**
* Constructor.
*
* @param reader Text stream to parse.
* @param observer Observer to use.
*/
public JsonParser(Reader reader, Observer observer)
{
this.tokenizer = new JsonTokenizer(reader);
this.observer = observer;
}
/**
* Parses single JSON element from the text stream.
*
* @return True when end-of-file is reached, false otherwise (i.e. another JSON element is present).
*/
public boolean parse()
{
if (tokenizer.getToken() == JsonToken.BEGIN_OF_FILE)
tokenizer.next();
if (tokenizer.getToken() == JsonToken.END_OF_FILE)
return true;
parseElement();
return tokenizer.getToken() == JsonToken.END_OF_FILE;
}
/**
* Gets current line number.
*
* @return Line number.
*/
public int getLine()
{
return tokenizer.getLine();
}
/**
* Gets current column number.
*
* @return Column number.
*/
public int getColumn()
{
return tokenizer.getColumn();
}
/**
* Json parser observer.
*/
static interface Observer {
/**
* Called when a JSON object begins - i.e. on '{'.
*/
public void beginObject();
/**
* Called when a JSON object ends - i.e. on '}'.
*/
public void endObject();
/**
* Called when a JSON array begins - i.e. on '['.
*/
public void beginArray();
/**
* Called when a JSON array ends - i.e. on ']'.
*/
public void endArray();
/**
* Called on a JSON key.
*
* @param key Key value.
*/
public void visitKey(String key);
/**
* Called on a JSON value.
*
* @param value JSON value.
*/
public void visitValue(Object value);
}
private void parseElement()
{
final JsonToken token = tokenizer.getToken();
if (token == JsonToken.BEGIN_ARRAY)
parseArray();
else if (token == JsonToken.BEGIN_OBJECT)
parseObject();
else if (token == JsonToken.VALUE)
parseValue();
else
throwUnexpectedToken(ELEMENT_TOKENS);
}
private void parseArray()
{
observer.beginArray();
final JsonToken token = tokenizer.next();
for (JsonToken elementToken : ELEMENT_TOKENS)
{
if (token == elementToken)
{
parseElements();
break;
}
}
consumeToken(JsonToken.END_ARRAY);
observer.endArray();
}
private void parseElements()
{
parseElement();
while (tokenizer.getToken() == JsonToken.ITEM_SEPARATOR)
{
tokenizer.next();
parseElement();
}
}
private void parseObject()
{
observer.beginObject();
final JsonToken token = tokenizer.next();
if (token == JsonToken.VALUE)
parseMembers();
consumeToken(JsonToken.END_OBJECT);
observer.endObject();
}
private void parseMembers()
{
parseMember();
while (tokenizer.getToken() == JsonToken.ITEM_SEPARATOR)
{
tokenizer.next();
parseMember();
}
}
private void parseMember()
{
checkToken(JsonToken.VALUE);
final Object key = tokenizer.getValue();
if (!(key instanceof String))
{
throw new JsonParserError("JsonParser:" + tokenizer.getLine() + ":" + tokenizer.getColumn() +
": Key must be a string value!");
}
observer.visitKey((String)key);
tokenizer.next();
consumeToken(JsonToken.KEY_SEPARATOR);
parseElement();
}
private void parseValue()
{
observer.visitValue(tokenizer.getValue());
tokenizer.next();
}
private void consumeToken(JsonToken expectedToken)
{
checkToken(expectedToken);
tokenizer.next();
}
private void checkToken(JsonToken expectedToken)
{
if (tokenizer.getToken() != expectedToken)
throwUnexpectedToken(new JsonToken[] {expectedToken});
}
private void throwUnexpectedToken(JsonToken[] expecting)
{
String msg = "JsonParser:" + tokenizer.getLine() + ":" + tokenizer.getColumn() +
": Unexpected token: " + tokenizer.getToken();
final Object value = tokenizer.getValue();
if (value != null)
{
msg += " ('" + value + "')";
}
if (expecting.length == 1)
{
msg += ", expecting " + expecting[0] + "!";
}
else
{
final StringBuilder builder = new StringBuilder();
int i = 0;
for (; i < expecting.length - 1; ++i)
{
builder.append(expecting[i].toString());
builder.append(", ");
}
builder.append(expecting[i].toString());
msg += ", expecting one of [" + builder.toString() + "]!";
}
throw new JsonParserError(msg);
}
static JsonToken[] ELEMENT_TOKENS =
new JsonToken[] {JsonToken.BEGIN_OBJECT, JsonToken.BEGIN_ARRAY, JsonToken.VALUE};
private final JsonTokenizer tokenizer;
private final Observer observer;
}