001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.json.client;
017:
018: import com.google.gwt.core.client.JavaScriptObject;
019:
020: import java.util.HashSet;
021: import java.util.Set;
022:
023: /**
024: * Represents a JSON object. A JSON object is a map of string-based keys onto a
025: * set of {@link com.google.gwt.json.client.JSONValue} objects.
026: */
027: public class JSONObject extends JSONValue {
028:
029: private static native void addAllKeysFromJavascriptObject(
030: Set<String> s, JavaScriptObject javaScriptObject) /*-{
031: for(var key in javaScriptObject) {
032: s.@java.util.Set::add(Ljava/lang/Object;)(key);
033: }
034: }-*/;
035:
036: private static native boolean containsBack(
037: JavaScriptObject backStore, String key) /*-{
038: key = String(key);
039: return Object.prototype.hasOwnProperty.call(backStore, key);
040: }-*/;
041:
042: private static native JSONValue getFront(
043: JavaScriptObject frontStore, String key) /*-{
044: key = String(key);
045: return Object.prototype.hasOwnProperty.call(frontStore, key) ? frontStore[key] : null;
046: }-*/;
047:
048: private static native void putFront(JavaScriptObject frontStore,
049: String key, JSONValue jsonValue) /*-{
050: frontStore[String(key)] = jsonValue;
051: }-*/;
052:
053: private static native JavaScriptObject removeBack(
054: JavaScriptObject backStore, String key) /*-{
055: key = String(key);
056: var result = backStore[key];
057: delete backStore[key];
058: if (typeof result != 'object') {
059: result = Object(result);
060: }
061: return result;
062: }-*/;
063:
064: private final JavaScriptObject backStore;
065:
066: private final JavaScriptObject frontStore = JavaScriptObject
067: .createObject();
068:
069: public JSONObject() {
070: backStore = JavaScriptObject.createObject();
071: }
072:
073: /**
074: * Creates a new JSONObject from the supplied JavaScript value.
075: */
076: public JSONObject(JavaScriptObject jsValue) {
077: backStore = jsValue;
078: }
079:
080: /**
081: * Tests whether or not this JSONObject contains the specified key.
082: *
083: * We use Object.hasOwnProperty here to verify that a given key is specified
084: * on this object rather than a superclass (such as standard properties
085: * defined on Object).
086: *
087: * @param key the key to search for
088: * @return <code>true</code> if the JSONObject contains the specified key
089: */
090: public boolean containsKey(String key) {
091: return get(key) != null;
092: }
093:
094: /**
095: * Gets the JSONValue associated with the specified key.
096: *
097: * We use Object.hasOwnProperty here to verify that a given key is specified
098: * on this object rather than a superclass (such as standard properties
099: * defined on Object).
100: *
101: * @param key the key to search for
102: * @return if found, the value associated with the specified key, or
103: * <code>null</code> otherwise
104: */
105: public JSONValue get(String key) {
106: if (key == null) {
107: return null;
108: }
109: JSONValue result = getFront(frontStore, key);
110: if (result == null && containsBack(backStore, key)) {
111: JavaScriptObject jso = removeBack(backStore, key);
112: result = JSONParser.buildValue(jso);
113: putFront(frontStore, key, result);
114: }
115: return result;
116: }
117:
118: /**
119: * Returns <code>this</code>, as this is a JSONObject.
120: */
121: @Override
122: public JSONObject isObject() {
123: return this ;
124: }
125:
126: /**
127: * Returns keys for which this JSONObject has associations.
128: *
129: * @return array of keys for which there is a value
130: */
131: public Set<String> keySet() {
132: Set<String> keySet = new HashSet<String>();
133: addAllKeysFromJavascriptObject(keySet, frontStore);
134: addAllKeysFromJavascriptObject(keySet, backStore);
135: return keySet;
136: }
137:
138: /**
139: * Maps the specified key to the specified value in this JSONObject. If the
140: * specified key already has an associated value, it is overwritten.
141: *
142: * @param key the key to associate with the specified value
143: * @param jsonValue the value to associate with this key
144: * @return if one existed, the previous value associated with the key, or
145: * <code>null</code> otherwise
146: * @throws NullPointerException if key is <code>null</code>
147: */
148: public JSONValue put(String key, JSONValue jsonValue) {
149: if (key == null) {
150: throw new NullPointerException();
151: }
152: JSONValue previous = get(key);
153: putFront(frontStore, key, jsonValue);
154: return previous;
155: }
156:
157: /**
158: * Determines the number of keys on this object.
159: */
160: public int size() {
161: return keySet().size();
162: }
163:
164: /**
165: * Converts a JSONObject into a JSON representation that can be used to
166: * communicate with a JSON service.
167: *
168: * @return a JSON string representation of this JSONObject instance
169: */
170: @Override
171: public native String toString() /*-{
172: for (var key in this.@com.google.gwt.json.client.JSONObject::backStore) {
173: // Wrap everything in backStore so that frontStore is canonical.
174: this.@com.google.gwt.json.client.JSONObject::get(Ljava/lang/String;)(key);
175: }
176: var out = [];
177: out.push("{");
178: var first = true;
179: for (var key in this.@com.google.gwt.json.client.JSONObject::frontStore) {
180: if(first) {
181: first = false;
182: } else {
183: out.push(", ");
184: }
185: var subObj =
186: (this.@com.google.gwt.json.client.JSONObject::frontStore[key]).
187: @com.google.gwt.json.client.JSONValue::toString()();
188: out.push("\"");
189: out.push(key);
190: out.push("\":");
191: out.push(subObj);
192: }
193: out.push("}")
194: return out.join("");
195: }-*/;
196: }
|