blob: bc186e71007a0d478a5cf03d4291ecd4b33ee05e [file] [log] [blame]
/*
* Copyright 2008 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.json.client;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsonUtils;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* Represents a JSON object. A JSON object consists of a set of properties.
*/
public class JSONObject extends JSONValue {
/**
* Called from {@link #getUnwrapper()}.
*/
@SuppressWarnings("unused")
private static JavaScriptObject unwrap(JSONObject value) {
return value.jsObject;
}
private final JavaScriptObject jsObject;
public JSONObject() {
this(JavaScriptObject.createObject());
}
/**
* Creates a new JSONObject from the supplied JavaScript value.
*/
public JSONObject(JavaScriptObject jsValue) {
jsObject = jsValue;
}
/**
* Tests whether or not this JSONObject contains the specified property.
*
* @param key the property to search for
* @return <code>true</code> if the JSONObject contains the specified property
*/
public native boolean containsKey(String key) /*-{
return key in this.@com.google.gwt.json.client.JSONObject::jsObject;
}-*/;
/**
* Returns <code>true</code> if <code>other</code> is a {@link JSONObject}
* wrapping the same underlying object.
*/
@Override
public boolean equals(Object other) {
if (!(other instanceof JSONObject)) {
return false;
}
return jsObject.equals(((JSONObject) other).jsObject);
}
/**
* Gets the JSONValue associated with the specified property.
*
* @param key the property to access
* @return the value of the specified property, or <code>null</code> if the
* property does not exist
* @throws NullPointerException if key is <code>null</code>
*/
public JSONValue get(String key) {
if (key == null) {
throw new NullPointerException();
}
return get0(key);
}
/**
* Returns the underlying JavaScript object that this object wraps.
*/
public JavaScriptObject getJavaScriptObject() {
return jsObject;
}
@Override
public int hashCode() {
return jsObject.hashCode();
}
/**
* Returns <code>this</code>, as this is a JSONObject.
*/
@Override
public JSONObject isObject() {
return this;
}
/**
* Returns the set of properties defined on this JSONObject. The returned set
* is immutable.
*/
public Set<String> keySet() {
final String[] keys = computeKeys();
return new AbstractSet<String>() {
@Override
public boolean contains(Object o) {
return (o instanceof String) && containsKey((String) o);
}
@Override
public Iterator<String> iterator() {
return Arrays.asList(keys).iterator();
}
@Override
public int size() {
return keys.length;
}
};
}
/**
* Assign the specified property to the specified value in this JSONObject. If
* the property already has an associated value, it is overwritten.
*
* @param key the property to assign
* @param jsonValue the value to assign
* @return the previous value of the property, or <code>null</code> if the
* property did not exist
* @throws NullPointerException if key is <code>null</code>
*/
public JSONValue put(String key, JSONValue jsonValue) {
if (key == null) {
throw new NullPointerException();
}
JSONValue previous = get(key);
put0(key, jsonValue);
return previous;
}
/**
* Determines the number of properties on this object.
*/
public int size() {
// Must always recheck due to foreign changes. :(
return computeSize();
}
/**
* Converts a JSONObject into a JSON representation that can be used to
* communicate with a JSON service.
*
* @return a JSON string representation of this JSONObject instance
*/
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("{");
boolean first = true;
String[] keys = computeKeys();
for (String key : keys) {
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(JsonUtils.escapeValue(key));
sb.append(":");
sb.append(get(key));
}
sb.append("}");
return sb.toString();
}
@Override
native JavaScriptObject getUnwrapper() /*-{
return @com.google.gwt.json.client.JSONObject::unwrap(Lcom/google/gwt/json/client/JSONObject;);
}-*/;
private native void addAllKeys(Collection<String> s) /*-{
var jsObject = this.@com.google.gwt.json.client.JSONObject::jsObject;
for (var key in jsObject) {
if (jsObject.hasOwnProperty(key)) {
s.@java.util.Collection::add(Ljava/lang/Object;)(key);
}
}
}-*/;
private String[] computeKeys() {
if (GWT.isScript()) {
return computeKeys0(new String[0]);
} else {
List<String> result = new ArrayList<String>();
addAllKeys(result);
return result.toArray(new String[result.size()]);
}
}
private native String[] computeKeys0(String[] result) /*-{
var jsObject = this.@com.google.gwt.json.client.JSONObject::jsObject;
var i = 0;
for (var key in jsObject) {
if (jsObject.hasOwnProperty(key)) {
result[i++] = key;
}
}
return result;
}-*/;
private native int computeSize() /*-{
var jsObject = this.@com.google.gwt.json.client.JSONObject::jsObject;
var size = 0;
for (var key in jsObject) {
if (jsObject.hasOwnProperty(key)) {
++size;
}
}
return size;
}-*/;
private native JSONValue get0(String key) /*-{
var jsObject = this.@com.google.gwt.json.client.JSONObject::jsObject;
var v;
// In Firefox, jsObject.hasOwnProperty(key) requires a primitive string
key = String(key);
if (jsObject.hasOwnProperty(key)) {
v = jsObject[key];
}
var func = @com.google.gwt.json.client.JSONParser::typeMap[typeof v];
var ret = func ? func(v) : @com.google.gwt.json.client.JSONParser::throwUnknownTypeException(Ljava/lang/String;)(typeof v);
return ret;
}-*/;
private native void put0(String key, JSONValue value) /*-{
if (value) {
var func = value.@com.google.gwt.json.client.JSONValue::getUnwrapper()();
this.@com.google.gwt.json.client.JSONObject::jsObject[key] = func(value);
} else {
delete this.@com.google.gwt.json.client.JSONObject::jsObject[key];
}
}-*/;
}