All Downloads are FREE. Search and download functionalities are using the official Maven repository.

playn.html.HtmlJson Maven / Gradle / Ivy

There is a newer version: 2.0.8
Show newest version
/**
 * Copyright 2010 The PlayN Authors
 *
 * 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 playn.html;

import playn.core.Json;
import playn.core.json.JsonImpl;
import playn.core.json.JsonParserException;
import playn.core.json.JsonSink;
import playn.core.json.JsonTypedArray;

import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.core.client.JavaScriptObject;

/**
 * HTML implementation of {@link Json}. We're forced to wrap and unwrap JS objects here to work
 * around GWT's development mode idiosyncrasies. Thankfully the wrapping is all elided once compiled
 * to native code.
 */
public class HtmlJson extends JsonImpl implements Json {
  static final class HtmlArray extends JavaScriptObject implements Json.Array {
    protected HtmlArray() {
    }

    @Override
    public void add(int index, java.lang.Object value) {
      // splice won't work as expected if the index is past the end of the array - it appends in
      // that case. Using set here instead to be more consistent.
      if (index > length())
        set0(index, wrapNative(value));
      else
        splice0(index, 0, wrapNative(value));
    }

    public void add(java.lang.Object value) {
      push0(wrapNative(value));
    }

    @Override
    public Json.Array getArray(int index) {
      return getArray(index, (Json.Array) null);
    }

    @Override
    public final Json.Array getArray(int index, Json.Array dflt) {
      return isValueArray(get0(index)) ? (Json.Array) unwrap0(get0(index)) : dflt;
    }

    @Override
    public boolean getBoolean(int index) {
      return getBoolean(index, false);
    }

    @Override
    public final boolean getBoolean(int index, boolean dflt) {
      return isValueBoolean(get0(index)) ? unwrapBoolean0(get0(index)) : dflt;
    }

    @Override
    public float getNumber(int index) {
      return (float) getDouble(index, 0);
    }

    @Override
    public float getNumber(int index, float dflt) {
      return (float) getDouble(index, dflt);
    }

    @Override
    public double getDouble(int index) {
      return getDouble(index, 0);
    }

    @Override
    public final double getDouble(int index, double dflt) {
      return isValueNumber(get0(index)) ? unwrapDouble0(get0(index)) : dflt;
    }

    @Override
    public int getInt(int index) {
      return (int) getDouble(index, 0);
    }

    @Override
    public int getInt(int index, int dflt) {
      return (int) getDouble(index, dflt);
    }

    @Override
    public long getLong(int index) {
      return (long) getDouble(index, 0);
    }

    @Override
    public long getLong(int index, long dflt) {
      return (long) getDouble(index, dflt);
    }

    @Override
    public Object getObject(int index) {
      return getObject(index, null);
    }

    @Override
    public final Json.Object getObject(int index, Json.Object dflt) {
      return isValueObject(get0(index)) ? (Json.Object) unwrap0(get0(index)) : dflt;
    }

    @Override
    public String getString(int index) {
      return getString(index, null);
    }

    @Override
    public final String getString(int index, String dflt) {
      return isValueString(get0(index)) ? (String) unwrap0(get0(index)) : dflt;
    }

    @Override
    public final  TypedArray getArray(int index, Class arrayType) {
      return new JsonTypedArray(getArray(index), arrayType);
    }

    @Override
    public boolean isArray(int index) {
      return isValueArray(get0(index));
    }

    @Override
    public boolean isBoolean(int index) {
      return isValueBoolean(get0(index));
    }

    @Override
    public native boolean isNull(int index) /*-{
      return this[index] == null;
    }-*/;

    @Override
    public boolean isNumber(int index) {
      return isValueNumber(get0(index));
    }

    @Override
    public boolean isObject(int index) {
      return isValueObject(get0(index));
    }

    @Override
    public boolean isString(int index) {
      return isValueString(get0(index));
    }

    @Override
    public final native int length() /*-{
      return this.length;
    }-*/;

    private final native java.lang.Object get0(int index) /*-{
      return @com.google.gwt.core.client.GWT::isProdMode()() ? this[index] : [ this[index] ];
    }-*/;

    @Override
    public native void remove(int index) /*-{
      // splice removes from the end if negative numbers are passed in
      index >= 0 && this.splice(index, 1);
    }-*/;

    @Override
    public void set(int index, java.lang.Object value) {
      set0(index, wrapNative(value));
    }

    @Override
    public > JsonSink write(JsonSink sink) {
      for (int i = 0; i < length(); i++) {
        java.lang.Object o = get0(i);
        if (o == null || isValueString(o))
          sink.value(unwrapString0(o));
        else if (isValueArray(o))
          sink.array((Json.Array) o);
        else if (isValueObject(o))
          sink.object((Json.Object) o);
        else if (isValueBoolean(o))
          sink.value(unwrapBoolean0(o));
        else if (isValueNumber(o))
          sink.value(unwrapDouble0(o));
        else
          throw new IllegalStateException("Invalid value inside JSON array");
      }
      return sink;
    }

    private native void push0(java.lang.Object value) /*-{
      this.push(@com.google.gwt.core.client.GWT::isProdMode()() ? value : value[0]);
    }-*/;

    private native void set0(int index, java.lang.Object value) /*-{
      this[index] = @com.google.gwt.core.client.GWT::isProdMode()() ? value : value[0];
    }-*/;

    private native void splice0(int index, int count, java.lang.Object value) /*-{
      this.splice(index, count,
          @com.google.gwt.core.client.GWT::isProdMode()() ? value : value[0]);
    }-*/;
  }

  static final class HtmlObject extends JavaScriptObject implements Json.Object {
    protected HtmlObject() {
    }

    @Override
    public Array getArray(String key) {
      return getArray(key, (Json.Array) null);
    }

    @Override
    public final Array getArray(String key, Json.Array dflt) {
      return isValueArray(get0(key)) ? (Json.Array) unwrap0(get0(key)) : dflt;
    }

    @Override
    public boolean getBoolean(String key) {
      return getBoolean(key, false);
    }

    @Override
    public final boolean getBoolean(String key, boolean dflt) {
      return isValueBoolean(get0(key)) ? unwrapBoolean0(get0(key)) : dflt;
    }

    @Override
    public float getNumber(String key) {
      return (float) getDouble(key, 0);
    }

    @Override
    public float getNumber(String key, float dflt) {
      return (float) getDouble(key, dflt);
    }

    @Override
    public double getDouble(String key) {
      return getDouble(key, 0);
    }

    @Override
    public final double getDouble(String key, double dflt) {
      return isValueNumber(get0(key)) ? unwrapDouble0(get0(key)) : dflt;
    }

    @Override
    public int getInt(String key) {
      return (int) getDouble(key, 0);
    }

    @Override
    public int getInt(String key, int dflt) {
      return (int) getDouble(key, dflt);
    }

    @Override
    public long getLong(String key) {
      return (long) getDouble(key, 0);
    }

    @Override
    public long getLong(String key, long dflt) {
      return (long) getDouble(key, dflt);
    }

    @Override
    public Object getObject(String key) {
      return getObject(key, null);
    }

    @Override
    public final Json.Object getObject(String key, Json.Object dflt) {
      return isValueObject(get0(key)) ? (Json.Object) unwrap0(get0(key)) : dflt;
    }

    @Override
    public String getString(String key) {
      return getString(key, null);
    }

    @Override
    public final String getString(String key, String dflt) {
      return isValueString(get0(key)) ? (String) unwrap0(get0(key)) : dflt;
    }

    @Override
    public  TypedArray getArray(String key, Class valueType, TypedArray dflt) {
      Json.Array array = getArray(key);
      if (array == null)
        return dflt;
      return new JsonTypedArray(array, valueType);
    }

    @Override
    public final  TypedArray getArray(String key, Class arrayType) {
      return new JsonTypedArray(getArray(key), arrayType);
    }

    @Override
    public final native boolean containsKey(String key) /*-{
      return this.hasOwnProperty(key);
    }-*/;

    @Override
    public TypedArray keys() {
      return new JsonTypedArray(getNativeKeys(), String.class);
    }

    @Override
    public boolean isArray(String key) {
      return isValueArray(get0(key));
    }

    @Override
    public boolean isBoolean(String key) {
      return isValueBoolean(get0(key));
    }

    @Override
    public native boolean isNull(String key) /*-{
      return this[key] == null;
    }-*/;

    @Override
    public boolean isNumber(String key) {
      return isValueNumber(get0(key));
    }

    @Override
    public boolean isObject(String key) {
      return isValueObject(get0(key));
    }

    @Override
    public boolean isString(String key) {
      return isValueString(get0(key));
    }

    @Override
    public void put(String key, java.lang.Object value) {
      put0(key, wrapNative(value));
    }

    @Override
    public native void remove(String key) /*-{
      delete this[key];
    }-*/;

    @Override
    public > JsonSink write(JsonSink sink) {
      for (String key : keys()) {
        java.lang.Object o = get0(key);
        if (o == null || isValueString(o))
          sink.value(key, unwrapString0(o));
        else if (isValueArray(o))
          sink.array(key, (Json.Array) o);
        else if (isValueObject(o))
          sink.object(key, (Json.Object) o);
        else if (isValueBoolean(o))
          sink.value(key, getBoolean(key));
        else if (isValueNumber(o))
          sink.value(key, getDouble(key));
        else
          throw new IllegalStateException("Invalid value inside JSON object");
      }
      return sink;
    }

    private final native JavaScriptObject get0(String key) /*-{
      return @com.google.gwt.core.client.GWT::isProdMode()() ? this[key] : [ this[key] ];
    }-*/;

    private final native Array getNativeKeys() /*-{
      if (Object.prototype.keys) {
        return this.keys();
      }
      var keys = [];
      for ( var key in this)
        if (this.hasOwnProperty(key)) {
          keys.push(key);
        }
      return keys;
    }-*/;

    private native void put0(String key, java.lang.Object value) /*-{
      this[key] = @com.google.gwt.core.client.GWT::isProdMode()() ? value : value[0];
    }-*/;

  }

  @Override
  public Array createArray() {
    return (Json.Array) JavaScriptObject.createArray().cast();
  }

  @Override
  public Object createObject() {
    return (Json.Object) JavaScriptObject.createObject().cast();
  }

  @Override
  public Object parse(String json) throws JsonParserException {
    try {
      JavaScriptObject jsonParse = jsonParse(json);
      if (!isValueObject(jsonParse))
        throw new JsonParserException(null, "Input JSON was not an object", -1, -1, -1);
      HtmlObject object = (HtmlObject) unwrap0(jsonParse);
      return object;
    } catch (JavaScriptException e) {
      throw new JsonParserException(e, "Failed to parse JSON", -1, -1, -1);
    }
  }

  @Override
  public Array parseArray(String json) throws JsonParserException {
    try {
      JavaScriptObject jsonParse = jsonParse(json);
      if (!isValueArray(jsonParse))
        throw new JsonParserException(null, "Input JSON was not an array", -1, -1, -1);
      HtmlArray array = (HtmlArray) unwrap0(jsonParse);
      return array;
    } catch (JavaScriptException e) {
      throw new JsonParserException(e, "Failed to parse JSON", -1, -1, -1);
    }
  }

  @Override
  public boolean isArray(java.lang.Object o) {
    return isObjectAnArray(o);
  }

  @Override
  public boolean isObject(java.lang.Object o) {
    return isObjectAnObject(o);
  }

  /**
   * Static method for use from JsonTypes.
   */
  public static boolean isObjectAnArray(java.lang.Object o) {
    return o instanceof JavaScriptObject && isValueArray(wrap0(o));
  }

  /**
   * Static method for use from JsonTypes.
   */
  public static boolean isObjectAnObject(java.lang.Object o) {
    return o instanceof JavaScriptObject && isValueObject(wrap0(o));
  }

  private static native String unwrapString0(java.lang.Object value) /*-{
    return value;
  }-*/;

  private static native boolean isValueArray(java.lang.Object value) /*-{
    return (@com.google.gwt.core.client.GWT::isProdMode()() ? value : value[0]) instanceof Array;
  }-*/;

  private static native boolean isValueBoolean(java.lang.Object value) /*-{
    return typeof (@com.google.gwt.core.client.GWT::isProdMode()() ? value : value[0]) == "boolean";
  }-*/;

  private static native boolean isValueNumber(java.lang.Object value) /*-{
    return typeof (@com.google.gwt.core.client.GWT::isProdMode()() ? value : value[0]) == "number";
  }-*/;

  private static native boolean isValueObject(java.lang.Object value) /*-{
    return (@com.google.gwt.core.client.GWT::isProdMode()() ? value : value[0]) != null
        && typeof (@com.google.gwt.core.client.GWT::isProdMode()() ? value : value[0]) == "object"
        && !((@com.google.gwt.core.client.GWT::isProdMode()() ? value : value[0]) instanceof Array);
  }-*/;

  private static native boolean isValueString(java.lang.Object value) /*-{
    return typeof (@com.google.gwt.core.client.GWT::isProdMode()() ? value : value[0]) == "string";
  }-*/;

  private static native JavaScriptObject jsonParse(String json) /*-{
    return @com.google.gwt.core.client.GWT::isProdMode()() ? JSON.parse(json) : [ JSON.parse(json) ];
  }-*/;

  private static native java.lang.Object unwrap0(java.lang.Object value) /*-{
    return (@com.google.gwt.core.client.GWT::isProdMode()() ? value : value[0]);
  }-*/;

  private static native boolean unwrapBoolean0(java.lang.Object value) /*-{
    return (@com.google.gwt.core.client.GWT::isProdMode()() ? value : value[0]);
  }-*/;

  private static native double unwrapDouble0(java.lang.Object value) /*-{
    return (@com.google.gwt.core.client.GWT::isProdMode()() ? value : value[0]);
  }-*/;

  private static native java.lang.Object wrap0(double value) /*-{
    return @com.google.gwt.core.client.GWT::isProdMode()() ? value : [ value ];
  }-*/;

  private static native java.lang.Object wrap0(boolean value) /*-{
    return @com.google.gwt.core.client.GWT::isProdMode()() ? value : [ value ];
  }-*/;

  private static native java.lang.Object wrap0(java.lang.Object value) /*-{
    return @com.google.gwt.core.client.GWT::isProdMode()() ? value : [ value ];
  }-*/;

  private static java.lang.Object wrapNative(java.lang.Object value) {
    // String and null are safely coerced directly to JavaScriptObject
    if (value == null || value instanceof String)
      return wrap0(value);

    // If it's a JavaScriptObject, assume that it's safely an array or object
    if (value instanceof JavaScriptObject)
      return wrap0(value);

    // Treat all numbers as doubles, since that's basically what they all are under the hood in
    // HTML mode
    if (value instanceof Number)
      return wrap0(((Number) value).doubleValue());

    if (value instanceof Boolean)
      return wrap0(((Boolean) value).booleanValue());

    throw new IllegalArgumentException("Invalid JSON type");
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy