jscover.mozilla.javascript.json.JsonParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rhino Show documentation
Show all versions of rhino Show documentation
Rhino is an open-source implementation of JavaScript written entirely in
Java. It is typically embedded into Java applications to provide
scripting to end users.
/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package jscover.mozilla.javascript.json;
import jscover.mozilla.javascript.Context;
import jscover.mozilla.javascript.Scriptable;
import jscover.mozilla.javascript.ScriptRuntime;
import java.util.ArrayList;
import java.util.List;
/**
* This class converts a stream of JSON tokens into a JSON value.
*
* See ECMA 15.12.
* @author Raphael Speyer
* @author Hannes Wallnoefer
*/
public class JsonParser {
private Context cx;
private Scriptable scope;
private int pos;
private int length;
private String src;
public JsonParser(Context cx, Scriptable scope) {
this.cx = cx;
this.scope = scope;
}
public synchronized Object parseValue(String json) throws ParseException {
if (json == null) {
throw new ParseException("Input string may not be null");
}
pos = 0;
length = json.length();
src = json;
Object value = readValue();
consumeWhitespace();
if (pos < length) {
throw new ParseException("Expected end of stream at char " + pos);
}
return value;
}
private Object readValue() throws ParseException {
consumeWhitespace();
while (pos < length) {
char c = src.charAt(pos++);
switch (c) {
case '{':
return readObject();
case '[':
return readArray();
case 't':
return readTrue();
case 'f':
return readFalse();
case '"':
return readString();
case 'n':
return readNull();
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
case '-':
return readNumber(c);
default:
throw new ParseException("Unexpected token: " + c);
}
}
throw new ParseException("Empty JSON string");
}
private Object readObject() throws ParseException {
consumeWhitespace();
Scriptable object = cx.newObject(scope);
// handle empty object literal case early
if (pos < length && src.charAt(pos) == '}') {
pos += 1;
return object;
}
String id;
Object value;
boolean needsComma = false;
while (pos < length) {
char c = src.charAt(pos++);
switch(c) {
case '}':
if (!needsComma) {
throw new ParseException("Unexpected comma in object literal");
}
return object;
case ',':
if (!needsComma) {
throw new ParseException("Unexpected comma in object literal");
}
needsComma = false;
break;
case '"':
if (needsComma) {
throw new ParseException("Missing comma in object literal");
}
id = readString();
consume(':');
value = readValue();
long index = ScriptRuntime.indexFromString(id);
if (index < 0) {
object.put(id, object, value);
} else {
object.put((int)index, object, value);
}
needsComma = true;
break;
default:
throw new ParseException("Unexpected token in object literal");
}
consumeWhitespace();
}
throw new ParseException("Unterminated object literal");
}
private Object readArray() throws ParseException {
consumeWhitespace();
// handle empty array literal case early
if (pos < length && src.charAt(pos) == ']') {
pos += 1;
return cx.newArray(scope, 0);
}
List