/** * 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 org.waveprotocol.wave.communication.gwt; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JavaScriptException; import com.google.gwt.core.client.JavaScriptObject; import org.waveprotocol.wave.communication.json.JsonException; import org.waveprotocol.wave.communication.json.RawStringData; /** * Implementation of a JSON message. * This is done by extending JavaScriptObject to minimise the size of the resulting code. * */ public class JsonMessage extends JavaScriptObject { private static final JsonSerializer serializer; private static boolean registerToString = false; static { if (GWT.isClient() && BrowserAidedJsonSerailizer.hasBrowserSupport()) { serializer = new BrowserAidedJsonSerailizer(); } else { serializer = new JavascriptJsonSerializer(); } } /** * Protected constructor. Instantiate via static create method. */ protected JsonMessage() { } /** * @return a new empty JsonMessage. */ public static JsonMessage createJsonMessage() { JsonMessage instance = JavaScriptObject.createObject().cast(); registerNativeJsonMessageToString(instance); return instance; } /** * Delete all existing properties on this object. */ public final native void clear() /*-{ for (var p in this) { if (this.hasOwnProperty(p) && typeof this[p] != "function") { delete p; } } }-*/; /** * Copy the contents of an existing JsonMessage into this one. * @param message */ public final native void copyFrom(JsonMessage message) /*-{ var clone = function (input) { if (input === undefined || input === null) { return null; } if (typeof input == 'object' && input.constructor === Array) { var output = []; for (var property = 0; property < input.length; ++property) { output[property] = clone(input[property]); } return output; } if (typeof input == 'object') { var output = {}; for (var property in input) { if (input.hasOwnProperty(property)) { output[property] = clone(input[property]); } } return output; } return input; }; // delete any existing properties for (var property in this) { if (this.hasOwnProperty(property) && typeof this[property] != "function") { delete property; } } // copy over new properties for (var property in message) { if (message.hasOwnProperty(property)) { this[property] = clone(message[property]); } } }-*/; /** * Convert this JsonMessage into a JSON String. * @return a JSON string representation. */ public final String toJson() { return serializer.serialize(this); } public static <T extends JsonMessage> T parse(String json) throws JsonException { return createJsonMessage(json).<T>cast(); } /** * Create a JsonMessage object from the given json * @param json The JSON String to load from. * @return true if evaluation is successful, false otherwise. */ public static JsonMessage createJsonMessage(String json) throws JsonException { try { JsonMessage obj = (JsonMessage) serializer.parse(json); registerNativeJsonMessageToString(obj); return obj; } catch (JavaScriptException e) { throw new JsonException(e); } } /** * Create a JsonMessage object from the given json * @param json The JSON String to load from. * @return true if evaluation is successful, false otherwise. */ public static JsonMessage createJsonMessage(RawStringData data) throws JsonException { try { JsonMessage obj = (JsonMessage) serializer.parse(data.getBaseString()); JsonHelper.registerRawStringData(obj, data); registerNativeJsonMessageToString(obj); return obj; } catch (JavaScriptException e) { throw new JsonException(e); } } /** * JSON implementation of equals() since we can override it. * * TODO(user): Rename this to isEqualTo() once GWT compiler is fixed to * not crash when another interface defines this method and is implemented by a subclass * of JsonMessage. * * @return true if the other object is the same as this object. */ public final native boolean nativeIsEqualTo(Object toCompare) /*-{ var same = function (objectA, objectB) { if (typeof objectA == "object" && typeof objectB == "object") { // Check all A is in B for (var property in objectA) { if (objectA.hasOwnProperty(property) && typeof objectA[property] != "function") { if (!same(objectA[property], objectB[property])) { return false; } } } // Check all B is in A for (var property in objectB) { // Something in B we don't know in A. if (objectB.hasOwnProperty(property) && typeof objectB[property] != "function" && !objectA.hasOwnProperty(property)) { return false; } } return true; } else { return objectA == objectB; } }; return same(this, toCompare); }-*/; /** Static version of toJson to allow us to call from JSNI */ @SuppressWarnings("unused") // Called from JSNI private static final String jsonMessageToJson(JsonMessage instance) { return instance.toJson(); } /** Enables toString on JSOs */ public static void enableJsoToString() { registerToString = true; } /** Disables toString on JSOs */ public static void disableJsoToString() { registerToString = false; } protected static final void registerNativeJsonMessageToString(JsonMessage instance) { if (registerToString) { nativeRegisterNativeJsonMessageToString(instance); } } /** * Recursively registers a native toString function on the object. * This toString method will be invoked from JavaScriptObject.toString(), * allowing us to print JSON when toString() is called on a JsonMessage. */ private static final native void nativeRegisterNativeJsonMessageToString(JsonMessage instance) /*-{ instance.toString = function () { return ( @org.waveprotocol.wave.communication.gwt.JsonMessage::jsonMessageToJson(Lorg/waveprotocol/wave/communication/gwt/JsonMessage;) (this)); } // set toString on all sub objects. for (var property in instance) { if (instance.hasOwnProperty(property) && typeof instance[property] == "object") { @org.waveprotocol.wave.communication.gwt.JsonMessage::registerNativeJsonMessageToString(Lorg/waveprotocol/wave/communication/gwt/JsonMessage;) (instance[property]); } } }-*/; }