blob: 34a01ceae77a5b1f77a06114625698c6aa59d4d1 [file] [log] [blame]
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.json;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* JSON object.
*/
public class JsonObject implements JsonValue, Iterable<Pair<String, JsonValue>> {
private static class Iter implements Iterator<Pair<String, JsonValue>> {
private final Iterator<Map.Entry<String, JsonValue>> iter;
Iter(Iterator<Map.Entry<String, JsonValue>> iter) {
this.iter = iter;
}
public boolean hasNext() {
return iter.hasNext();
}
public Pair<String, JsonValue> next() {
final Map.Entry<String, JsonValue> entry = iter.next();
return new Pair<String, JsonValue>(entry.getKey(), entry.getValue());
}
public void remove() {
iter.remove();
}
}
public static JsonObject create() {
return new JsonObject();
}
/**
* Creates a {@link JsonObject} from a valid JSON string. This routine expects
* the first non-whitespace character to be '{', then reads a single encoded
* object from the {@link Reader} and leaves the reader positioned after the
* last '}' character.
*
* @param reader {@link Reader} positioned to contain a JSON object encoded as
* a string.
* @return a valid {@link JsonObject} on success, throws exception on failure.
* @throws JsonException The input string is not in valid JSON format.
* @throws IOException An IO error was encountered before all of the string
* could be read.
*/
public static JsonObject parse(Reader reader) throws JsonException,
IOException {
return JsonObject.parse(new Tokenizer(reader));
}
static JsonObject parse(Tokenizer tokenizer) throws IOException,
JsonException {
final JsonObject object = new JsonObject();
int c = tokenizer.nextNonWhitespace();
if (c != '{') {
throw new JsonException("Payload does not begin with '{'. Got " + c + "("
+ Character.valueOf((char) c) + ")");
}
while (true) {
c = tokenizer.nextNonWhitespace();
switch (c) {
case '}':
// We're done.
return object;
case '"':
tokenizer.back(c);
// Ready to start a key.
final String key = tokenizer.nextString();
if (tokenizer.nextNonWhitespace() != ':') {
throw new JsonException("Invalid object: expecting \":\"");
}
// TODO(knorton): Make sure this key is not already set.
object.put(key, tokenizer.nextValue());
switch (tokenizer.nextNonWhitespace()) {
case ',':
break;
case '}':
return object;
default:
throw new JsonException("Invalid object: expecting } or ,");
}
break;
case ',':
break;
default:
throw new JsonException("Invalid object: ");
}
}
}
private final Map<String, JsonValue> properties = new HashMap<String, JsonValue>();
public JsonObject() {
}
public JsonArray asArray() {
return null;
}
public JsonBoolean asBoolean() {
return null;
}
public JsonNumber asNumber() {
return null;
}
public JsonObject asObject() {
return this;
}
public JsonString asString() {
return null;
}
public JsonObject copyDeeply() {
final JsonObject copy = new JsonObject();
for (Map.Entry<String, JsonValue> entry : properties.entrySet()) {
final JsonValue value = entry.getValue();
copy.properties.put(entry.getKey(), value == null ? null
: value.copyDeeply());
}
return copy;
}
public JsonValue get(String key) {
final JsonValue value = properties.get(key);
return (value == null) ? JsonValue.NULL : value;
}
public boolean isArray() {
return false;
}
public boolean isBoolean() {
return false;
}
public boolean isEmpty() {
return properties.isEmpty();
}
public boolean isNumber() {
return false;
}
public boolean isObject() {
return true;
}
public boolean isString() {
return false;
}
public Iterator<Pair<String, JsonValue>> iterator() {
return new Iter(properties.entrySet().iterator());
}
public void put(String key, boolean val) {
put(key, JsonBoolean.create(val));
}
public void put(String key, double val) {
put(key, JsonNumber.create(val));
}
public void put(String key, JsonValue val) {
properties.put(key, val);
}
public void put(String key, long val) {
put(key, JsonNumber.create(val));
}
public void put(String key, String val) {
put(key, JsonString.create(val));
}
public void write(Writer writer) throws IOException {
boolean first = true;
writer.write('{');
for (Map.Entry<String, JsonValue> e : properties.entrySet()) {
if (!first) {
writer.append(',');
} else {
first = false;
}
JsonString.write(e.getKey(), writer);
writer.append(':');
e.getValue().write(writer);
}
writer.write('}');
}
}