package org.swellrt.beta.model.js; import java.util.ArrayList; import org.swellrt.beta.model.IllegalCastException; import org.swellrt.beta.model.SMap; import org.swellrt.beta.model.SNode; import org.swellrt.beta.model.SUtils; import org.waveprotocol.wave.client.common.util.JsoView; import com.google.gwt.core.client.JavaScriptObject; public class SMapJs implements SMap { public interface Func { public void apply(String key, Object value); } private static native boolean isObject(JavaScriptObject jso) /*-{ return typeof jso == "object"; }-*/; private static native boolean isArray(JavaScriptObject jso) /*-{ return Array.isArray(jso); }-*/; private static native boolean isPrimitive(JavaScriptObject jso) /*-{ if (typeof jso == "number" || typeof jso == "boolean" || typeof jso == "string") { return true; } return false; }-*/; private static native void iterateObject(JavaScriptObject jso, Func f) /*-{ for (var property in jso) { if (jso.hasOwnProperty(property)) { f.@org.swellrt.beta.model.js.SMapJs.Func::apply(Ljava/lang/String;Ljava/lang/Object;)(property, jso[property]); } } }-*/; private static native void iterateArray(JavaScriptObject jso, Func f) /*-{ for (var i = 0; i < jso.lenght; i++) { f.@org.swellrt.beta.model.js.SMapJs.Func::apply(Ljava/lang/String;Ljava/lang/Object;)(i+"", jso[i]); } }-*/; private static SNode castToSNode(JavaScriptObject object) throws IllegalCastException { if (object == null) throw new IllegalCastException("Error casting a null javascript object"); if (isPrimitive(object)) { // We assume primite JS values are transparently converted to Java primitives return SUtils.castToSNode(object); } else if (isObject(object)) { return new SMapJs(object); } else if (isArray(object)) { // TBC throw new IllegalCastException("Error casting from javascript to SNode"); } throw new IllegalCastException("Error casting from javascript to SNode"); } private static Object castToValue(JavaScriptObject object) throws IllegalCastException { if (object == null) throw new IllegalCastException("Error casting a null javascript object"); if (isPrimitive(object)) { return object; } else if (isObject(object)) { return new SMapJs(object); } else if (isArray(object)) { // TBC throw new IllegalCastException("Error casting from javascript to value"); } throw new IllegalCastException("Error casting from javascript to value"); } public static SMapJs create(JavaScriptObject jso) throws IllegalCastException { if (jso == null || !isObject(jso)) { throw new IllegalCastException("JavaScript type is not an object or it is null"); } return new SMapJs(jso); } private final JavaScriptObject jso; private final JsoView jsv; protected SMapJs(JavaScriptObject jso) { this.jso = jso; this.jsv = JsoView.as(jso); } @Override public Object get(String key) { if (key == null) return null; try { return castToValue(jsv.getJso(key)); } catch (IllegalCastException e) { return null; } } @Override public SNode getNode(String key) { if (key == null) return null; try { return castToSNode(jsv.getJso(key)); } catch (IllegalCastException e) { return null; } } @Override public SMap put(String key, SNode value) { throw new IllegalStateException("Wrapped Javascript SNode's can't be mutated"); } @Override public SMap put(String key, Object object) throws IllegalCastException { throw new IllegalStateException("Wrapped Javascript SNode's can't be mutated"); } @Override public void remove(String key) { throw new IllegalStateException("Wrapped Javascript SNode's can't be mutated"); } @Override public boolean has(String key) { return jsv.containsKey(key); } @Override public String[] keys() { ArrayList<String> keyList = new ArrayList<String>(); iterateObject(this.jso, new Func() { @Override public void apply(String key, Object value) { keyList.add(key); } }); return keyList.toArray(new String[keyList.size()]); } @Override public void clear() { throw new IllegalStateException("Wrapped Javascript SNode's can't be mutated"); } @Override public boolean isEmpty() { return keys().length == 0; } @Override public int size() { return keys().length; } @Override public Object asNative() { return jso; } }