org.mozilla.javascript.json.JsonParser Maven / Gradle / Ivy
Show all versions of rhino-runtime Show documentation
/* -*- 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 org.mozilla.javascript.json;
import java.util.ArrayList;
import java.util.List;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.ScriptRuntime.StringIdOrIndex;
import org.mozilla.javascript.Scriptable;
/**
* 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();
StringIdOrIndex indexObj = ScriptRuntime.toStringIdOrIndex(id);
if (indexObj.getStringId() == null) {
object.put(indexObj.getIndex(), object, value);
} else {
object.put(indexObj.getStringId(), 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