Roll back AutoBeans change due to serialization problems with Safari4 on XP. Patch by: bobv Review by: rchandia (TBR) git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9958 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java index 5eb9763..11f6998 100644 --- a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java +++ b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
@@ -535,7 +535,7 @@ */ JClassType implementingType = typeOracle.getSingleJsoImpl(intfMethod.getEnclosingType()); - if (implementingType == null || implementingType.isAnnotationPresent(GwtScriptOnly.class)) { + if (implementingType == null) { /* * This means that there is no concrete implementation of the * interface by a JSO. Any implementation that might be created by a
diff --git a/tools/api-checker/config/gwt22_23userApi.conf b/tools/api-checker/config/gwt22_23userApi.conf index 7041d0b..88cb8f5 100644 --- a/tools/api-checker/config/gwt22_23userApi.conf +++ b/tools/api-checker/config/gwt22_23userApi.conf
@@ -16,9 +16,7 @@ :**/tools/**\ :com/google/gwt/regexp/shared/**\ :com/google/gwt/autobean/**/impl/**\ -:com/google/gwt/autobean/shared/ValueCodex.java\ :com/google/gwt/autobean/shared/ValueCodexHelper.java\ -:com/google/gwt/autobean/shared/AutoBeanCodex.java\ :com/google/gwt/core/client/impl/WeakMapping.java\ :com/google/gwt/core/ext/**\ :com/google/gwt/dev/*.java\
diff --git a/user/src/com/google/gwt/autobean/client/impl/AbstractAutoBeanFactory.java b/user/src/com/google/gwt/autobean/client/impl/AbstractAutoBeanFactory.java index f8f9e3e..0310304 100644 --- a/user/src/com/google/gwt/autobean/client/impl/AbstractAutoBeanFactory.java +++ b/user/src/com/google/gwt/autobean/client/impl/AbstractAutoBeanFactory.java
@@ -46,7 +46,7 @@ /** * EnumMap support. */ - public <E extends Enum<?>> E getEnum(Class<E> clazz, String token) { + public <E extends Enum<E>> E getEnum(Class<E> clazz, String token) { maybeInitializeEnumMap(); List<Enum<?>> list = stringsToEnumsMap.get(token); if (list == null) {
diff --git a/user/src/com/google/gwt/autobean/client/impl/ClientPropertyContext.java b/user/src/com/google/gwt/autobean/client/impl/ClientPropertyContext.java index 620c9ec..73a077a 100644 --- a/user/src/com/google/gwt/autobean/client/impl/ClientPropertyContext.java +++ b/user/src/com/google/gwt/autobean/client/impl/ClientPropertyContext.java
@@ -18,8 +18,6 @@ import com.google.gwt.autobean.shared.AutoBeanVisitor.CollectionPropertyContext; import com.google.gwt.autobean.shared.AutoBeanVisitor.MapPropertyContext; import com.google.gwt.autobean.shared.AutoBeanVisitor.ParameterizationVisitor; -import com.google.gwt.autobean.shared.AutoBeanVisitor.PropertyContext; -import com.google.gwt.autobean.shared.impl.AbstractAutoBean; import com.google.gwt.core.client.JavaScriptObject; import java.util.List; @@ -29,20 +27,18 @@ /** * Provides base methods for generated implementations of PropertyContext. */ -public final class ClientPropertyContext implements PropertyContext, CollectionPropertyContext, - MapPropertyContext { +public final class ClientPropertyContext implements CollectionPropertyContext, MapPropertyContext { /** * A reference to an instance setter method. */ public static final class Setter extends JavaScriptObject { /** - * Create a trivial Setter that calls {@link AbstractAutoBean#setProperty()} - * . + * Create a trivial Setter that calls {@link Map#put(Object, Object)}. */ - public static native Setter beanSetter(AbstractAutoBean<?> bean, String key) /*-{ + public static native Setter mapSetter(Map<String, Object> values, String key) /*-{ return function(value) { - bean.@com.google.gwt.autobean.shared.impl.AbstractAutoBean::setProperty(*)(key, value); + values.@java.util.Map::put(*)(key, value); }; }-*/;
diff --git a/user/src/com/google/gwt/autobean/client/impl/JsoSplittable.java b/user/src/com/google/gwt/autobean/client/impl/JsoSplittable.java index f6d3f3f..6a86993 100644 --- a/user/src/com/google/gwt/autobean/client/impl/JsoSplittable.java +++ b/user/src/com/google/gwt/autobean/client/impl/JsoSplittable.java
@@ -16,11 +16,8 @@ package com.google.gwt.autobean.client.impl; import com.google.gwt.autobean.shared.Splittable; -import com.google.gwt.autobean.shared.impl.HasSplittable; import com.google.gwt.autobean.shared.impl.StringQuoter; -import com.google.gwt.core.client.GwtScriptOnly; import com.google.gwt.core.client.JavaScriptObject; -import com.google.gwt.core.client.JsonUtils; import java.util.ArrayList; import java.util.Collections; @@ -28,106 +25,91 @@ /** * Implements the EntityCodex.Splittable interface using a raw JavaScriptObject. - * <p> - * A string value represented by a JsoSplittable can't use the string object - * directly, since {@code String.prototype} is overridden, so instead a - * temporary wrapper object is used to encapsulate the string data. */ -@GwtScriptOnly -public final class JsoSplittable extends JavaScriptObject implements Splittable, HasSplittable { - public static native JsoSplittable create() /*-{ - return {}; - }-*/; +public final class JsoSplittable extends JavaScriptObject implements Splittable { + /** + * This type is used because we can't treat Strings as JSOs. + */ + public static class StringSplittable implements Splittable { + private final String value; - public static Splittable create(boolean value) { - return create0(value); + public StringSplittable(String value) { + this.value = value; + } + + public String asString() { + return value; + } + + public Splittable get(int index) { + throw new UnsupportedOperationException(); + } + + public Splittable get(String key) { + throw new UnsupportedOperationException(); + } + + public String getPayload() { + return StringQuoter.quote(value); + } + + public List<String> getPropertyKeys() { + return Collections.emptyList(); + } + + public boolean isIndexed() { + return false; + } + + public boolean isKeyed() { + return false; + } + + public boolean isNull(int index) { + throw new UnsupportedOperationException(); + } + + public boolean isNull(String key) { + throw new UnsupportedOperationException(); + } + + public boolean isString() { + return true; + } + + public int size() { + return 0; + } } - public static Splittable create(double value) { - return create0(value); + public static Splittable create(Object object) { + if (object instanceof String) { + return new StringSplittable((String) object); + } + return create0(object); } - public static Splittable create(String value) { - return create0(value); - } - - public static native JsoSplittable createIndexed() /*-{ - return []; - }-*/; - - public static native JsoSplittable nullValue() /*-{ - return null; - }-*/; - - private static native Splittable create0(boolean object) /*-{ + private static native Splittable create0(Object object) /*-{ return object; }-*/; - private static native Splittable create0(double object) /*-{ - return object; - }-*/; - - private static native Splittable create0(String object) /*-{ - return { - __s : object - }; - }-*/; - - private static native boolean stringifyFastSupported() /*-{ - return $wnd.JSON && $wnd.JSON.stringify; - }-*/; - protected JsoSplittable() { } - public native boolean asBoolean() /*-{ - return this == true; - }-*/;; - - public native double asNumber() /*-{ - return Number(this); - }-*/; - - public void assign(Splittable parent, int index) { - if (isString()) { - assign0(parent, index, asString()); - } else { - assign0(parent, index, this); - } - } - - public void assign(Splittable parent, String index) { - if (isString()) { - assign0(parent, index, asString()); - } else { - assign0(parent, index, this); - } - } - public native String asString() /*-{ - return this.__s; + return String(this); }-*/; - public Splittable deepCopy() { - return StringQuoter.split(getPayload()); + public Splittable get(int index) { + return create(get0(index)); } - public JsoSplittable get(int index) { - return getRaw(index); - } - - public JsoSplittable get(String key) { - return getRaw(key); + public Splittable get(String key) { + return create(get0(key)); } public String getPayload() { - if (isString()) { - return JsonUtils.escapeValue(asString()); - } - if (stringifyFastSupported()) { - return stringifyFast(); - } - return stringifySlow(); + throw new UnsupportedOperationException("Cannot convert JsoSplittable to payload"); } public List<String> getPropertyKeys() { @@ -136,28 +118,12 @@ return Collections.unmodifiableList(toReturn); } - public native Object getReified(String key) /*-{ - return this.__reified && this.__reified[':' + key]; - }-*/; - - public Splittable getSplittable() { - return this; - } - - public native boolean isBoolean() /*-{ - return typeof (this) == 'boolean' || this instanceof Boolean; - }-*/; - - public native boolean isFunction() /*-{ - return typeof this == 'function'; - }-*/; - public native boolean isIndexed() /*-{ return this instanceof Array; }-*/; public boolean isKeyed() { - return this != NULL && !isString() && !isIndexed() && !isFunction(); + return !isString() && !isIndexed(); } public native boolean isNull(int index) /*-{ @@ -168,50 +134,20 @@ return this[key] == null; }-*/; - public native boolean isNumber() /*-{ - return typeof (this) == 'number' || this instanceof Number; - }-*/; - - public native boolean isReified(String key) /*-{ - return !!(this.__reified && this.__reified.hasOwnProperty(':' + key)); - }-*/; - public native boolean isString() /*-{ - return this && this.__s != null; - }-*/; - - public native boolean isUndefined(String key) /*-{ - return this[key] === undefined; - }-*/; - - public native void setReified(String key, Object object) /*-{ - // Use a function object so native JSON.stringify will ignore - (this.__reified || (this.__reified = function() { - }))[':' + key] = object; - }-*/; - - public native void setSize(int size) /*-{ - this.length = size; + return typeof (this) == 'string' || this instanceof String; }-*/; public native int size() /*-{ return this.length; }-*/; - private native void assign0(Splittable parent, int index, Splittable value) /*-{ - parent[index] = value; + private native Object get0(int index) /*-{ + return Object(this[index]); }-*/; - private native void assign0(Splittable parent, int index, String value) /*-{ - parent[index] = value; - }-*/; - - private native void assign0(Splittable parent, String index, Splittable value) /*-{ - parent[index] = value; - }-*/; - - private native void assign0(Splittable parent, String index, String value) /*-{ - parent[index] = value; + private native Object get0(String key) /*-{ + return Object(this[key]); }-*/; private native void getPropertyKeys0(List<String> list) /*-{ @@ -221,83 +157,4 @@ } } }-*/; - - private native JsoSplittable getRaw(int index) /*-{ - _ = this[index]; - if (_ == null) { - return null; - } - if (typeof _ == 'string' || _ instanceof String) { - return @com.google.gwt.autobean.client.impl.JsoSplittable::create(Ljava/lang/String;)(_); - } - return Object(_); - }-*/; - - private native JsoSplittable getRaw(String index) /*-{ - _ = this[index]; - if (_ == null) { - return null; - } - if (typeof _ == 'string' || _ instanceof String) { - return @com.google.gwt.autobean.client.impl.JsoSplittable::create(Ljava/lang/String;)(_); - } - return Object(_); - }-*/; - - private native String stringifyFast() /*-{ - return $wnd.JSON.stringify(this); - }-*/; - - private String stringifySlow() { - StringBuilder sb = new StringBuilder(); - stringifySlow(sb); - return sb.toString(); - } - - private void stringifySlow(StringBuilder sb) { - if (this == NULL) { - sb.append("null"); - return; - } - if (isBoolean()) { - sb.append(asBoolean()); - return; - } - if (isNumber()) { - sb.append(asNumber()); - return; - } - if (isString()) { - sb.append(JsonUtils.escapeValue(asString())); - return; - } - if (isIndexed()) { - sb.append("["); - for (int i = 0, j = size(); i < j; i++) { - if (i > 0) { - sb.append(","); - } - get(i).stringifySlow(sb); - } - sb.append("]"); - return; - } - - sb.append("{"); - boolean needsComma = false; - for (String key : getPropertyKeys()) { - if (needsComma) { - sb.append(","); - } else { - needsComma = true; - } - JsoSplittable value = get(key); - if (!value.isFunction()) { - sb.append(JsonUtils.escapeValue(key)); - sb.append(":"); - value.stringifySlow(sb); - } - } - sb.append("}"); - } }
diff --git a/user/src/com/google/gwt/autobean/rebind/AutoBeanFactoryGenerator.java b/user/src/com/google/gwt/autobean/rebind/AutoBeanFactoryGenerator.java index 1ec4f78..a07758d 100644 --- a/user/src/com/google/gwt/autobean/rebind/AutoBeanFactoryGenerator.java +++ b/user/src/com/google/gwt/autobean/rebind/AutoBeanFactoryGenerator.java
@@ -27,7 +27,6 @@ import com.google.gwt.autobean.shared.AutoBeanFactory; import com.google.gwt.autobean.shared.AutoBeanUtils; import com.google.gwt.autobean.shared.AutoBeanVisitor; -import com.google.gwt.autobean.shared.Splittable; import com.google.gwt.autobean.shared.impl.AbstractAutoBean; import com.google.gwt.autobean.shared.impl.AbstractAutoBean.OneShotContext; import com.google.gwt.core.client.JavaScriptObject; @@ -186,16 +185,28 @@ AutoBeanFactory.class.getCanonicalName()); } + // Clone constructor + // public FooIntfAutoBean(FooIntfoAutoBean toClone, boolean deepClone) { + sw.println("public %1$s(%1$s toClone, boolean deep) {", type.getSimpleSourceName()); + sw.indentln("super(toClone, deep);"); + sw.println("}"); + // Wrapping constructor // public FooIntfAutoBean(AutoBeanFactory factory, FooIntfo wrapped) { sw.println("public %s(%s factory, %s wrapped) {", type.getSimpleSourceName(), AutoBeanFactory.class.getCanonicalName(), type.getPeerType().getQualifiedSourceName()); - sw.indentln("super(wrapped, factory);"); + sw.indentln("super(factory, wrapped);"); sw.println("}"); // public FooIntf as() {return shim;} sw.println("public %s as() {return shim;}", type.getPeerType().getQualifiedSourceName()); + // public FooIntfAutoBean clone(boolean deep) { + sw.println("public %s clone(boolean deep) {", type.getQualifiedSourceName()); + // return new FooIntfAutoBean(this, deep); + sw.indentln("return new %s(this, deep);", type.getSimpleSourceName()); + sw.println("}"); + // public Class<Intf> getType() {return Intf.class;} sw.println("public Class<%1$s> getType() {return %1$s.class;}", ModelUtils.ensureBaseType( type.getPeerType()).getQualifiedSourceName()); @@ -219,49 +230,45 @@ // return new FooIntf() { sw.println("return new %s() {", type.getPeerType().getQualifiedSourceName()); sw.indent(); - sw.println("private final %s data = %s.this.data;", Splittable.class.getCanonicalName(), type - .getQualifiedSourceName()); for (AutoBeanMethod method : type.getMethods()) { JMethod jmethod = method.getMethod(); - JType returnType = jmethod.getReturnType(); sw.println("public %s {", getBaseMethodDeclaration(jmethod)); sw.indent(); switch (method.getAction()) { case GET: { - String castType; - if (returnType.isPrimitive() != null) { - castType = returnType.isPrimitive().getQualifiedBoxedSourceName(); - // Boolean toReturn = getOrReify("foo"); - sw.println("%s toReturn = getOrReify(\"%s\");", castType, method.getPropertyName()); - // return toReturn == null ? false : toReturn; - sw.println("return toReturn == null ? %s : toReturn;", returnType.isPrimitive() - .getUninitializedFieldExpression()); - } else if (returnType.equals(context.getTypeOracle().findType( - Splittable.class.getCanonicalName()))) { - sw.println("return data.isNull(\"%1$s\") ? null : data.get(\"%1$s\");", method - .getPropertyName()); + // Must handle de-boxing primitive types + JPrimitiveType primitive = jmethod.getReturnType().isPrimitive(); + if (primitive != null) { + // Object toReturn = values.get("foo"); + sw.println("Object toReturn = values.get(\"%s\");", method.getPropertyName()); + sw.println("if (toReturn == null) {"); + // return 0; + sw.indentln("return %s;", primitive.getUninitializedFieldExpression()); + sw.println("} else {"); + // return (BoxedType) toReturn; + sw.indentln("return (%s) toReturn;", primitive.getQualifiedBoxedSourceName()); + sw.println("}"); } else { - // return (ReturnType) values.getOrReify(\"foo\"); - castType = ModelUtils.getQualifiedBaseSourceName(returnType); - sw.println("return (%s) getOrReify(\"%s\");", castType, method.getPropertyName()); + // return (ReturnType) values.get(\"foo\"); + sw.println("return (%s) values.get(\"%s\");", ModelUtils + .getQualifiedBaseSourceName(jmethod.getReturnType()), method.getPropertyName()); } } break; case SET: - case SET_BUILDER: { - JParameter param = jmethod.getParameters()[0]; - // setProperty("foo", parameter); - sw.println("setProperty(\"%s\", %s);", method.getPropertyName(), param.getName()); + case SET_BUILDER: + // values.put("foo", parameter); + sw.println("values.put(\"%s\", %s);", method.getPropertyName(), + jmethod.getParameters()[0].getName()); if (JBeanMethod.SET_BUILDER.equals(method.getAction())) { sw.println("return this;"); } break; - } case CALL: // return com.example.Owner.staticMethod(Outer.this, param, // param); JMethod staticImpl = method.getStaticImpl(); - if (!returnType.equals(JPrimitiveType.VOID)) { + if (!jmethod.getReturnType().equals(JPrimitiveType.VOID)) { sw.print("return "); } sw.print("%s.%s(%s.this", staticImpl.getEnclosingType().getQualifiedSourceName(), @@ -499,15 +506,19 @@ sw.println("public %s {", getBaseMethodDeclaration(jmethod)); sw.indent(); + // Use explicit enclosing this reference to avoid method conflicts + sw.println("%s.this.checkWrapped();", type.getSimpleSourceName()); + switch (method.getAction()) { case GET: /* * The getter call will ensure that any non-value return type is * definitely wrapped by an AutoBean instance. */ - sw.println("%s toReturn = %s.this.getWrapped().%s();", ModelUtils - .getQualifiedBaseSourceName(jmethod.getReturnType()), type.getSimpleSourceName(), - methodName); + // Foo toReturn=FooAutoBean.this.get("getFoo", getWrapped().getFoo()); + sw.println("%s toReturn = %3$s.this.get(\"%2$s\", getWrapped().%2$s());", ModelUtils + .getQualifiedBaseSourceName(jmethod.getReturnType()), methodName, type + .getSimpleSourceName()); // Non-value types might need to be wrapped writeReturnWrapper(sw, type, method); @@ -515,6 +526,7 @@ break; case SET: case SET_BUILDER: + sw.println("%s.this.checkFrozen();", type.getSimpleSourceName()); // getWrapped().setFoo(foo); sw.println("%s.this.getWrapped().%s(%s);", type.getSimpleSourceName(), methodName, parameters[0].getName()); @@ -584,6 +596,7 @@ sw.println("%s propertyContext;", ClientPropertyContext.class.getCanonicalName()); // Local variable ref cleans up emitted js sw.println("%1$s as = as();", type.getPeerType().getQualifiedSourceName()); + sw.println("%s<String, Object> values = this.values;", Map.class.getCanonicalName()); for (AutoBeanMethod method : type.getMethods()) { if (!method.getAction().equals(JBeanMethod.GET)) { @@ -649,9 +662,9 @@ referencedSetters.add(setter); } else { // Create a function that will update the values map - // CPContext.beanSetter(FooBeanImpl.this, "foo"); - sw.println("%s.beanSetter(%s.this, \"%s\"),", ClientPropertyContext.Setter.class - .getCanonicalName(), type.getSimpleSourceName(), method.getPropertyName()); + // CPContext.mapSetter(values, "foo"); + sw.println("%s.mapSetter(values, \"%s\"),", ClientPropertyContext.Setter.class + .getCanonicalName(), method.getPropertyName()); } } if (typeList.size() == 1) {
diff --git a/user/src/com/google/gwt/autobean/server/impl/BeanMethod.java b/user/src/com/google/gwt/autobean/server/impl/BeanMethod.java index 32b4a1b..132393f 100644 --- a/user/src/com/google/gwt/autobean/server/impl/BeanMethod.java +++ b/user/src/com/google/gwt/autobean/server/impl/BeanMethod.java
@@ -16,7 +16,6 @@ package com.google.gwt.autobean.server.impl; import com.google.gwt.autobean.shared.AutoBean; -import com.google.gwt.autobean.shared.AutoBean.PropertyName; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -39,7 +38,8 @@ } @Override - Object invoke(SimpleBeanHandler<?> handler, Method method, Object[] args) throws Throwable { + Object invoke(SimpleBeanHandler<?> handler, Method method, Object[] args) + throws Throwable { if (CALL.matches(handler, method)) { return CALL.invoke(handler, method, args); } @@ -58,7 +58,7 @@ @Override public String inferName(Method method) { String name = method.getName(); - if (name.startsWith(IS_PREFIX) && !method.isAnnotationPresent(PropertyName.class)) { + if (name.startsWith(IS_PREFIX)) { Class<?> returnType = method.getReturnType(); if (Boolean.TYPE.equals(returnType) || Boolean.class.equals(returnType)) { return decapitalize(name.substring(2)); @@ -70,7 +70,7 @@ @Override Object invoke(SimpleBeanHandler<?> handler, Method method, Object[] args) { String propertyName = inferName(method); - Object toReturn = handler.getBean().getOrReify(propertyName); + Object toReturn = handler.getBean().getValues().get(propertyName); if (toReturn == null && method.getReturnType().isPrimitive()) { toReturn = TypeUtils.getDefaultPrimitiveValue(method.getReturnType()); } @@ -80,14 +80,15 @@ @Override boolean matches(SimpleBeanHandler<?> handler, Method method) { Class<?> returnType = method.getReturnType(); - if (method.getParameterTypes().length != 0 || Void.TYPE.equals(returnType)) { + if (method.getParameterTypes().length != 0 + || Void.TYPE.equals(returnType)) { return false; } String name = method.getName(); if (Boolean.TYPE.equals(returnType) || Boolean.class.equals(returnType)) { - if (name.startsWith(IS_PREFIX) && name.length() > 2 || name.startsWith(HAS_PREFIX) - && name.length() > 3) { + if (name.startsWith(IS_PREFIX) && name.length() > 2 + || name.startsWith(HAS_PREFIX) && name.length() > 3) { return true; } } @@ -100,7 +101,7 @@ SET { @Override Object invoke(SimpleBeanHandler<?> handler, Method method, Object[] args) { - handler.getBean().setProperty(inferName(method), args[0]); + handler.getBean().getValues().put(inferName(method), args[0]); return null; } @@ -108,7 +109,8 @@ boolean matches(SimpleBeanHandler<?> handler, Method method) { String name = method.getName(); return name.startsWith(SET_PREFIX) && name.length() > 3 - && method.getParameterTypes().length == 1 && method.getReturnType().equals(Void.TYPE); + && method.getParameterTypes().length == 1 + && method.getReturnType().equals(Void.TYPE); } }, /** @@ -119,15 +121,15 @@ SET_BUILDER { @Override Object invoke(SimpleBeanHandler<?> handler, Method method, Object[] args) { - ProxyAutoBean<?> bean = handler.getBean(); - bean.setProperty(inferName(method), args[0]); - return bean.as(); + handler.getBean().getValues().put(inferName(method), args[0]); + return handler.getBean().as(); } @Override boolean matches(SimpleBeanHandler<?> handler, Method method) { String name = method.getName(); - return name.startsWith(SET_PREFIX) && name.length() > 3 + return name.startsWith(SET_PREFIX) + && name.length() > 3 && method.getParameterTypes().length == 1 && method.getReturnType().isAssignableFrom(method.getDeclaringClass()); } @@ -142,7 +144,8 @@ } @Override - Object invoke(SimpleBeanHandler<?> handler, Method method, Object[] args) throws Throwable { + Object invoke(SimpleBeanHandler<?> handler, Method method, Object[] args) + throws Throwable { if (args == null) { args = EMPTY_OBJECT; } @@ -187,9 +190,8 @@ continue; } // Check the AutoBean parameterization of the 0th argument - Class<?> foundAutoBean = - TypeUtils.ensureBaseType(TypeUtils.getSingleParameterization(AutoBean.class, found - .getGenericParameterTypes()[0])); + Class<?> foundAutoBean = TypeUtils.ensureBaseType(TypeUtils.getSingleParameterization( + AutoBean.class, found.getGenericParameterTypes()[0])); if (!foundAutoBean.isAssignableFrom(autoBeanType)) { continue; } @@ -203,8 +205,8 @@ } /** - * Private equivalent of Introspector.decapitalize(String) since - * java.beans.Introspector is not available in Android 2.2. + * Private equivalent of Introspector.decapitalize(String) + * since java.beans.Introspector is not available in Android 2.2. */ private static String decapitalize(String name) { if (name == null) { @@ -221,10 +223,6 @@ } public String inferName(Method method) { - PropertyName prop = method.getAnnotation(PropertyName.class); - if (prop != null) { - return prop.value(); - } return decapitalize(method.getName().substring(3)); } @@ -238,11 +236,11 @@ /** * Invoke the method. */ - abstract Object invoke(SimpleBeanHandler<?> handler, Method method, Object[] args) - throws Throwable; + abstract Object invoke(SimpleBeanHandler<?> handler, Method method, + Object[] args) throws Throwable; /** * Determine if the method maches the given type. */ abstract boolean matches(SimpleBeanHandler<?> handler, Method method); -} \ No newline at end of file +}
diff --git a/user/src/com/google/gwt/autobean/server/impl/BeanPropertyContext.java b/user/src/com/google/gwt/autobean/server/impl/BeanPropertyContext.java index c1f4be9..f17e879 100644 --- a/user/src/com/google/gwt/autobean/server/impl/BeanPropertyContext.java +++ b/user/src/com/google/gwt/autobean/server/impl/BeanPropertyContext.java
@@ -16,19 +16,20 @@ package com.google.gwt.autobean.server.impl; import java.lang.reflect.Method; +import java.util.Map; /** * A property context that allows setters to be called on a simple peer, * regardless of whether or not the interface actually has a setter. */ class BeanPropertyContext extends MethodPropertyContext { - private final ProxyAutoBean<?> bean; private final String propertyName; + private final Map<String, Object> map; public BeanPropertyContext(ProxyAutoBean<?> bean, Method getter) { super(getter); - this.bean = bean; propertyName = BeanMethod.GET.inferName(getter); + map = bean.getPropertyMap(); } @Override @@ -41,6 +42,6 @@ Class<?> maybeAutobox = TypeUtils.maybeAutobox(getType()); assert value == null || maybeAutobox.isInstance(value) : value.getClass().getCanonicalName() + " is not assignable to " + maybeAutobox.getCanonicalName(); - bean.setProperty(propertyName, maybeAutobox.cast(value)); + map.put(propertyName, maybeAutobox.cast(value)); } }
diff --git a/user/src/com/google/gwt/autobean/server/impl/JsonSplittable.java b/user/src/com/google/gwt/autobean/server/impl/JsonSplittable.java index 04b77a6..51e7902 100644 --- a/user/src/com/google/gwt/autobean/server/impl/JsonSplittable.java +++ b/user/src/com/google/gwt/autobean/server/impl/JsonSplittable.java
@@ -16,39 +16,21 @@ package com.google.gwt.autobean.server.impl; import com.google.gwt.autobean.shared.Splittable; -import com.google.gwt.autobean.shared.impl.HasSplittable; import com.google.gwt.autobean.shared.impl.StringQuoter; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.Iterator; import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; /** * Uses the org.json packages to slice and dice request payloads. */ -public class JsonSplittable implements Splittable, HasSplittable { - - /** - * Ensures that the same JsonSplittable will be returned for a given backing - * JSONObject. - */ - private static final Map<Object, Reference<JsonSplittable>> canonical = - new WeakHashMap<Object, Reference<JsonSplittable>>(); - - public static JsonSplittable create() { - return new JsonSplittable(new JSONObject()); - } - +public class JsonSplittable implements Splittable { public static Splittable create(String payload) { try { switch (payload.charAt(0)) { @@ -57,8 +39,8 @@ case '[': return new JsonSplittable(new JSONArray(payload)); case '"': - return new JsonSplittable(new JSONArray("[" + payload + "]").getString(0)); - case '-': + return new JsonSplittable( + new JSONArray("[" + payload + "]").getString(0)); case '0': case '1': case '2': @@ -69,31 +51,19 @@ case '7': case '8': case '9': - return new JsonSplittable(Double.parseDouble(payload)); - case 't': - case 'f': - return new JsonSplittable(Boolean.parseBoolean(payload)); - case 'n': - return null; + return new JsonSplittable(payload); default: - throw new RuntimeException("Could not parse payload: payload[0] = " + payload.charAt(0)); + throw new RuntimeException("Could not parse payload: payload[0] = " + + payload.charAt(0)); } } catch (JSONException e) { throw new RuntimeException("Could not parse payload", e); } } - public static Splittable createIndexed() { - return new JsonSplittable(new JSONArray()); - } - - public static Splittable createNull() { - return new JsonSplittable(); - } - /** - * Private equivalent of org.json.JSONObject.getNames(JSONObject) since that - * method is not available in Android 2.2. Used to represent a null value. + * Private equivalent of org.json.JSONObject.getNames(JSONObject) + * since that method is not available in Android 2.2. */ private static String[] getNames(JSONObject json) { int length = json.length(); @@ -109,38 +79,20 @@ return names; } - private JSONArray array; - private Boolean bool; - /** - * Used to represent a null value. - */ - private boolean isNull; - private Double number; - private JSONObject obj; - private String string; - private final Map<String, Object> reified = new HashMap<String, Object>(); - - /** - * Constructor for a null value. - */ - private JsonSplittable() { - isNull = true; - } - - private JsonSplittable(boolean value) { - this.bool = value; - } - - private JsonSplittable(double value) { - this.number = value; - } + private final JSONArray array; + private final JSONObject obj; + private final String string; private JsonSplittable(JSONArray array) { this.array = array; + this.obj = null; + this.string = null; } private JsonSplittable(JSONObject obj) { + this.array = null; this.obj = obj; + this.string = null; } private JsonSplittable(String string) { @@ -149,38 +101,10 @@ this.string = string; } - public boolean asBoolean() { - return bool; - } - - public double asNumber() { - return number; - } - - public void assign(Splittable parent, int index) { - try { - ((JsonSplittable) parent).array.put(index, value()); - } catch (JSONException e) { - throw new RuntimeException(e); - } - } - - public void assign(Splittable parent, String propertyName) { - try { - ((JsonSplittable) parent).obj.put(propertyName, value()); - } catch (JSONException e) { - throw new RuntimeException(e); - } - } - public String asString() { return string; } - public Splittable deepCopy() { - return create(getPayload()); - } - public Splittable get(int index) { try { return makeSplittable(array.get(index)); @@ -198,9 +122,6 @@ } public String getPayload() { - if (isNull) { - return "null"; - } if (obj != null) { return obj.toString(); } @@ -210,12 +131,6 @@ if (string != null) { return StringQuoter.quote(string); } - if (number != null) { - return String.valueOf(number); - } - if (bool != null) { - return String.valueOf(bool); - } throw new RuntimeException("No data in this JsonSplittable"); } @@ -228,18 +143,6 @@ } } - public Object getReified(String key) { - return reified.get(key); - } - - public Splittable getSplittable() { - return this; - } - - public boolean isBoolean() { - return bool != null; - } - public boolean isIndexed() { return array != null; } @@ -257,39 +160,10 @@ return !obj.has(key) || obj.isNull(key); } - public boolean isNumber() { - return number != null; - } - - public boolean isReified(String key) { - return reified.containsKey(key); - } - public boolean isString() { return string != null; } - public boolean isUndefined(String key) { - return !obj.has(key); - } - - public void setReified(String key, Object object) { - reified.put(key, object); - } - - public void setSize(int size) { - // This is terrible, but there's no API support for resizing or splicing - JSONArray newArray = new JSONArray(); - for (int i = 0; i < size; i++) { - try { - newArray.put(i, array.get(i)); - } catch (JSONException e) { - throw new RuntimeException(e); - } - } - array = newArray; - } - public int size() { return array.length(); } @@ -299,53 +173,23 @@ */ @Override public String toString() { - return getPayload(); - } - - private synchronized JsonSplittable makeSplittable(Object object) { - if (JSONObject.NULL.equals(object)) { - return null; - } - Reference<JsonSplittable> ref = canonical.get(object); - JsonSplittable seen = ref == null ? null : ref.get(); - if (seen == null) { - if (object instanceof JSONObject) { - seen = new JsonSplittable((JSONObject) object); - } else if (object instanceof JSONArray) { - seen = new JsonSplittable((JSONArray) object); - } else if (object instanceof String) { - seen = new JsonSplittable(object.toString()); - } else if (object instanceof Number) { - seen = new JsonSplittable(((Number) object).doubleValue()); - } else if (object instanceof Boolean) { - seen = new JsonSplittable((Boolean) object); - } else { - throw new RuntimeException("Unhandled type " + object.getClass()); - } - canonical.put(object, new WeakReference<JsonSplittable>(seen)); - } - return seen; - } - - private Object value() { - if (isNull) { - return null; - } if (obj != null) { - return obj; - } - if (array != null) { - return array; - } - if (string != null) { + return obj.toString(); + } else if (array != null) { + return array.toString(); + } else if (string != null) { return string; } - if (number != null) { - return number; - } - if (bool != null) { - return bool; - } - throw new RuntimeException("No data"); + return "<Uninitialized>"; } -} \ No newline at end of file + + private JsonSplittable makeSplittable(Object object) { + if (object instanceof JSONObject) { + return new JsonSplittable((JSONObject) object); + } + if (object instanceof JSONArray) { + return new JsonSplittable((JSONArray) object); + } + return new JsonSplittable(object.toString()); + } +}
diff --git a/user/src/com/google/gwt/autobean/server/impl/ProxyAutoBean.java b/user/src/com/google/gwt/autobean/server/impl/ProxyAutoBean.java index de65b5e..4f8e3be 100644 --- a/user/src/com/google/gwt/autobean/server/impl/ProxyAutoBean.java +++ b/user/src/com/google/gwt/autobean/server/impl/ProxyAutoBean.java
@@ -66,14 +66,15 @@ Class<?>... extraInterfaces) { Class<?>[] intfs; if (extraInterfaces == null) { - intfs = new Class<?>[]{intf}; + intfs = new Class<?>[] {intf}; } else { intfs = new Class<?>[extraInterfaces.length + 1]; intfs[0] = intf; System.arraycopy(extraInterfaces, 0, intfs, 1, extraInterfaces.length); } - return intf.cast(Proxy.newProxyInstance(intf.getClassLoader(), intfs, handler)); + return intf.cast(Proxy.newProxyInstance(intf.getClassLoader(), intfs, + handler)); } private static Data calculateData(Class<?> beanType) { @@ -116,11 +117,13 @@ private final Class<T> beanType; private final Configuration configuration; private final Data data; + private final T shim; // These constructors mirror the generated constructors. @SuppressWarnings("unchecked") - public ProxyAutoBean(AutoBeanFactory factory, Class<?> beanType, Configuration configuration) { + public ProxyAutoBean(AutoBeanFactory factory, Class<?> beanType, + Configuration configuration) { super(factory); this.beanType = (Class<T>) beanType; this.configuration = configuration; @@ -129,20 +132,36 @@ } @SuppressWarnings("unchecked") - public ProxyAutoBean(AutoBeanFactory factory, Class<?> beanType, Configuration configuration, - T toWrap) { - super(toWrap, factory); + public ProxyAutoBean(AutoBeanFactory factory, Class<?> beanType, + Configuration configuration, T toWrap) { + super(factory, toWrap); + if (Proxy.isProxyClass(toWrap.getClass())) { + System.out.println("blah"); + } this.beanType = (Class<T>) beanType; this.configuration = configuration; this.data = calculateData(beanType); this.shim = createShim(); } + private ProxyAutoBean(ProxyAutoBean<T> toClone, boolean deep) { + super(toClone, deep); + this.beanType = toClone.beanType; + this.configuration = toClone.configuration; + this.data = toClone.data; + this.shim = createShim(); + } + @Override public T as() { return shim; } + @Override + public AutoBean<T> clone(boolean deep) { + return new ProxyAutoBean<T>(this, deep); + } + public Configuration getConfiguration() { return configuration; } @@ -175,13 +194,9 @@ super.checkWrapped(); } - /** - * Not used in this implementation. Instead, the simple implementation is - * created lazily in {@link #getWrapped()}. - */ @Override protected T createSimplePeer() { - return null; + return ProxyAutoBean.makeProxy(beanType, new SimpleBeanHandler<T>(this)); } /** @@ -193,11 +208,10 @@ } /** - * Allow access by BeanMethod. + * Allow access by {@link BeanMethod}. */ - @Override - protected <V> V getOrReify(String propertyName) { - return super.<V> getOrReify(propertyName); + protected Map<String, Object> getValues() { + return values; } /** @@ -205,9 +219,6 @@ */ @Override protected T getWrapped() { - if (wrapped == null && isUsingSimplePeer()) { - wrapped = (T) ProxyAutoBean.makeProxy(beanType, new SimpleBeanHandler<T>(this)); - } return super.getWrapped(); } @@ -219,11 +230,6 @@ super.set(method, value); } - @Override - protected void setProperty(String propertyName, Object value) { - super.setProperty(propertyName, value); - } - // TODO: Port to model-based when class-based TypeOracle is available. @Override protected void traverseProperties(AutoBeanVisitor visitor, OneShotContext ctx) { @@ -251,9 +257,8 @@ } // Create the context used for the property visitation - MethodPropertyContext x = - isUsingSimplePeer() ? new BeanPropertyContext(this, getter) : new GetterPropertyContext( - this, getter); + MethodPropertyContext x = isUsingSimplePeer() ? new BeanPropertyContext( + this, getter) : new GetterPropertyContext(this, getter); switch (propertyType) { case VALUE: { @@ -308,8 +313,13 @@ return beanType; } + Map<String, Object> getPropertyMap() { + return values; + } + private T createShim() { - T toReturn = ProxyAutoBean.makeProxy(beanType, new ShimHandler<T>(this, getWrapped())); + T toReturn = ProxyAutoBean.makeProxy(beanType, new ShimHandler<T>(this, + getWrapped())); WeakMapping.set(toReturn, AutoBean.class.getName(), this); return toReturn; }
diff --git a/user/src/com/google/gwt/autobean/server/impl/ShimHandler.java b/user/src/com/google/gwt/autobean/server/impl/ShimHandler.java index dc920a7..23c190d 100644 --- a/user/src/com/google/gwt/autobean/server/impl/ShimHandler.java +++ b/user/src/com/google/gwt/autobean/server/impl/ShimHandler.java
@@ -19,7 +19,6 @@ import com.google.gwt.autobean.shared.AutoBeanUtils; import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; @@ -32,9 +31,11 @@ class ShimHandler<T> implements InvocationHandler { private final ProxyAutoBean<T> bean; private final Method interceptor; + private final T toWrap; public ShimHandler(ProxyAutoBean<T> bean, T toWrap) { this.bean = bean; + this.toWrap = toWrap; Method maybe = null; for (Class<?> clazz : bean.getConfiguration().getCategories()) { @@ -66,35 +67,36 @@ return bean.getWrapped().hashCode(); } - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { method.setAccessible(true); Object toReturn; String name = method.getName(); + bean.checkWrapped(); method.setAccessible(true); - try { - if (BeanMethod.OBJECT.matches(method)) { - return method.invoke(this, args); - } else if (BeanMethod.GET.matches(method)) { - toReturn = method.invoke(bean.getWrapped(), args); - toReturn = bean.get(name, toReturn); - } else if (BeanMethod.SET.matches(method) || BeanMethod.SET_BUILDER.matches(method)) { - toReturn = method.invoke(bean.getWrapped(), args); - bean.set(name, args[0]); - } else { - // XXX How should freezing and calls work together? - toReturn = method.invoke(bean.getWrapped(), args); - bean.call(name, toReturn, args); - } - Class<?> intf = method.getReturnType(); - if (!Object.class.equals(intf)) { - // XXX Need to deal with resolving generic T return types - toReturn = maybeWrap(intf, toReturn); - } - if (interceptor != null) { - toReturn = interceptor.invoke(null, bean, toReturn); - } - } catch (InvocationTargetException e) { - throw e.getCause(); + if (BeanMethod.OBJECT.matches(method)) { + return method.invoke(this, args); + } else if (BeanMethod.GET.matches(method)) { + toReturn = method.invoke(toWrap, args); + toReturn = bean.get(name, toReturn); + } else if (BeanMethod.SET.matches(method) + || BeanMethod.SET_BUILDER.matches(method)) { + bean.checkFrozen(); + toReturn = method.invoke(toWrap, args); + bean.set(name, args[0]); + } else { + // XXX How should freezing and calls work together? + // bean.checkFrozen(); + toReturn = method.invoke(toWrap, args); + bean.call(name, toReturn, args); + } + Class<?> intf = method.getReturnType(); + if (!Object.class.equals(intf)) { + // XXX Need to deal with resolving generic T return types + toReturn = maybeWrap(intf, toReturn); + } + if (interceptor != null) { + toReturn = interceptor.invoke(null, bean, toReturn); } return toReturn; } @@ -108,11 +110,9 @@ if (toReturn == null) { return null; } - AutoBean<?> returnBean = AutoBeanUtils.getAutoBean(toReturn); - if (returnBean != null) { - return returnBean.as(); - } - if (TypeUtils.isValueType(intf) || TypeUtils.isValueType(toReturn.getClass()) + if (TypeUtils.isValueType(intf) + || TypeUtils.isValueType(toReturn.getClass()) + || AutoBeanUtils.getAutoBean(toReturn) != null || bean.getConfiguration().getNoWrap().contains(intf)) { return toReturn; } @@ -124,8 +124,8 @@ */ return toReturn; } - ProxyAutoBean<Object> newBean = - new ProxyAutoBean<Object>(bean.getFactory(), intf, bean.getConfiguration(), toReturn); + ProxyAutoBean<Object> newBean = new ProxyAutoBean<Object>( + bean.getFactory(), intf, bean.getConfiguration(), toReturn); return newBean.as(); } } \ No newline at end of file
diff --git a/user/src/com/google/gwt/autobean/server/impl/SimpleBeanHandler.java b/user/src/com/google/gwt/autobean/server/impl/SimpleBeanHandler.java index 766d26b..d25468d 100644 --- a/user/src/com/google/gwt/autobean/server/impl/SimpleBeanHandler.java +++ b/user/src/com/google/gwt/autobean/server/impl/SimpleBeanHandler.java
@@ -37,7 +37,8 @@ /** * Delegates most work to {@link BeanMethod}. */ - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { for (BeanMethod type : BeanMethod.values()) { if (type.matches(this, method)) { Object toReturn = type.invoke(this, method, args); @@ -46,12 +47,12 @@ } throw new RuntimeException("Unhandled invocation " + method.getName()); } - + /** * For debugging use only. */ @Override public String toString() { - return bean.getSplittable().getPayload(); + return bean.getPropertyMap().toString(); } } \ No newline at end of file
diff --git a/user/src/com/google/gwt/autobean/server/impl/TypeUtils.java b/user/src/com/google/gwt/autobean/server/impl/TypeUtils.java index 2503696..cf769c1 100644 --- a/user/src/com/google/gwt/autobean/server/impl/TypeUtils.java +++ b/user/src/com/google/gwt/autobean/server/impl/TypeUtils.java
@@ -164,9 +164,8 @@ return false; } - public static <V> Class<V> maybeAutobox(Class<V> domainType) { - @SuppressWarnings("unchecked") - Class<V> autoBoxType = (Class<V>) AUTOBOX_MAP.get(domainType); + public static Class<?> maybeAutobox(Class<?> domainType) { + Class<?> autoBoxType = AUTOBOX_MAP.get(domainType); return autoBoxType == null ? domainType : autoBoxType; }
diff --git a/user/src/com/google/gwt/autobean/shared/AutoBean.java b/user/src/com/google/gwt/autobean/shared/AutoBean.java index 3af5128..8b8613f 100644 --- a/user/src/com/google/gwt/autobean/shared/AutoBean.java +++ b/user/src/com/google/gwt/autobean/shared/AutoBean.java
@@ -58,21 +58,16 @@ T as(); /** - * This method always throws an {@link UnsupportedOperationException}. The - * implementation of this method in previous releases was not sufficiently - * robust and there are no further uses of this method within the GWT code - * base. Furthermore, there are many different semantics that can be applied - * to a cloning process that cannot be adequately addressed with a single - * implementation. + * Creates a copy of the AutoBean. * <p> - * A simple clone of an acyclic datastructure can be created by using - * {@link AutoBeanCodex} to encode and decode the root object. Other cloning - * algorithms are best implemented by using an {@link AutoBeanVisitor}. + * If the AutoBean has tags, the tags will be copied into the cloned AutoBean. + * If any of the tag values are AutoBeans, they will not be cloned, regardless + * of the value of <code>deep</code>. * - * @throws UnsupportedOperationException - * @deprecated with no replacement + * @param deep indicates if all referenced AutoBeans should be cloned + * @return a copy of this {@link AutoBean} + * @throws IllegalStateException if the AutoBean is a wrapper type */ - @Deprecated AutoBean<T> clone(boolean deep); /**
diff --git a/user/src/com/google/gwt/autobean/shared/AutoBeanCodex.java b/user/src/com/google/gwt/autobean/shared/AutoBeanCodex.java index 9797984..b7e4d52 100644 --- a/user/src/com/google/gwt/autobean/shared/AutoBeanCodex.java +++ b/user/src/com/google/gwt/autobean/shared/AutoBeanCodex.java
@@ -15,10 +15,21 @@ */ package com.google.gwt.autobean.shared; -import com.google.gwt.autobean.shared.impl.AutoBeanCodexImpl; -import com.google.gwt.autobean.shared.impl.AutoBeanCodexImpl.EncodeState; +import com.google.gwt.autobean.shared.AutoBeanVisitor.ParameterizationVisitor; +import com.google.gwt.autobean.shared.impl.EnumMap; +import com.google.gwt.autobean.shared.impl.LazySplittable; import com.google.gwt.autobean.shared.impl.StringQuoter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + /** * Utility methods for encoding an AutoBean graph into a JSON-compatible string. * This codex intentionally does not preserve object identity, nor does it @@ -27,16 +38,363 @@ public class AutoBeanCodex { /** - * Decode an AutoBeanCodex payload. - * - * @param <T> the expected return type - * @param factory an AutoBeanFactory capable of producing {@code AutoBean<T>} - * @param clazz the expected return type - * @param data a payload previously generated by {@link #encode(AutoBean)} - * @return an AutoBean containing the payload contents + * Describes a means of encoding or decoding a particular type of data to or + * from a wire format representation. */ + interface Coder { + Object decode(Splittable data); + + void encode(StringBuilder sb, Object value); + } + + /** + * Creates a Coder that is capable of operating on a particular + * parameterization of a datastructure (e.g. {@code Map<String, List<String>>} + * ). + */ + class CoderCreator extends ParameterizationVisitor { + private Stack<Coder> stack = new Stack<Coder>(); + + @Override + public void endVisitType(Class<?> type) { + if (List.class.equals(type) || Set.class.equals(type)) { + stack.push(new CollectionCoder(type, stack.pop())); + } else if (Map.class.equals(type)) { + // Note that the parameters are passed in reverse order + stack.push(new MapCoder(stack.pop(), stack.pop())); + } else if (Splittable.class.equals(type)) { + stack.push(new SplittableDecoder()); + } else if (type.getEnumConstants() != null) { + @SuppressWarnings(value = {"rawtypes", "unchecked"}) + EnumCoder decoder = new EnumCoder(type); + stack.push(decoder); + } else if (ValueCodex.canDecode(type)) { + stack.push(new ValueCoder(type)); + } else { + stack.push(new ObjectCoder(type)); + } + } + + public Coder getCoder() { + assert stack.size() == 1 : "Incorrect size: " + stack.size(); + return stack.pop(); + } + } + + class CollectionCoder implements Coder { + private final Coder elementDecoder; + private final Class<?> type; + + public CollectionCoder(Class<?> type, Coder elementDecoder) { + this.elementDecoder = elementDecoder; + this.type = type; + } + + public Object decode(Splittable data) { + Collection<Object> collection; + if (List.class.equals(type)) { + collection = new ArrayList<Object>(); + } else if (Set.class.equals(type)) { + collection = new HashSet<Object>(); + } else { + // Should not reach here + throw new RuntimeException(type.getName()); + } + for (int i = 0, j = data.size(); i < j; i++) { + Object element = data.isNull(i) ? null : elementDecoder.decode(data.get(i)); + collection.add(element); + } + return collection; + } + + public void encode(StringBuilder sb, Object value) { + if (value == null) { + sb.append("null"); + return; + } + + Iterator<?> it = ((Collection<?>) value).iterator(); + sb.append("["); + if (it.hasNext()) { + elementDecoder.encode(sb, it.next()); + while (it.hasNext()) { + sb.append(","); + elementDecoder.encode(sb, it.next()); + } + } + sb.append("]"); + } + } + + class EnumCoder<E extends Enum<E>> implements Coder { + private final Class<E> type; + + public EnumCoder(Class<E> type) { + this.type = type; + } + + public Object decode(Splittable data) { + return enumMap.getEnum(type, data.asString()); + } + + public void encode(StringBuilder sb, Object value) { + if (value == null) { + sb.append("null"); + } + sb.append(StringQuoter.quote(enumMap.getToken((Enum<?>) value))); + } + } + + /** + * Used to stop processing. + */ + static class HaltException extends RuntimeException { + public HaltException(RuntimeException cause) { + super(cause); + } + + @Override + public RuntimeException getCause() { + return (RuntimeException) super.getCause(); + } + } + + class MapCoder implements Coder { + private final Coder keyDecoder; + private final Coder valueDecoder; + + /** + * Parameters in reversed order to accommodate stack-based setup. + */ + public MapCoder(Coder valueDecoder, Coder keyDecoder) { + this.keyDecoder = keyDecoder; + this.valueDecoder = valueDecoder; + } + + public Object decode(Splittable data) { + Map<Object, Object> toReturn = new HashMap<Object, Object>(); + if (data.isIndexed()) { + assert data.size() == 2 : "Wrong data size: " + data.size(); + Splittable keys = data.get(0); + Splittable values = data.get(1); + for (int i = 0, j = keys.size(); i < j; i++) { + Object key = keys.isNull(i) ? null : keyDecoder.decode(keys.get(i)); + Object value = values.isNull(i) ? null : valueDecoder.decode(values.get(i)); + toReturn.put(key, value); + } + } else { + ValueCoder keyValueDecoder = (ValueCoder) keyDecoder; + for (String rawKey : data.getPropertyKeys()) { + Object key = keyValueDecoder.decode(rawKey); + Object value = data.isNull(rawKey) ? null : valueDecoder.decode(data.get(rawKey)); + toReturn.put(key, value); + } + } + return toReturn; + } + + public void encode(StringBuilder sb, Object value) { + if (value == null) { + sb.append("null"); + return; + } + + Map<?, ?> map = (Map<?, ?>) value; + boolean isSimpleMap = keyDecoder instanceof ValueCoder; + if (isSimpleMap) { + boolean first = true; + sb.append("{"); + for (Map.Entry<?, ?> entry : map.entrySet()) { + Object mapKey = entry.getKey(); + if (mapKey == null) { + // A null key in a simple map is meaningless + continue; + } + Object mapValue = entry.getValue(); + if (mapValue == null) { + // A null value can be ignored + continue; + } + + if (first) { + first = false; + } else { + sb.append(","); + } + + keyDecoder.encode(sb, mapKey); + sb.append(":"); + valueDecoder.encode(sb, mapValue); + } + sb.append("}"); + } else { + List<Object> keys = new ArrayList<Object>(map.size()); + List<Object> values = new ArrayList<Object>(map.size()); + for (Map.Entry<?, ?> entry : map.entrySet()) { + keys.add(entry.getKey()); + values.add(entry.getValue()); + } + sb.append("["); + new CollectionCoder(List.class, keyDecoder).encode(sb, keys); + sb.append(","); + new CollectionCoder(List.class, valueDecoder).encode(sb, values); + sb.append("]"); + } + } + } + + class ObjectCoder implements Coder { + private final Class<?> type; + + public ObjectCoder(Class<?> type) { + this.type = type; + } + + public Object decode(Splittable data) { + AutoBean<?> bean = doDecode(type, data); + return bean == null ? null : bean.as(); + } + + public void encode(StringBuilder sb, Object value) { + if (value == null) { + sb.append("null"); + return; + } + doEncode(sb, AutoBeanUtils.getAutoBean(value)); + } + } + + /** + * Extracts properties from a bean and turns them into JSON text. + */ + class PropertyGetter extends AutoBeanVisitor { + private boolean first = true; + private final StringBuilder sb; + + public PropertyGetter(StringBuilder sb) { + this.sb = sb; + } + + @Override + public void endVisit(AutoBean<?> bean, Context ctx) { + sb.append("}"); + seen.pop(); + } + + @Override + public boolean visit(AutoBean<?> bean, Context ctx) { + if (seen.contains(bean)) { + throw new HaltException(new UnsupportedOperationException("Cycles not supported")); + } + seen.push(bean); + sb.append("{"); + return true; + } + + @Override + public boolean visitReferenceProperty(String propertyName, AutoBean<?> value, + PropertyContext ctx) { + if (value != null) { + encodeProperty(propertyName, value.as(), ctx); + } + return false; + } + + @Override + public boolean visitValueProperty(String propertyName, Object value, PropertyContext ctx) { + if (value != null && !value.equals(ValueCodex.getUninitializedFieldValue(ctx.getType()))) { + encodeProperty(propertyName, value, ctx); + } + return false; + } + + private void encodeProperty(String propertyName, Object value, PropertyContext ctx) { + CoderCreator pd = new CoderCreator(); + ctx.accept(pd); + Coder decoder = pd.getCoder(); + if (first) { + first = false; + } else { + sb.append(","); + } + sb.append(StringQuoter.quote(propertyName)); + sb.append(":"); + decoder.encode(sb, value); + } + } + + /** + * Populates beans with data extracted from an evaluated JSON payload. + */ + class PropertySetter extends AutoBeanVisitor { + private Splittable data; + + public void decodeInto(Splittable data, AutoBean<?> bean) { + this.data = data; + bean.accept(this); + } + + @Override + public boolean visitReferenceProperty(String propertyName, AutoBean<?> value, + PropertyContext ctx) { + decodeProperty(propertyName, ctx); + return false; + } + + @Override + public boolean visitValueProperty(String propertyName, Object value, PropertyContext ctx) { + decodeProperty(propertyName, ctx); + return false; + } + + protected void decodeProperty(String propertyName, PropertyContext ctx) { + if (!data.isNull(propertyName)) { + CoderCreator pd = new CoderCreator(); + ctx.accept(pd); + Coder decoder = pd.getCoder(); + Object propertyValue = decoder.decode(data.get(propertyName)); + ctx.set(propertyValue); + } + } + } + + class SplittableDecoder implements Coder { + public Object decode(Splittable data) { + return data; + } + + public void encode(StringBuilder sb, Object value) { + if (value == null) { + sb.append("null"); + return; + } + sb.append(((Splittable) value).getPayload()); + } + } + + class ValueCoder implements Coder { + private final Class<?> type; + + public ValueCoder(Class<?> type) { + assert type.getEnumConstants() == null : "Should use EnumTypeCodex"; + this.type = type; + } + + public Object decode(Splittable propertyValue) { + return decode(propertyValue.asString()); + } + + public Object decode(String propertyValue) { + return ValueCodex.decode(type, propertyValue); + } + + public void encode(StringBuilder sb, Object value) { + sb.append(ValueCodex.encode(value).getPayload()); + } + } + public static <T> AutoBean<T> decode(AutoBeanFactory factory, Class<T> clazz, Splittable data) { - return AutoBeanCodexImpl.doDecode(EncodeState.forDecode(factory), clazz, data); + return new AutoBeanCodex(factory).doDecode(clazz, data); } /** @@ -46,8 +404,7 @@ * @param factory an AutoBeanFactory capable of producing {@code AutoBean<T>} * @param clazz the expected return type * @param payload a payload string previously generated by - * {@link #encode(AutoBean)}{@link Splittable#getPayload() - * .getPayload()}. + * {@link #encode(AutoBean)} * @return an AutoBean containing the payload contents */ public static <T> AutoBean<T> decode(AutoBeanFactory factory, Class<T> clazz, String payload) { @@ -63,7 +420,7 @@ * @param bean the target AutoBean */ public static void decodeInto(Splittable data, AutoBean<?> bean) { - AutoBeanCodexImpl.doDecodeInto(EncodeState.forDecode(bean.getFactory()), data, bean); + new AutoBeanCodex(bean.getFactory()).doDecodeInto(data, bean); } /** @@ -75,12 +432,42 @@ */ public static Splittable encode(AutoBean<?> bean) { if (bean == null) { - return Splittable.NULL; + return LazySplittable.NULL; } StringBuilder sb = new StringBuilder(); - EncodeState state = EncodeState.forEncode(bean.getFactory(), sb); - AutoBeanCodexImpl.doEncode(state, bean); - return StringQuoter.split(sb.toString()); + new AutoBeanCodex(bean.getFactory()).doEncode(sb, bean); + return new LazySplittable(sb.toString()); + } + + private final EnumMap enumMap; + private final AutoBeanFactory factory; + private final Stack<AutoBean<?>> seen = new Stack<AutoBean<?>>(); + + private AutoBeanCodex(AutoBeanFactory factory) { + this.factory = factory; + this.enumMap = factory instanceof EnumMap ? (EnumMap) factory : null; + } + + <T> AutoBean<T> doDecode(Class<T> clazz, Splittable data) { + AutoBean<T> toReturn = factory.create(clazz); + if (toReturn == null) { + throw new IllegalArgumentException(clazz.getName()); + } + doDecodeInto(data, toReturn); + return toReturn; + } + + void doDecodeInto(Splittable data, AutoBean<?> bean) { + new PropertySetter().decodeInto(data, bean); + } + + void doEncode(StringBuilder sb, AutoBean<?> bean) { + PropertyGetter e = new PropertyGetter(sb); + try { + bean.accept(e); + } catch (HaltException ex) { + throw ex.getCause(); + } } }
diff --git a/user/src/com/google/gwt/autobean/shared/AutoBeanUtils.java b/user/src/com/google/gwt/autobean/shared/AutoBeanUtils.java index ca8de60..e617ddf 100644 --- a/user/src/com/google/gwt/autobean/shared/AutoBeanUtils.java +++ b/user/src/com/google/gwt/autobean/shared/AutoBeanUtils.java
@@ -314,7 +314,8 @@ */ private static boolean sameOrEquals(Collection<?> collection, Collection<?> otherCollection, Map<PendingComparison, Comparison> pending, Map<Object, Object> pairs) { - if (collection.size() != otherCollection.size()) { + if (collection.size() != otherCollection.size() + || !collection.getClass().equals(otherCollection.getClass())) { return false; }
diff --git a/user/src/com/google/gwt/autobean/shared/Splittable.java b/user/src/com/google/gwt/autobean/shared/Splittable.java index 9011fc7..65a2286 100644 --- a/user/src/com/google/gwt/autobean/shared/Splittable.java +++ b/user/src/com/google/gwt/autobean/shared/Splittable.java
@@ -15,8 +15,6 @@ */ package com.google.gwt.autobean.shared; -import com.google.gwt.autobean.shared.impl.StringQuoter; - import java.util.List; /** @@ -26,41 +24,11 @@ */ public interface Splittable { /** - * A value that represents {@code null}. - */ - Splittable NULL = StringQuoter.nullValue(); - - /** - * Returns a boolean representation of the data; - */ - boolean asBoolean(); - - /** - * Returns a numeric representation of the data. - */ - double asNumber(); - - /** - * Assign the splittable to the specified index of the {@code parent} object. - */ - void assign(Splittable parent, int index); - - /** - * Assign the splittable to the named property of the {@code parent} object. - */ - void assign(Splittable parent, String propertyName); - - /** * Returns a string representation of the data. */ String asString(); /** - * Clones the Splittable, ignoring cycles and tags. - */ - Splittable deepCopy(); - - /** * Returns the nth element of a list. */ Splittable get(int index); @@ -82,16 +50,6 @@ List<String> getPropertyKeys(); /** - * Returns a value previously set with {@link #setReified(String, Object)}. - */ - Object getReified(String key); - - /** - * Returns {@code true} if the value of the Splittable is a boolean. - */ - boolean isBoolean(); - - /** * Returns {@code} true if {@link #size()} and {@link #get(int)} can be * expected to return meaningful values. */ @@ -104,49 +62,23 @@ boolean isKeyed(); /** - * Indicates if the nth element of a list is null or undefined. + * Indicates if the nth element of a list is null. */ boolean isNull(int index); /** - * Indicates if the named property is null or undefined. + * Indicates if the named property is null. */ boolean isNull(String key); /** - * Returns {@code true} if the value of the Splittable is numeric. - */ - boolean isNumber(); - - /** - * Returns {@code true} if {@link #setReified(String, Object)} has been called - * with the given key. - */ - boolean isReified(String key); - - /** * Returns {@code} true if {@link #asString()} can be expected to return a * meaningful value. */ boolean isString(); /** - * Returns {@code true} if the value of the key is undefined. - */ - boolean isUndefined(String key); - - /** - * Associates a tag value with the Splittable. - */ - void setReified(String key, Object object); - - /** - * Resets the length of an indexed Splittable. - */ - void setSize(int i); - - /** - * Returns the size of an indexed Splittable. + * Returns the size of the list. */ int size(); } \ No newline at end of file
diff --git a/user/src/com/google/gwt/autobean/shared/ValueCodex.java b/user/src/com/google/gwt/autobean/shared/ValueCodex.java index 8234ee6..b5e103d 100644 --- a/user/src/com/google/gwt/autobean/shared/ValueCodex.java +++ b/user/src/com/google/gwt/autobean/shared/ValueCodex.java
@@ -15,6 +15,7 @@ */ package com.google.gwt.autobean.shared; +import com.google.gwt.autobean.shared.impl.LazySplittable; import com.google.gwt.autobean.shared.impl.StringQuoter; import java.math.BigDecimal; @@ -37,13 +38,13 @@ } @Override - public BigDecimal decode(Class<?> clazz, Splittable value) { - return new BigDecimal(value.asString()); + public BigDecimal decode(Class<?> clazz, String value) { + return new BigDecimal(value); } @Override - public Splittable encode(Object value) { - return StringQuoter.create(((BigDecimal) value).toString()); + public String toJsonExpression(Object value) { + return StringQuoter.quote(((BigDecimal) value).toString()); } }, BIG_INTEGER(BigInteger.class) { @@ -53,46 +54,31 @@ } @Override - public BigInteger decode(Class<?> clazz, Splittable value) { - return new BigInteger(value.asString()); + public BigInteger decode(Class<?> clazz, String value) { + return new BigInteger(value); } @Override - public Splittable encode(Object value) { - return StringQuoter.create(((BigInteger) value).toString()); + public String toJsonExpression(Object value) { + return StringQuoter.quote(((BigInteger) value).toString()); } }, BOOLEAN(Boolean.class, boolean.class, false) { @Override - public Boolean decode(Class<?> clazz, Splittable value) { - return value.asBoolean(); - } - - @Override - public Splittable encode(Object value) { - return StringQuoter.create((Boolean) value); + public Boolean decode(Class<?> clazz, String value) { + return Boolean.valueOf(value); } }, BYTE(Byte.class, byte.class, (byte) 0) { @Override - public Byte decode(Class<?> clazz, Splittable value) { - return (byte) value.asNumber(); - } - - @Override - public Splittable encode(Object value) { - return StringQuoter.create((Byte) value); + public Byte decode(Class<?> clazz, String value) { + return Byte.valueOf(value); } }, CHARACTER(Character.class, char.class, (char) 0) { @Override - public Character decode(Class<?> clazz, Splittable value) { - return value.asString().charAt(0); - } - - @Override - public Splittable encode(Object value) { - return StringQuoter.create(String.valueOf((Character) value)); + public Character decode(Class<?> clazz, String value) { + return value.charAt(0); } }, DATE(Date.class) { @@ -102,111 +88,80 @@ } @Override - public Date decode(Class<?> clazz, Splittable value) { - return StringQuoter.tryParseDate(value.asString()); + public Date decode(Class<?> clazz, String value) { + return StringQuoter.tryParseDate(value); } @Override - public Splittable encode(Object value) { - return StringQuoter.create(String.valueOf(((Date) value).getTime())); + public String toJsonExpression(Object value) { + return String.valueOf(((Date) value).getTime()); } }, DOUBLE(Double.class, double.class, 0d) { @Override - public Double decode(Class<?> clazz, Splittable value) { - return value.asNumber(); - } - - @Override - public Splittable encode(Object value) { - return StringQuoter.create((Double) value); + public Double decode(Class<?> clazz, String value) { + return Double.valueOf(value); } }, ENUM(Enum.class) { @Override - public Enum<?> decode(Class<?> clazz, Splittable value) { - return (Enum<?>) clazz.getEnumConstants()[(int) value.asNumber()]; + public Enum<?> decode(Class<?> clazz, String value) { + return (Enum<?>) clazz.getEnumConstants()[Integer.valueOf(value)]; } @Override - public Splittable encode(Object value) { - return StringQuoter.create(((Enum<?>) value).ordinal()); + public String toJsonExpression(Object value) { + return String.valueOf(((Enum<?>) value).ordinal()); } }, FLOAT(Float.class, float.class, 0f) { @Override - public Float decode(Class<?> clazz, Splittable value) { - return (float) value.asNumber(); - } - - @Override - public Splittable encode(Object value) { - return StringQuoter.create((Float) value); + public Float decode(Class<?> clazz, String value) { + return Float.valueOf(value); } }, INTEGER(Integer.class, int.class, 0) { @Override - public Integer decode(Class<?> clazz, Splittable value) { - return Integer.valueOf((int) value.asNumber()); - } - - @Override - public Splittable encode(Object value) { - return StringQuoter.create((Integer) value); + public Integer decode(Class<?> clazz, String value) { + return Integer.valueOf(value); } }, LONG(Long.class, long.class, 0L) { @Override - public Long decode(Class<?> clazz, Splittable value) { - return Long.parseLong(value.asString()); + public Long decode(Class<?> clazz, String value) { + return Long.valueOf(value); } @Override - public Splittable encode(Object value) { - return StringQuoter.create(String.valueOf((Long) value)); + public String toJsonExpression(Object value) { + // Longs cannot be expressed as a JS double + if (value instanceof Long) { + return StringQuoter.quote(String.valueOf(value)); + } else { + throw new IllegalArgumentException("value should be a Long"); + } } }, SHORT(Short.class, short.class, (short) 0) { @Override - public Short decode(Class<?> clazz, Splittable value) { - return (short) value.asNumber(); - } - - @Override - public Splittable encode(Object value) { - return StringQuoter.create((Short) value); + public Short decode(Class<?> clazz, String value) { + return Short.valueOf(value); } }, STRING(String.class) { @Override - public String decode(Class<?> clazz, Splittable value) { - return value.asString(); - } - - @Override - public Splittable encode(Object value) { - return StringQuoter.create((String) value); - } - }, - SPLITTABLE(Splittable.class) { - @Override - public Splittable decode(Class<?> clazz, Splittable value) { + public String decode(Class<?> clazz, String value) { return value; } @Override - public Splittable encode(Object value) { - return (Splittable) value; + public String toJsonExpression(Object value) { + return StringQuoter.quote((String) value); } }, VOID(Void.class, void.class, null) { @Override - public Void decode(Class<?> clazz, Splittable value) { - return null; - } - - @Override - public Splittable encode(Object value) { + public Void decode(Class<?> clazz, String value) { return null; } }; @@ -235,9 +190,7 @@ return false; } - public abstract Object decode(Class<?> clazz, Splittable value); - - public abstract Splittable encode(Object value); + public abstract Object decode(Class<?> clazz, String value); public Object getDefaultValue() { return defaultValue; @@ -250,6 +203,10 @@ public Class<?> getType() { return type; } + + public String toJsonExpression(Object value) { + return String.valueOf(value); + } } private static final Set<Class<?>> ALL_VALUE_TYPES; @@ -280,23 +237,19 @@ return ValueCodexHelper.canDecode(clazz); } - @SuppressWarnings("unchecked") public static <T> T decode(Class<T> clazz, Splittable split) { - if (split == null || split == Splittable.NULL) { + if (split == null || split == LazySplittable.NULL) { return null; } - return (T) getTypeOrDie(clazz).decode(clazz, split); + return decode(clazz, split.asString()); } - /** - * No callers in GWT codebase. - * - * @deprecated use {@link #decode(Class, Splittable)} instead. - * @throws UnsupportedOperationException - */ - @Deprecated + @SuppressWarnings("unchecked") public static <T> T decode(Class<T> clazz, String string) { - throw new UnsupportedOperationException(); + if (string == null) { + return null; + } + return (T) getTypeOrDie(clazz).decode(clazz, string); } /** @@ -305,14 +258,14 @@ */ public static Splittable encode(Class<?> clazz, Object obj) { if (obj == null) { - return Splittable.NULL; + return LazySplittable.NULL; } - return getTypeOrDie(clazz).encode(obj); + return new LazySplittable(getTypeOrDie(clazz).toJsonExpression(obj)); } public static Splittable encode(Object obj) { if (obj == null) { - return Splittable.NULL; + return LazySplittable.NULL; } Type t = findType(obj.getClass()); // Try upcasting @@ -327,7 +280,7 @@ if (t == null) { throw new UnsupportedOperationException(obj.getClass().getName()); } - return t.encode(obj); + return new LazySplittable(t.toJsonExpression(obj)); } /**
diff --git a/user/src/com/google/gwt/autobean/shared/impl/AbstractAutoBean.java b/user/src/com/google/gwt/autobean/shared/impl/AbstractAutoBean.java index d45da29..ad2dc20 100644 --- a/user/src/com/google/gwt/autobean/shared/impl/AbstractAutoBean.java +++ b/user/src/com/google/gwt/autobean/shared/impl/AbstractAutoBean.java
@@ -20,9 +20,6 @@ import com.google.gwt.autobean.shared.AutoBeanUtils; import com.google.gwt.autobean.shared.AutoBeanVisitor; import com.google.gwt.autobean.shared.AutoBeanVisitor.Context; -import com.google.gwt.autobean.shared.Splittable; -import com.google.gwt.autobean.shared.impl.AutoBeanCodexImpl.Coder; -import com.google.gwt.autobean.shared.impl.AutoBeanCodexImpl.EncodeState; import com.google.gwt.core.client.impl.WeakMapping; import java.util.HashMap; @@ -35,7 +32,7 @@ * * @param <T> the wrapper type */ -public abstract class AbstractAutoBean<T> implements AutoBean<T>, HasSplittable { +public abstract class AbstractAutoBean<T> implements AutoBean<T> { /** * Used to avoid cycles when visiting. */ @@ -47,50 +44,64 @@ } } - public static final String UNSPLITTABLE_VALUES_KEY = "__unsplittableValues"; protected static final Object[] EMPTY_OBJECT = new Object[0]; /** * Used by {@link #createSimplePeer()}. */ - protected Splittable data; - protected T wrapped; + protected final Map<String, Object> values; + private final AutoBeanFactory factory; private boolean frozen; + /** * Lazily initialized by {@link #setTag(String, Object)} because not all * instances will make use of tags. */ private Map<String, Object> tags; private final boolean usingSimplePeer; + private T wrapped; /** * Constructor that will use a generated simple peer. */ protected AbstractAutoBean(AutoBeanFactory factory) { - this(factory, StringQuoter.createSplittable()); - } - - /** - * Constructor that will use a generated simple peer, backed with existing - * data. - */ - protected AbstractAutoBean(AutoBeanFactory factory, Splittable data) { - this.data = data; this.factory = factory; usingSimplePeer = true; - wrapped = createSimplePeer(); + values = new HashMap<String, Object>(); } /** - * Constructor that wraps an existing object. The parameters on this method - * are reversed to avoid conflicting with the other two-arg constructor for - * {@code AutoBean<Splittable>} instances. + * Clone constructor. */ - protected AbstractAutoBean(T wrapped, AutoBeanFactory factory) { + protected AbstractAutoBean(AbstractAutoBean<T> toClone, boolean deep) { + this.factory = toClone.factory; + if (!toClone.usingSimplePeer) { + throw new IllegalStateException("Cannot clone wrapped bean"); + } + if (toClone.tags != null) { + tags = new HashMap<String, Object>(toClone.tags); + } + usingSimplePeer = true; + values = new HashMap<String, Object>(toClone.values); + + if (deep) { + for (Map.Entry<String, Object> entry : values.entrySet()) { + AutoBean<?> auto = AutoBeanUtils.getAutoBean(entry.getValue()); + if (auto != null) { + entry.setValue(auto.clone(true).as()); + } + } + } + } + + /** + * Constructor that wraps an existing object. + */ + protected AbstractAutoBean(AutoBeanFactory factory, T wrapped) { this.factory = factory; usingSimplePeer = false; - data = null; + values = null; this.wrapped = wrapped; // Used by AutoBeanUtils @@ -103,31 +114,17 @@ public abstract T as(); - public AutoBean<T> clone(boolean deep) { - throw new UnsupportedOperationException(); - } + public abstract AutoBean<T> clone(boolean deep); public AutoBeanFactory getFactory() { return factory; } - public Splittable getSplittable() { - return data; - } - @SuppressWarnings("unchecked") public <Q> Q getTag(String tagName) { return tags == null ? null : (Q) tags.get(tagName); } - /** - * Indicates that the value returned from {@link #getSplittable()} may not - * contain all of the data encapsulated by the AutoBean. - */ - public boolean hasUnsplittableValues() { - return data.isReified(UNSPLITTABLE_VALUES_KEY); - } - public boolean isFrozen() { return frozen; } @@ -136,16 +133,6 @@ return !usingSimplePeer; } - public void setData(Splittable data) { - assert data != null : "null data"; - this.data = data; - /* - * The simple peer aliases the data object from the enclosing bean to avoid - * needing to call up the this.this$0 chain. - */ - wrapped = createSimplePeer(); - } - public void setFrozen(boolean frozen) { this.frozen = frozen; } @@ -221,30 +208,11 @@ return AutoBeanUtils.<W, W> getAutoBean(obj).as(); } - /** - * Native getters and setters for primitive properties are generated for each - * type to ensure inlining. - */ - protected <Q> Q getOrReify(String propertyName) { - checkWrapped(); - if (data.isReified(propertyName)) { - @SuppressWarnings("unchecked") - Q temp = (Q) data.getReified(propertyName); - return temp; - } - if (data.isNull(propertyName)) { - return null; - } - data.setReified(propertyName, null); - Coder coder = AutoBeanCodexImpl.doCoderFor(this, propertyName); - @SuppressWarnings("unchecked") - Q toReturn = (Q) coder.decode(EncodeState.forDecode(factory), data.get(propertyName)); - data.setReified(propertyName, toReturn); - return toReturn; - } - protected T getWrapped() { - checkWrapped(); + if (wrapped == null) { + assert usingSimplePeer : "checkWrapped should have failed"; + wrapped = createSimplePeer(); + } return wrapped; } @@ -265,27 +233,6 @@ protected void set(String method, Object value) { } - protected void setProperty(String propertyName, Object value) { - checkWrapped(); - checkFrozen(); - data.setReified(propertyName, value); - if (value == null) { - Splittable.NULL.assign(data, propertyName); - return; - } - Coder coder = AutoBeanCodexImpl.doCoderFor(this, propertyName); - Splittable backing = coder.extractSplittable(EncodeState.forDecode(factory), value); - if (backing == null) { - /* - * External data type, such as an ArrayList or a concrete implementation - * of a setter's interface type. This means that a slow serialization pass - * is necessary. - */ - data.setReified(UNSPLITTABLE_VALUES_KEY, true); - } else { - backing.assign(data, propertyName); - } - } - - protected abstract void traverseProperties(AutoBeanVisitor visitor, OneShotContext ctx); + protected abstract void traverseProperties(AutoBeanVisitor visitor, + OneShotContext ctx); }
diff --git a/user/src/com/google/gwt/autobean/shared/impl/AutoBeanCodexImpl.java b/user/src/com/google/gwt/autobean/shared/impl/AutoBeanCodexImpl.java deleted file mode 100644 index 88cd297..0000000 --- a/user/src/com/google/gwt/autobean/shared/impl/AutoBeanCodexImpl.java +++ /dev/null
@@ -1,607 +0,0 @@ -/* - * Copyright 2011 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.autobean.shared.impl; - -import com.google.gwt.autobean.shared.AutoBean; -import com.google.gwt.autobean.shared.AutoBeanFactory; -import com.google.gwt.autobean.shared.AutoBeanUtils; -import com.google.gwt.autobean.shared.AutoBeanVisitor; -import com.google.gwt.autobean.shared.AutoBeanVisitor.ParameterizationVisitor; -import com.google.gwt.autobean.shared.Splittable; -import com.google.gwt.autobean.shared.ValueCodex; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Stack; - -/** - * Contains the implementation details of AutoBeanCodex. This type was factored - * out of AutoBeanCodex so that various implementation details can be accessed - * without polluting a public API. - */ -public class AutoBeanCodexImpl { - - /** - * Describes a means of encoding or decoding a particular type of data to or - * from a wire format representation. Any given instance of a Coder should be - * stateless; any state required for operation must be maintained in an - * {@link EncodeState}. - */ - public interface Coder { - Object decode(EncodeState state, Splittable data); - - void encode(EncodeState state, Object value); - - Splittable extractSplittable(EncodeState state, Object value); - } - - /** - * Contains transient state for Coder operation. - */ - public static class EncodeState { - /** - * Constructs a state object used for decoding payloads. - */ - public static EncodeState forDecode(AutoBeanFactory factory) { - return new EncodeState(factory, null); - } - - /** - * Constructs a state object used for encoding payloads. - */ - public static EncodeState forEncode(AutoBeanFactory factory, StringBuilder sb) { - return new EncodeState(factory, sb); - } - - /** - * Constructs a "stateless" state for testing Coders that do not require - * AutoBean implementation details. - */ - public static EncodeState forTesting() { - return new EncodeState(null, null); - } - - final EnumMap enumMap; - final AutoBeanFactory factory; - final StringBuilder sb; - final Stack<AutoBean<?>> seen; - - private EncodeState(AutoBeanFactory factory, StringBuilder sb) { - this.factory = factory; - enumMap = factory instanceof EnumMap ? (EnumMap) factory : null; - this.sb = sb; - this.seen = sb == null ? null : new Stack<AutoBean<?>>(); - } - } - - /** - * Dynamically creates a Coder that is capable of operating on a particular - * parameterization of a datastructure (e.g. {@code Map<String, List<String>>} - * ). - */ - static class CoderCreator extends ParameterizationVisitor { - private Stack<Coder> stack = new Stack<Coder>(); - - @Override - public void endVisitType(Class<?> type) { - if (List.class.equals(type) || Set.class.equals(type)) { - stack.push(collectionCoder(type, stack.pop())); - } else if (Map.class.equals(type)) { - // Note that the parameters are passed in reverse order - stack.push(mapCoder(stack.pop(), stack.pop())); - } else if (Splittable.class.equals(type)) { - stack.push(splittableCoder()); - } else if (type.getEnumConstants() != null) { - @SuppressWarnings(value = {"unchecked"}) - Class<Enum<?>> enumType = (Class<Enum<?>>) type; - stack.push(enumCoder(enumType)); - } else if (ValueCodex.canDecode(type)) { - stack.push(valueCoder(type)); - } else { - stack.push(objectCoder(type)); - } - } - - public Coder getCoder() { - assert stack.size() == 1 : "Incorrect size: " + stack.size(); - return stack.pop(); - } - } - - /** - * Constructs one of the lightweight collection types. - */ - static class CollectionCoder implements Coder { - private final Coder elementDecoder; - private final Class<?> type; - - public CollectionCoder(Class<?> type, Coder elementDecoder) { - this.elementDecoder = elementDecoder; - this.type = type; - } - - public Object decode(EncodeState state, Splittable data) { - Collection<Object> collection; - if (List.class.equals(type)) { - collection = new SplittableList<Object>(data, elementDecoder, state); - } else if (Set.class.equals(type)) { - collection = new SplittableSet<Object>(data, elementDecoder, state); - } else { - // Should not reach here - throw new RuntimeException(type.getName()); - } - return collection; - } - - public void encode(EncodeState state, Object value) { - if (value == null) { - state.sb.append("null"); - return; - } - - Iterator<?> it = ((Collection<?>) value).iterator(); - state.sb.append("["); - if (it.hasNext()) { - elementDecoder.encode(state, it.next()); - while (it.hasNext()) { - state.sb.append(","); - elementDecoder.encode(state, it.next()); - } - } - state.sb.append("]"); - } - - public Splittable extractSplittable(EncodeState state, Object value) { - return tryExtractSplittable(value); - } - } - - /** - * Produces enums. - * - * @param <E> - */ - static class EnumCoder<E extends Enum<?>> implements Coder { - private final Class<E> type; - - public EnumCoder(Class<E> type) { - this.type = type; - } - - public Object decode(EncodeState state, Splittable data) { - return state.enumMap.getEnum(type, data.asString()); - } - - public void encode(EncodeState state, Object value) { - if (value == null) { - state.sb.append("null"); - return; - } - state.sb.append(StringQuoter.quote(state.enumMap.getToken((Enum<?>) value))); - } - - public Splittable extractSplittable(EncodeState state, Object value) { - return StringQuoter.split(StringQuoter.quote(state.enumMap.getToken((Enum<?>) value))); - } - } - - /** - * Used to stop processing. - */ - static class HaltException extends RuntimeException { - public HaltException(RuntimeException cause) { - super(cause); - } - - @Override - public RuntimeException getCause() { - return (RuntimeException) super.getCause(); - } - } - - /** - * Constructs one of the lightweight Map types, depending on the key type. - */ - static class MapCoder implements Coder { - private final Coder keyDecoder; - private final Coder valueDecoder; - - /** - * Parameters in reversed order to accommodate stack-based setup. - */ - public MapCoder(Coder valueDecoder, Coder keyDecoder) { - this.keyDecoder = keyDecoder; - this.valueDecoder = valueDecoder; - } - - public Object decode(EncodeState state, Splittable data) { - Map<Object, Object> toReturn; - if (data.isIndexed()) { - assert data.size() == 2 : "Wrong data size: " + data.size(); - toReturn = new SplittableComplexMap<Object, Object>(data, keyDecoder, valueDecoder, state); - } else { - toReturn = new SplittableSimpleMap<Object, Object>(data, keyDecoder, valueDecoder, state); - } - return toReturn; - } - - public void encode(EncodeState state, Object value) { - if (value == null) { - state.sb.append("null"); - return; - } - - Map<?, ?> map = (Map<?, ?>) value; - boolean isSimpleMap = keyDecoder instanceof ValueCoder; - if (isSimpleMap) { - boolean first = true; - state.sb.append("{"); - for (Map.Entry<?, ?> entry : map.entrySet()) { - Object mapKey = entry.getKey(); - if (mapKey == null) { - // A null key in a simple map is meaningless - continue; - } - Object mapValue = entry.getValue(); - - if (first) { - first = false; - } else { - state.sb.append(","); - } - - keyDecoder.encode(state, mapKey); - state.sb.append(":"); - if (mapValue == null) { - // Null values must be preserved - state.sb.append("null"); - } else { - valueDecoder.encode(state, mapValue); - } - } - state.sb.append("}"); - } else { - List<Object> keys = new ArrayList<Object>(map.size()); - List<Object> values = new ArrayList<Object>(map.size()); - for (Map.Entry<?, ?> entry : map.entrySet()) { - keys.add(entry.getKey()); - values.add(entry.getValue()); - } - state.sb.append("["); - collectionCoder(List.class, keyDecoder).encode(state, keys); - state.sb.append(","); - collectionCoder(List.class, valueDecoder).encode(state, values); - state.sb.append("]"); - } - } - - public Splittable extractSplittable(EncodeState state, Object value) { - return tryExtractSplittable(value); - } - } - - /** - * Recurses into {@link AutoBeanCodexImpl}. - */ - static class ObjectCoder implements Coder { - private final Class<?> type; - - public ObjectCoder(Class<?> type) { - this.type = type; - } - - public Object decode(EncodeState state, Splittable data) { - AutoBean<?> bean = doDecode(state, type, data); - return bean == null ? null : bean.as(); - } - - public void encode(EncodeState state, Object value) { - if (value == null) { - state.sb.append("null"); - return; - } - doEncode(state, AutoBeanUtils.getAutoBean(value)); - } - - public Splittable extractSplittable(EncodeState state, Object value) { - return tryExtractSplittable(value); - } - } - - static class PropertyCoderCreator extends AutoBeanVisitor { - private AutoBean<?> bean; - - @Override - public boolean visit(AutoBean<?> bean, Context ctx) { - this.bean = bean; - return true; - } - - @Override - public boolean visitReferenceProperty(String propertyName, AutoBean<?> value, - PropertyContext ctx) { - maybeCreateCoder(propertyName, ctx); - return false; - } - - @Override - public boolean visitValueProperty(String propertyName, Object value, PropertyContext ctx) { - maybeCreateCoder(propertyName, ctx); - return false; - } - - private void maybeCreateCoder(String propertyName, PropertyContext ctx) { - CoderCreator creator = new CoderCreator(); - ctx.accept(creator); - coderFor.put(key(bean, propertyName), creator.getCoder()); - } - } - - /** - * Extracts properties from a bean and turns them into JSON text. - */ - static class PropertyGetter extends AutoBeanVisitor { - private boolean first = true; - private final EncodeState state; - - public PropertyGetter(EncodeState state) { - this.state = state; - } - - @Override - public void endVisit(AutoBean<?> bean, Context ctx) { - state.sb.append("}"); - state.seen.pop(); - } - - @Override - public boolean visit(AutoBean<?> bean, Context ctx) { - if (state.seen.contains(bean)) { - throw new HaltException(new UnsupportedOperationException("Cycles not supported")); - } - state.seen.push(bean); - state.sb.append("{"); - return true; - } - - @Override - public boolean visitReferenceProperty(String propertyName, AutoBean<?> value, - PropertyContext ctx) { - if (value != null) { - encodeProperty(propertyName, value.as(), ctx); - } - return false; - } - - @Override - public boolean visitValueProperty(String propertyName, Object value, PropertyContext ctx) { - if (value != null && !value.equals(ValueCodex.getUninitializedFieldValue(ctx.getType()))) { - encodeProperty(propertyName, value, ctx); - } - return false; - } - - private void encodeProperty(String propertyName, Object value, PropertyContext ctx) { - CoderCreator pd = new CoderCreator(); - ctx.accept(pd); - Coder decoder = pd.getCoder(); - if (first) { - first = false; - } else { - state.sb.append(","); - } - state.sb.append(StringQuoter.quote(propertyName)); - state.sb.append(":"); - decoder.encode(state, value); - } - } - - /** - * Populates beans with data extracted from an evaluated JSON payload. - */ - static class PropertySetter extends AutoBeanVisitor { - private Splittable data; - private EncodeState state; - - public void decodeInto(EncodeState state, Splittable data, AutoBean<?> bean) { - this.data = data; - this.state = state; - bean.accept(this); - } - - @Override - public boolean visitReferenceProperty(String propertyName, AutoBean<?> value, - PropertyContext ctx) { - decodeProperty(propertyName, ctx); - return false; - } - - @Override - public boolean visitValueProperty(String propertyName, Object value, PropertyContext ctx) { - decodeProperty(propertyName, ctx); - return false; - } - - protected void decodeProperty(String propertyName, PropertyContext ctx) { - if (!data.isNull(propertyName)) { - CoderCreator pd = new CoderCreator(); - ctx.accept(pd); - Coder decoder = pd.getCoder(); - Object propertyValue = decoder.decode(state, data.get(propertyName)); - ctx.set(propertyValue); - } - } - } - - /** - * A passthrough Coder. - */ - static class SplittableCoder implements Coder { - static final Coder INSTANCE = new SplittableCoder(); - - public Object decode(EncodeState state, Splittable data) { - return data; - } - - public void encode(EncodeState state, Object value) { - if (value == null) { - state.sb.append("null"); - return; - } - state.sb.append(((Splittable) value).getPayload()); - } - - public Splittable extractSplittable(EncodeState state, Object value) { - return (Splittable) value; - } - } - - /** - * Delegates to ValueCodex. - */ - static class ValueCoder implements Coder { - private final Class<?> type; - - public ValueCoder(Class<?> type) { - assert type.getEnumConstants() == null : "Should use EnumTypeCodex"; - this.type = type; - } - - public Object decode(EncodeState state, Splittable propertyValue) { - if (propertyValue == null || propertyValue == Splittable.NULL) { - return ValueCodex.getUninitializedFieldValue(type); - } - return ValueCodex.decode(type, propertyValue); - } - - public void encode(EncodeState state, Object value) { - state.sb.append(ValueCodex.encode(type, value).getPayload()); - } - - public Splittable extractSplittable(EncodeState state, Object value) { - return ValueCodex.encode(type, value); - } - } - - /** - * A map of AutoBean interface+property names to the Coder for that property. - */ - private static final Map<String, Coder> coderFor = new HashMap<String, Coder>(); - /** - * A map of types to a Coder that handles the type. - */ - private static final Map<Class<?>, Coder> coders = new HashMap<Class<?>, Coder>(); - - public static Coder collectionCoder(Class<?> type, Coder elementCoder) { - return new CollectionCoder(type, elementCoder); - } - - public static Coder doCoderFor(AutoBean<?> bean, String propertyName) { - String key = key(bean, propertyName); - Coder toReturn = coderFor.get(key); - if (toReturn == null) { - bean.accept(new PropertyCoderCreator()); - toReturn = coderFor.get(key); - if (toReturn == null) { - throw new IllegalArgumentException(propertyName); - } - } - return toReturn; - } - - public static <T> AutoBean<T> doDecode(EncodeState state, Class<T> clazz, Splittable data) { - @SuppressWarnings("unchecked") - AutoBean<T> toReturn = (AutoBean<T>) data.getReified(AutoBeanCodexImpl.class.getName()); - if (toReturn != null) { - return toReturn; - } - toReturn = state.factory.create(clazz); - data.setReified(AutoBeanCodexImpl.class.getName(), toReturn); - if (toReturn == null) { - throw new IllegalArgumentException(clazz.getName()); - } - ((AbstractAutoBean<T>) toReturn).setData(data); - return toReturn; - } - - public static void doDecodeInto(EncodeState state, Splittable data, AutoBean<?> bean) { - new PropertySetter().decodeInto(state, data, bean); - } - - public static void doEncode(EncodeState state, AutoBean<?> bean) { - PropertyGetter e = new PropertyGetter(state); - try { - bean.accept(e); - } catch (HaltException ex) { - throw ex.getCause(); - } - } - - public static <E extends Enum<?>> Coder enumCoder(Class<E> type) { - Coder toReturn = coders.get(type); - if (toReturn == null) { - toReturn = new EnumCoder<E>(type); - coders.put(type, toReturn); - } - return toReturn; - } - - public static Coder mapCoder(Coder valueCoder, Coder keyCoder) { - return new MapCoder(valueCoder, keyCoder); - } - - public static Coder objectCoder(Class<?> type) { - Coder toReturn = coders.get(type); - if (toReturn == null) { - toReturn = new ObjectCoder(type); - coders.put(type, toReturn); - } - return toReturn; - } - - public static Coder splittableCoder() { - return SplittableCoder.INSTANCE; - } - - public static Coder valueCoder(Class<?> type) { - Coder toReturn = coders.get(type); - if (toReturn == null) { - toReturn = new ValueCoder(type); - coders.put(type, toReturn); - } - return toReturn; - } - - static Splittable tryExtractSplittable(Object value) { - AutoBean<?> bean = AutoBeanUtils.getAutoBean(value); - if (bean != null) { - value = bean; - } - if (bean instanceof HasSplittable) { - return ((HasSplittable) bean).getSplittable(); - } - return null; - } - - private static String key(AutoBean<?> bean, String propertyName) { - return bean.getType().getName() + ":" + propertyName; - } -}
diff --git a/user/src/com/google/gwt/autobean/shared/impl/EnumMap.java b/user/src/com/google/gwt/autobean/shared/impl/EnumMap.java index 0a3d298..3b53d1b 100644 --- a/user/src/com/google/gwt/autobean/shared/impl/EnumMap.java +++ b/user/src/com/google/gwt/autobean/shared/impl/EnumMap.java
@@ -27,7 +27,7 @@ Class<? extends Enum<?>>[] value(); } - <E extends Enum<?>> E getEnum(Class<E> clazz, String token); + <E extends Enum<E>> E getEnum(Class<E> clazz, String token); String getToken(Enum<?> e); }
diff --git a/user/src/com/google/gwt/autobean/shared/impl/HasSplittable.java b/user/src/com/google/gwt/autobean/shared/impl/HasSplittable.java deleted file mode 100644 index 7ae4a33..0000000 --- a/user/src/com/google/gwt/autobean/shared/impl/HasSplittable.java +++ /dev/null
@@ -1,26 +0,0 @@ -/* - * Copyright 2011 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.autobean.shared.impl; - -import com.google.gwt.autobean.shared.Splittable; - -/** - * Allows reified type facades to return their underlying Splittable - * datastructure. - */ -public interface HasSplittable { - Splittable getSplittable(); -}
diff --git a/user/src/com/google/gwt/autobean/shared/impl/LazySplittable.java b/user/src/com/google/gwt/autobean/shared/impl/LazySplittable.java new file mode 100644 index 0000000..2fb4749 --- /dev/null +++ b/user/src/com/google/gwt/autobean/shared/impl/LazySplittable.java
@@ -0,0 +1,92 @@ +/* + * 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 com.google.gwt.autobean.shared.impl; + +import com.google.gwt.autobean.shared.Splittable; + +import java.util.List; + +/** + * Holds a string payload with the expectation that the object will be used only + * for creating a larger payload. + */ +public class LazySplittable implements Splittable { + public static final Splittable NULL = new LazySplittable("null"); + + private final String payload; + private Splittable split; + + public LazySplittable(String payload) { + this.payload = payload; + } + + public String asString() { + maybeSplit(); + return split.asString(); + } + + public Splittable get(int index) { + maybeSplit(); + return split.get(index); + } + + public Splittable get(String key) { + maybeSplit(); + return split.get(key); + } + + public String getPayload() { + return payload; + } + + public List<String> getPropertyKeys() { + maybeSplit(); + return split.getPropertyKeys(); + } + + public boolean isIndexed() { + return payload.charAt(0) == '['; + } + + public boolean isKeyed() { + return payload.charAt(0) == '{'; + } + + public boolean isNull(int index) { + maybeSplit(); + return split.isNull(index); + } + + public boolean isNull(String key) { + maybeSplit(); + return split.isNull(key); + } + + public boolean isString() { + return payload.charAt(0) == '\"'; + } + + public int size() { + maybeSplit(); + return split.size(); + } + + private void maybeSplit() { + if (split == null) { + split = StringQuoter.split(payload); + } + } +}
diff --git a/user/src/com/google/gwt/autobean/shared/impl/SplittableComplexMap.java b/user/src/com/google/gwt/autobean/shared/impl/SplittableComplexMap.java deleted file mode 100644 index 4225512..0000000 --- a/user/src/com/google/gwt/autobean/shared/impl/SplittableComplexMap.java +++ /dev/null
@@ -1,200 +0,0 @@ -/* - * Copyright 2011 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.autobean.shared.impl; - -import com.google.gwt.autobean.shared.Splittable; -import com.google.gwt.autobean.shared.impl.AutoBeanCodexImpl.Coder; -import com.google.gwt.autobean.shared.impl.AutoBeanCodexImpl.EncodeState; - -import java.util.AbstractCollection; -import java.util.AbstractSet; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.Set; - -/** - * A Map implementation for complex keys. - * - * @param <K> the key type - * @param <V> the value type - */ -public class SplittableComplexMap<K, V> implements Map<K, V>, HasSplittable { - private final Splittable data; - private final List<K> keys; - private final List<V> values; - - public SplittableComplexMap(Splittable data, Coder keyCoder, Coder valueCoder, EncodeState state) { - this.data = data; - this.keys = new SplittableList<K>(data.get(0), keyCoder, state); - this.values = new SplittableList<V>(data.get(1), valueCoder, state); - assert this.keys.size() == this.values.size(); - } - - public void clear() { - // Trigger ConcurrentModificationExceptions for any outstanding Iterators - keys.clear(); - values.clear(); - } - - public boolean containsKey(Object key) { - return keys.contains(key); - } - - public boolean containsValue(Object value) { - return values.contains(value); - } - - public Set<java.util.Map.Entry<K, V>> entrySet() { - return new AbstractSet<Map.Entry<K, V>>() { - - @Override - public Iterator<java.util.Map.Entry<K, V>> iterator() { - return new Iterator<Map.Entry<K, V>>() { - Iterator<K> keyIt = keys.iterator(); - ListIterator<V> valueIt = values.listIterator(); - - public boolean hasNext() { - assert keyIt.hasNext() == valueIt.hasNext(); - return keyIt.hasNext(); - } - - public java.util.Map.Entry<K, V> next() { - return new Map.Entry<K, V>() { - final K key = keyIt.next(); - final V value = valueIt.next(); - - public K getKey() { - return key; - } - - public V getValue() { - return value; - } - - public V setValue(V value) { - valueIt.set(value); - return value; - } - }; - } - - public void remove() { - keyIt.remove(); - valueIt.remove(); - } - }; - } - - @Override - public int size() { - return keys.size(); - } - }; - } - - public V get(Object key) { - int idx = keys.indexOf(key); - if (idx == -1) { - return null; - } - return values.get(idx); - } - - public Splittable getSplittable() { - return data; - } - - public boolean isEmpty() { - return keys.isEmpty(); - } - - public Set<K> keySet() { - return new AbstractSet<K>() { - @Override - public Iterator<K> iterator() { - return keys.iterator(); - } - - @Override - public int size() { - return keys.size(); - } - }; - } - - public V put(K key, V value) { - int idx = keys.indexOf(key); - if (idx == -1) { - keys.add(key); - values.add(value); - return null; - } - return values.set(idx, value); - } - - public void putAll(Map<? extends K, ? extends V> m) { - for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) { - put(entry.getKey(), entry.getValue()); - } - } - - public V remove(Object key) { - int idx = keys.indexOf(key); - if (idx == -1) { - return null; - } - keys.remove(idx); - return values.remove(idx); - } - - public int size() { - return keys.size(); - } - - public Collection<V> values() { - return new AbstractCollection<V>() { - @Override - public Iterator<V> iterator() { - return new Iterator<V>() { - final Iterator<K> keyIt = keys.iterator(); - final Iterator<V> valueIt = values.iterator(); - - public boolean hasNext() { - return keyIt.hasNext(); - } - - public V next() { - keyIt.next(); - return valueIt.next(); - } - - public void remove() { - keyIt.remove(); - valueIt.remove(); - } - }; - } - - @Override - public int size() { - return keys.size(); - } - }; - } -}
diff --git a/user/src/com/google/gwt/autobean/shared/impl/SplittableList.java b/user/src/com/google/gwt/autobean/shared/impl/SplittableList.java deleted file mode 100644 index 0a7df50..0000000 --- a/user/src/com/google/gwt/autobean/shared/impl/SplittableList.java +++ /dev/null
@@ -1,114 +0,0 @@ -/* - * Copyright 2011 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.autobean.shared.impl; - -import com.google.gwt.autobean.shared.Splittable; -import com.google.gwt.autobean.shared.impl.AutoBeanCodexImpl.Coder; -import com.google.gwt.autobean.shared.impl.AutoBeanCodexImpl.EncodeState; - -import java.util.AbstractList; - -/** - * A list implementation that lazily reifies its constituent elements. - * - * @param <E> the element type - */ -public class SplittableList<E> extends AbstractList<E> implements HasSplittable { - static <Q> Q reify(EncodeState state, Splittable data, int index, Coder coder) { - if (data.isNull(index)) { - return null; - } - @SuppressWarnings("unchecked") - Q toReturn = (Q) coder.decode(state, data.get(index)); - data.setReified(String.valueOf(index), toReturn); - return toReturn; - } - - static void set(EncodeState state, Splittable data, int index, Coder coder, Object value) { - data.setReified(String.valueOf(index), value); - if (value == null) { - Splittable.NULL.assign(data, index); - return; - } - Splittable backing = coder.extractSplittable(state, value); - if (backing == null) { - /* - * External data type, such as an ArrayList or a concrete implementation - * of a setter's interface type. This means that a slow serialization pass - * is necessary. - */ - data.setReified(AbstractAutoBean.UNSPLITTABLE_VALUES_KEY, true); - } else { - backing.assign(data, index); - } - } - - private Splittable data; - private final Coder elementCoder; - private final EncodeState state; - - public SplittableList(Splittable data, Coder elementCoder, EncodeState state) { - assert data.isIndexed() : "Expecting indexed data"; - this.data = data; - this.elementCoder = elementCoder; - this.state = state; - } - - @Override - public void add(int index, E element) { - set(state, data, index, elementCoder, element); - } - - @Override - public E get(int index) { - if (data.isReified(String.valueOf(index))) { - @SuppressWarnings("unchecked") - E toReturn = (E) data.getReified(String.valueOf(index)); - return toReturn; - } - // javac generics bug - return SplittableList.<E> reify(state, data, index, elementCoder); - } - - public Splittable getSplittable() { - return data; - } - - @Override - public E remove(int index) { - E toReturn = get(index); - // XXX This is terrible, use Array.splice - int newSize = data.size() - 1; - for (int i = index; i < newSize; i++) { - data.get(i + 1).assign(data, i); - data.setReified(String.valueOf(i), data.getReified(String.valueOf(i + 1))); - } - data.setSize(newSize); - return toReturn; - } - - @Override - public E set(int index, E element) { - E previous = get(index); - set(state, data, index, elementCoder, element); - return previous; - } - - @Override - public int size() { - return data.size(); - } -}
diff --git a/user/src/com/google/gwt/autobean/shared/impl/SplittableSet.java b/user/src/com/google/gwt/autobean/shared/impl/SplittableSet.java deleted file mode 100644 index fbd3f5d..0000000 --- a/user/src/com/google/gwt/autobean/shared/impl/SplittableSet.java +++ /dev/null
@@ -1,71 +0,0 @@ -/* - * Copyright 2011 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.autobean.shared.impl; - -import com.google.gwt.autobean.shared.Splittable; -import com.google.gwt.autobean.shared.impl.AutoBeanCodexImpl.Coder; -import com.google.gwt.autobean.shared.impl.AutoBeanCodexImpl.EncodeState; - -import java.util.AbstractSet; -import java.util.Iterator; - -/** - * This type is optimized for the read-only case and has {@code O(n)} insertion - * / lookup performance since computing hashcodes for the elements would require - * up-front reification. - * - * @param <E> the element type - */ -public class SplittableSet<E> extends AbstractSet<E> implements HasSplittable { - private SplittableList<E> data; - - public SplittableSet(Splittable data, Coder elementCoder, EncodeState state) { - this.data = new SplittableList<E>(data, elementCoder, state); - } - - @Override - public boolean add(E e) { - if (!data.contains(e)) { - data.add(e); - return true; - } - return false; - } - - @Override - public void clear() { - data.clear(); - } - - public Splittable getSplittable() { - return data.getSplittable(); - } - - @Override - public Iterator<E> iterator() { - return data.iterator(); - } - - @Override - public boolean remove(Object o) { - return data.remove(o); - } - - @Override - public int size() { - return data.size(); - } -}
diff --git a/user/src/com/google/gwt/autobean/shared/impl/SplittableSimpleMap.java b/user/src/com/google/gwt/autobean/shared/impl/SplittableSimpleMap.java deleted file mode 100644 index edfeff4..0000000 --- a/user/src/com/google/gwt/autobean/shared/impl/SplittableSimpleMap.java +++ /dev/null
@@ -1,255 +0,0 @@ -/* - * Copyright 2011 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.autobean.shared.impl; - -import com.google.gwt.autobean.shared.Splittable; -import com.google.gwt.autobean.shared.impl.AutoBeanCodexImpl.Coder; -import com.google.gwt.autobean.shared.impl.AutoBeanCodexImpl.EncodeState; - -import java.util.AbstractCollection; -import java.util.AbstractSet; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * A Map implementation for regular JSON maps with value-type keys. - * - * @param <K> the key type - * @param <V> the value type - */ -public class SplittableSimpleMap<K, V> implements Map<K, V>, HasSplittable { - private final Splittable data; - private final Coder keyCoder; - private final EncodeState state; - private final Coder valueCoder; - /** - * Don't hang the reified data from {@link #data} since we can't tell the - * __reified field from the actual data. - */ - private Splittable reified = StringQuoter.createSplittable(); - - public SplittableSimpleMap(Splittable data, Coder keyCoder, Coder valueCoder, EncodeState state) { - this.data = data; - this.keyCoder = keyCoder; - this.state = state; - this.valueCoder = valueCoder; - } - - public void clear() { - for (String key : data.getPropertyKeys()) { - Splittable.NULL.assign(data, key); - reified.setReified(key, null); - } - } - - public boolean containsKey(Object key) { - String encodedKey = encodedKey(key); - return !data.isUndefined(encodedKey) || reified.isReified(encodedKey); - } - - public boolean containsValue(Object value) { - return values().contains(value); - } - - public Set<java.util.Map.Entry<K, V>> entrySet() { - return new AbstractSet<Map.Entry<K, V>>() { - final List<String> keys = data.getPropertyKeys(); - - @Override - public Iterator<java.util.Map.Entry<K, V>> iterator() { - return new Iterator<Map.Entry<K, V>>() { - Iterator<String> keyIterator = keys.iterator(); - String encodedKey; - - public boolean hasNext() { - return keyIterator.hasNext(); - } - - public java.util.Map.Entry<K, V> next() { - encodedKey = keyIterator.next(); - return new Map.Entry<K, V>() { - @SuppressWarnings("unchecked") - final K key = (K) keyCoder.decode(state, StringQuoter.split(StringQuoter - .quote(encodedKey))); - @SuppressWarnings("unchecked") - final V value = (V) valueCoder.decode(state, data.get(encodedKey)); - - public K getKey() { - return key; - } - - public V getValue() { - return value; - } - - public V setValue(V newValue) { - return put(key, newValue); - } - }; - } - - public void remove() { - Splittable.NULL.assign(data, encodedKey); - reified.setReified(encodedKey, null); - } - }; - } - - @Override - public int size() { - return keys.size(); - } - }; - } - - public V get(Object key) { - String encodedKey = encodedKey(key); - return getRaw(encodedKey); - } - - public Splittable getSplittable() { - return data; - } - - public boolean isEmpty() { - return data.getPropertyKeys().isEmpty(); - } - - public Set<K> keySet() { - return new AbstractSet<K>() { - final List<String> keys = data.getPropertyKeys(); - - @Override - public Iterator<K> iterator() { - return new Iterator<K>() { - final Iterator<String> it = keys.iterator(); - String lastEncodedKey; - - public boolean hasNext() { - return it.hasNext(); - } - - public K next() { - lastEncodedKey = it.next(); - @SuppressWarnings("unchecked") - K toReturn = - (K) keyCoder.decode(state, StringQuoter.split(StringQuoter.quote(lastEncodedKey))); - return toReturn; - } - - public void remove() { - Splittable.NULL.assign(data, lastEncodedKey); - reified.setReified(lastEncodedKey, null); - } - }; - } - - @Override - public int size() { - return keys.size(); - } - }; - } - - public V put(K key, V value) { - V toReturn = get(key); - String encodedKey = encodedKey(key); - reified.setReified(encodedKey, value); - Splittable encodedValue = valueCoder.extractSplittable(state, value); - if (encodedValue == null) { - // External datastructure - reified.setReified(AbstractAutoBean.UNSPLITTABLE_VALUES_KEY, true); - } else { - encodedValue.assign(data, encodedKey); - } - return toReturn; - } - - public void putAll(Map<? extends K, ? extends V> m) { - for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) { - put(entry.getKey(), entry.getValue()); - } - } - - public V remove(Object key) { - V toReturn = get(key); - String encodedKey = encodedKey(key); - reified.setReified(encodedKey, null); - Splittable.NULL.assign(data, encodedKey); - return toReturn; - } - - public int size() { - return data.getPropertyKeys().size(); - } - - public Collection<V> values() { - return new AbstractCollection<V>() { - final List<String> keys = data.getPropertyKeys(); - - @Override - public Iterator<V> iterator() { - return new Iterator<V>() { - final Iterator<String> it = keys.iterator(); - String lastEncodedKey; - - public boolean hasNext() { - return it.hasNext(); - } - - public V next() { - lastEncodedKey = it.next(); - return getRaw(lastEncodedKey); - } - - public void remove() { - Splittable.NULL.assign(data, lastEncodedKey); - reified.setReified(lastEncodedKey, null); - } - }; - } - - @Override - public int size() { - return keys.size(); - } - }; - } - - private String encodedKey(Object key) { - return keyCoder.extractSplittable(state, key).asString(); - } - - private V getRaw(String encodedKey) { - if (reified.isReified(encodedKey)) { - @SuppressWarnings("unchecked") - V toReturn = (V) reified.getReified(encodedKey); - return toReturn; - } - // Both undefined or an explicit null should return null here - if (data.isNull(encodedKey)) { - return null; - } - Splittable value = data.get(encodedKey); - @SuppressWarnings("unchecked") - V toReturn = (V) valueCoder.decode(state, value); - reified.setReified(encodedKey, toReturn); - return toReturn; - } -}
diff --git a/user/src/com/google/gwt/autobean/shared/impl/StringQuoter.java b/user/src/com/google/gwt/autobean/shared/impl/StringQuoter.java index 559e2af..899ed52 100644 --- a/user/src/com/google/gwt/autobean/shared/impl/StringQuoter.java +++ b/user/src/com/google/gwt/autobean/shared/impl/StringQuoter.java
@@ -31,36 +31,12 @@ */ public class StringQuoter { private static final String ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSz"; - private static final DateFormat ISO8601 = new SimpleDateFormat(ISO8601_PATTERN, Locale - .getDefault()); + private static final DateFormat ISO8601 = new SimpleDateFormat( + ISO8601_PATTERN, Locale.getDefault()); private static final String RFC2822_PATTERN = "EEE, d MMM yyyy HH:mm:ss Z"; - private static final DateFormat RFC2822 = new SimpleDateFormat(RFC2822_PATTERN, Locale - .getDefault()); - - public static Splittable create(boolean value) { - return JsonSplittable.create(String.valueOf(value)); - } - - public static Splittable create(double value) { - return JsonSplittable.create(String.valueOf(value)); - } - - public static Splittable create(String value) { - return JsonSplittable.create(quote(value)); - } - - public static Splittable createIndexed() { - return JsonSplittable.createIndexed(); - } - - public static Splittable createSplittable() { - return JsonSplittable.create(); - } - - public static Splittable nullValue() { - return JsonSplittable.createNull(); - } + private static final DateFormat RFC2822 = new SimpleDateFormat( + RFC2822_PATTERN, Locale.getDefault()); /** * Create a quoted JSON string.
diff --git a/user/src/com/google/gwt/core/client/JsonUtils.java b/user/src/com/google/gwt/core/client/JsonUtils.java index 7088266..e44bb35 100644 --- a/user/src/com/google/gwt/core/client/JsonUtils.java +++ b/user/src/com/google/gwt/core/client/JsonUtils.java
@@ -60,17 +60,17 @@ try { return JSON.parse(json); } catch (e) { - return @com.google.gwt.core.client.JsonUtils::throwIllegalArgumentException(*)("Error parsing JSON: " + e, json); + return @com.google.gwt.core.client.JsonUtils::throwIllegalArgumentException(Ljava/lang/String;)("Error parsing JSON: " + e); } } else { if (!@com.google.gwt.core.client.JsonUtils::safeToEval(Ljava/lang/String;)(json)) { - return @com.google.gwt.core.client.JsonUtils::throwIllegalArgumentException(*)("Illegal character in JSON string", json); + return @com.google.gwt.core.client.JsonUtils::throwIllegalArgumentException(Ljava/lang/String;)("Illegal character in JSON string"); } json = @com.google.gwt.core.client.JsonUtils::escapeJsonForEval(Ljava/lang/String;)(json); try { return eval('(' + json + ')'); } catch (e) { - return @com.google.gwt.core.client.JsonUtils::throwIllegalArgumentException(*)("Error parsing JSON: " + e, json); + return @com.google.gwt.core.client.JsonUtils::throwIllegalArgumentException(Ljava/lang/String;)("Error parsing JSON: " + e); } } }-*/; @@ -110,12 +110,12 @@ try { return eval('(' + escaped + ')'); } catch (e) { - return @com.google.gwt.core.client.JsonUtils::throwIllegalArgumentException(*)("Error parsing JSON: " + e, json); + return @com.google.gwt.core.client.JsonUtils::throwIllegalArgumentException(Ljava/lang/String;)("Error parsing JSON: " + e); } }-*/; - static void throwIllegalArgumentException(String message, String data) { - throw new IllegalArgumentException(message + "\n" + data); + static void throwIllegalArgumentException(String message) { + throw new IllegalArgumentException(message); } private static native String escapeChar(String c) /*-{
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestContext.java b/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestContext.java index 429dd6f..6487320 100644 --- a/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestContext.java +++ b/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestContext.java
@@ -26,8 +26,8 @@ import com.google.gwt.autobean.shared.AutoBeanVisitor; import com.google.gwt.autobean.shared.Splittable; import com.google.gwt.autobean.shared.ValueCodex; -import com.google.gwt.autobean.shared.impl.AbstractAutoBean; import com.google.gwt.autobean.shared.impl.EnumMap; +import com.google.gwt.autobean.shared.impl.LazySplittable; import com.google.gwt.autobean.shared.impl.StringQuoter; import com.google.gwt.event.shared.UmbrellaException; import com.google.web.bindery.requestfactory.shared.BaseProxy; @@ -36,13 +36,13 @@ import com.google.web.bindery.requestfactory.shared.EntityProxyId; import com.google.web.bindery.requestfactory.shared.Receiver; import com.google.web.bindery.requestfactory.shared.RequestContext; -import com.google.web.bindery.requestfactory.shared.RequestTransport.TransportReceiver; import com.google.web.bindery.requestfactory.shared.ServerFailure; +import com.google.web.bindery.requestfactory.shared.ValueProxy; import com.google.web.bindery.requestfactory.shared.Violation; import com.google.web.bindery.requestfactory.shared.WriteOperation; +import com.google.web.bindery.requestfactory.shared.RequestTransport.TransportReceiver; import com.google.web.bindery.requestfactory.shared.impl.posers.DatePoser; import com.google.web.bindery.requestfactory.shared.messages.IdMessage; -import com.google.web.bindery.requestfactory.shared.messages.IdMessage.Strength; import com.google.web.bindery.requestfactory.shared.messages.InvocationMessage; import com.google.web.bindery.requestfactory.shared.messages.JsonRpcRequest; import com.google.web.bindery.requestfactory.shared.messages.MessageFactory; @@ -51,6 +51,7 @@ import com.google.web.bindery.requestfactory.shared.messages.ResponseMessage; import com.google.web.bindery.requestfactory.shared.messages.ServerFailureMessage; import com.google.web.bindery.requestfactory.shared.messages.ViolationMessage; +import com.google.web.bindery.requestfactory.shared.messages.IdMessage.Strength; import java.util.ArrayList; import java.util.Collection; @@ -66,7 +67,8 @@ /** * Base implementations for RequestContext services. */ -public abstract class AbstractRequestContext implements RequestContext, EntityCodex.EntitySource { +public abstract class AbstractRequestContext implements RequestContext, + EntityCodex.EntitySource { /** * Allows the payload dialect to be injected into the AbstractRequestContext * without the caller needing to be concerned with how the implementation @@ -108,7 +110,8 @@ * DialectImpl interface and restored to to AbstractRequestContext. */ if (!invocations.isEmpty()) { - throw new RuntimeException("Only one invocation per request, pending backend support"); + throw new RuntimeException( + "Only one invocation per request, pending backend support"); } invocations.add(request); for (Object arg : request.getRequestData().getOrderedParameters()) { @@ -149,23 +152,20 @@ if (!raw.isNull("error")) { Splittable error = raw.get("error"); - ServerFailure failure = - new ServerFailure(error.get("message").asString(), error.get("code").asString(), - payload, true); + ServerFailure failure = new ServerFailure( + error.get("message").asString(), error.get("code").asString(), + payload, true); fail(receiver, failure); return; } Splittable result = raw.get("result"); @SuppressWarnings("unchecked") - Class<BaseProxy> target = - (Class<BaseProxy>) invocations.get(0).getRequestData().getReturnType(); + Class<BaseProxy> target = (Class<BaseProxy>) invocations.get(0).getRequestData().getReturnType(); SimpleProxyId<BaseProxy> id = getRequestFactory().allocateId(target); AutoBean<BaseProxy> bean = createProxy(target, id); - // XXX expose this as a proper API - ((AbstractAutoBean<?>) bean).setData(result); - // AutoBeanCodex.decodeInto(result, bean); + AutoBeanCodex.decodeInto(result, bean); if (callback != null) { callback.onSuccess(bean.as()); @@ -178,8 +178,9 @@ Splittable encode(Object obj) { Splittable value; if (obj == null) { - return Splittable.NULL; - } else if (obj.getClass().isEnum() && getAutoBeanFactory() instanceof EnumMap) { + return LazySplittable.NULL; + } else if (obj.getClass().isEnum() + && getAutoBeanFactory() instanceof EnumMap) { value = ValueCodex.encode(((EnumMap) getAutoBeanFactory()).getToken((Enum<?>) obj)); } else if (ValueCodex.canDecode(obj.getClass())) { value = ValueCodex.encode(obj); @@ -232,13 +233,13 @@ } public void processPayload(final Receiver<Void> receiver, String payload) { - ResponseMessage response = - AutoBeanCodex.decode(MessageFactoryHolder.FACTORY, ResponseMessage.class, payload).as(); + ResponseMessage response = AutoBeanCodex.decode( + MessageFactoryHolder.FACTORY, ResponseMessage.class, payload).as(); if (response.getGeneralFailure() != null) { ServerFailureMessage failure = response.getGeneralFailure(); - ServerFailure fail = - new ServerFailure(failure.getMessage(), failure.getExceptionType(), failure - .getStackTrace(), failure.isFatal()); + ServerFailure fail = new ServerFailure(failure.getMessage(), + failure.getExceptionType(), failure.getStackTrace(), + failure.isFatal()); fail(receiver, fail); return; @@ -265,12 +266,13 @@ if (response.getStatusCodes().get(i)) { invocations.get(i).onSuccess(response.getInvocationResults().get(i)); } else { - ServerFailureMessage failure = - AutoBeanCodex.decode(MessageFactoryHolder.FACTORY, ServerFailureMessage.class, - response.getInvocationResults().get(i)).as(); + ServerFailureMessage failure = AutoBeanCodex.decode( + MessageFactoryHolder.FACTORY, ServerFailureMessage.class, + response.getInvocationResults().get(i)).as(); invocations.get(i).onFail( - new ServerFailure(failure.getMessage(), failure.getExceptionType(), failure - .getStackTrace(), failure.isFatal())); + new ServerFailure(failure.getMessage(), + failure.getExceptionType(), failure.getStackTrace(), + failure.isFatal())); } } catch (Throwable t) { if (causes == null) { @@ -366,14 +368,12 @@ * Objects are placed into this map by being passed into {@link #edit} or as * an invocation argument. */ - private final Map<SimpleProxyId<?>, AutoBean<? extends BaseProxy>> editedProxies = - new LinkedHashMap<SimpleProxyId<?>, AutoBean<? extends BaseProxy>>(); + private final Map<SimpleProxyId<?>, AutoBean<? extends BaseProxy>> editedProxies = new LinkedHashMap<SimpleProxyId<?>, AutoBean<? extends BaseProxy>>(); /** * A map that contains the canonical instance of an entity to return in the * return graph, since this is built from scratch. */ - private final Map<SimpleProxyId<?>, AutoBean<?>> returnedProxies = - new HashMap<SimpleProxyId<?>, AutoBean<?>>(); + private final Map<SimpleProxyId<?>, AutoBean<?>> returnedProxies = new HashMap<SimpleProxyId<?>, AutoBean<?>>(); /** * A map that allows us to handle the case where the server has sent back an @@ -381,12 +381,12 @@ * client will need to swap out the request-local ids with a regular * client-allocated id. */ - private final Map<Integer, SimpleProxyId<?>> syntheticIds = - new HashMap<Integer, SimpleProxyId<?>>(); + private final Map<Integer, SimpleProxyId<?>> syntheticIds = new HashMap<Integer, SimpleProxyId<?>>(); private final DialectImpl dialect; - protected AbstractRequestContext(AbstractRequestFactory factory, Dialect dialect) { + protected AbstractRequestContext(AbstractRequestFactory factory, + Dialect dialect) { this.requestFactory = factory; this.dialect = dialect.create(this); } @@ -405,10 +405,12 @@ /** * Creates a new proxy with an assigned ID. */ - public <T extends BaseProxy> AutoBean<T> createProxy(Class<T> clazz, SimpleProxyId<T> id) { + public <T extends BaseProxy> AutoBean<T> createProxy(Class<T> clazz, + SimpleProxyId<T> id) { AutoBean<T> created = getAutoBeanFactory().create(clazz); if (created == null) { - throw new IllegalArgumentException("Unknown proxy type " + clazz.getName()); + throw new IllegalArgumentException("Unknown proxy type " + + clazz.getName()); } created.setTag(STABLE_ID, id); return created; @@ -479,9 +481,10 @@ /** * EntityCodex support. */ - public <Q extends BaseProxy> AutoBean<Q> getBeanForPayload(Splittable serializedProxyId) { - IdMessage ref = - AutoBeanCodex.decode(MessageFactoryHolder.FACTORY, IdMessage.class, serializedProxyId).as(); + public <Q extends BaseProxy> AutoBean<Q> getBeanForPayload( + Splittable serializedProxyId) { + IdMessage ref = AutoBeanCodex.decode(MessageFactoryHolder.FACTORY, + IdMessage.class, serializedProxyId).as(); @SuppressWarnings("unchecked") SimpleProxyId<Q> id = (SimpleProxyId<Q>) getId(ref); return getProxyForReturnPayloadGraph(id); @@ -563,7 +566,8 @@ protected void fail(Receiver<Void> receiver, ServerFailure failure) { reuse(); Set<Throwable> causes = null; - for (AbstractRequest<?> request : new ArrayList<AbstractRequest<?>>(invocations)) { + for (AbstractRequest<?> request : new ArrayList<AbstractRequest<?>>( + invocations)) { try { request.onFail(failure); } catch (Throwable t) { @@ -602,7 +606,8 @@ protected void violation(final Receiver<Void> receiver, Set<Violation> errors) { reuse(); Set<Throwable> causes = null; - for (AbstractRequest<?> request : new ArrayList<AbstractRequest<?>>(invocations)) { + for (AbstractRequest<?> request : new ArrayList<AbstractRequest<?>>( + invocations)) { try { request.onViolation(errors); } catch (Throwable t) { @@ -635,14 +640,16 @@ if (Strength.SYNTHETIC.equals(op.getStrength())) { return allocateSyntheticId(op.getTypeToken(), op.getSyntheticId()); } - return requestFactory.getId(op.getTypeToken(), op.getServerId(), op.getClientId()); + return requestFactory.getId(op.getTypeToken(), op.getServerId(), + op.getClientId()); } /** * Creates or retrieves a new canonical AutoBean to represent the given id in * the returned payload. */ - <Q extends BaseProxy> AutoBean<Q> getProxyForReturnPayloadGraph(SimpleProxyId<Q> id) { + <Q extends BaseProxy> AutoBean<Q> getProxyForReturnPayloadGraph( + SimpleProxyId<Q> id) { @SuppressWarnings("unchecked") AutoBean<Q> bean = (AutoBean<Q>) returnedProxies.get(id); if (bean == null) { @@ -658,8 +665,8 @@ * Create a single OperationMessage that encapsulates the state of a proxy * AutoBean. */ - AutoBean<OperationMessage> makeOperationMessage(SimpleProxyId<BaseProxy> stableId, - AutoBean<?> proxyBean, boolean useDelta) { + AutoBean<OperationMessage> makeOperationMessage( + SimpleProxyId<BaseProxy> stableId, AutoBean<?> proxyBean, boolean useDelta) { // The OperationMessages describes operations on exactly one entity AutoBean<OperationMessage> toReturn = MessageFactoryHolder.FACTORY.operation(); @@ -703,9 +710,8 @@ Map<String, Object> diff = Collections.emptyMap(); if (isEntityType(stableId.getProxyClass())) { // Compute what's changed on the client - diff = - useDelta ? AutoBeanUtils.diff(parent, proxyBean) : AutoBeanUtils - .getAllProperties(proxyBean); + diff = useDelta ? AutoBeanUtils.diff(parent, proxyBean) + : AutoBeanUtils.getAllProperties(proxyBean); } else if (isValueType(stableId.getProxyClass())) { // Send everything diff = AutoBeanUtils.getAllProperties(proxyBean); @@ -714,7 +720,8 @@ if (!diff.isEmpty()) { Map<String, Splittable> propertyMap = new HashMap<String, Splittable>(); for (Map.Entry<String, Object> entry : diff.entrySet()) { - propertyMap.put(entry.getKey(), EntityCodex.encode(this, entry.getValue())); + propertyMap.put(entry.getKey(), + EntityCodex.encode(this, entry.getValue())); } operation.setPropertyMap(propertyMap); } @@ -728,8 +735,8 @@ * @param returnRecord the JSON map containing property/value pairs * @param operations the WriteOperation eventns to broadcast over the EventBus */ - <Q extends BaseProxy> Q processReturnOperation(SimpleProxyId<Q> id, OperationMessage op, - WriteOperation... operations) { + <Q extends BaseProxy> Q processReturnOperation(SimpleProxyId<Q> id, + OperationMessage op, WriteOperation... operations) { AutoBean<Q> toMutate = getProxyForReturnPayloadGraph(id); toMutate.setTag(Constants.VERSION_PROPERTY_B64, op.getVersion()); @@ -739,16 +746,15 @@ // Apply updates toMutate.accept(new AutoBeanVisitor() { @Override - public boolean visitReferenceProperty(String propertyName, AutoBean<?> value, - PropertyContext ctx) { + public boolean visitReferenceProperty(String propertyName, + AutoBean<?> value, PropertyContext ctx) { if (ctx.canSet()) { if (properties.containsKey(propertyName)) { Splittable raw = properties.get(propertyName); - Class<?> elementType = - ctx instanceof CollectionPropertyContext ? ((CollectionPropertyContext) ctx) - .getElementType() : null; - Object decoded = - EntityCodex.decode(AbstractRequestContext.this, ctx.getType(), elementType, raw); + Class<?> elementType = ctx instanceof CollectionPropertyContext + ? ((CollectionPropertyContext) ctx).getElementType() : null; + Object decoded = EntityCodex.decode(AbstractRequestContext.this, + ctx.getType(), elementType, raw); ctx.set(decoded); } } @@ -756,7 +762,8 @@ } @Override - public boolean visitValueProperty(String propertyName, Object value, PropertyContext ctx) { + public boolean visitValueProperty(String propertyName, Object value, + PropertyContext ctx) { if (ctx.canSet()) { if (properties.containsKey(propertyName)) { Splittable raw = properties.get(propertyName); @@ -792,8 +799,8 @@ continue; } requestFactory.getEventBus().fireEventFromSource( - new EntityProxyChange<EntityProxy>((EntityProxy) proxy, writeOperation), - id.getProxyClass()); + new EntityProxyChange<EntityProxy>((EntityProxy) proxy, + writeOperation), id.getProxyClass()); } } return proxy; @@ -804,8 +811,8 @@ * * @see #syntheticIds */ - private <Q extends BaseProxy> SimpleProxyId<Q> allocateSyntheticId(String typeToken, - int syntheticId) { + private <Q extends BaseProxy> SimpleProxyId<Q> allocateSyntheticId( + String typeToken, int syntheticId) { @SuppressWarnings("unchecked") SimpleProxyId<Q> toReturn = (SimpleProxyId<Q>) syntheticIds.get(syntheticId); if (toReturn == null) { @@ -854,25 +861,19 @@ * Shallow-clones an autobean and makes duplicates of the collection types. A * regular {@link AutoBean#clone} won't duplicate reference properties. */ - private <T extends BaseProxy> AutoBean<T> cloneBeanAndCollections(final AutoBean<T> toClone) { - AutoBean<T> clone = toClone.getFactory().create(toClone.getType()); - clone.setTag(STABLE_ID, toClone.getTag(STABLE_ID)); - clone.setTag(Constants.VERSION_PROPERTY_B64, toClone.getTag(Constants.VERSION_PROPERTY_B64)); + private <T extends BaseProxy> AutoBean<T> cloneBeanAndCollections( + AutoBean<T> toClone) { + AutoBean<T> clone = toClone.clone(false); /* * Take ownership here to prevent cycles in value objects from overflowing * the stack. */ takeOwnership(clone); clone.accept(new AutoBeanVisitor() { - final Map<String, Object> values = AutoBeanUtils.getAllProperties(toClone); @Override - public boolean visitCollectionProperty(String propertyName, AutoBean<Collection<?>> value, - CollectionPropertyContext ctx) { - // javac generics bug - value = - AutoBeanUtils.<Collection<?>, Collection<?>> getAutoBean((Collection<?>) values - .get(propertyName)); + public boolean visitCollectionProperty(String propertyName, + AutoBean<Collection<?>> value, CollectionPropertyContext ctx) { if (value != null) { Collection<Object> collection; if (List.class == ctx.getType()) { @@ -884,20 +885,20 @@ throw new IllegalArgumentException(ctx.getType().getName()); } - if (isValueType(ctx.getElementType()) || isEntityType(ctx.getElementType())) { + if (isValueType(ctx.getElementType())) { /* - * Proxies must be edited up-front so that the elements in the - * collection have stable identity. + * Value proxies must be cloned upfront, since the value is replaced + * outright. */ for (Object o : value.as()) { if (o == null) { collection.add(null); } else { - collection.add(editProxy((BaseProxy) o)); + collection.add(editProxy((ValueProxy) o)); } } } else { - // For simple values, just copy the values + // For entities and simple values, just alias the values collection.addAll(value.as()); } @@ -907,30 +908,21 @@ } @Override - public boolean visitReferenceProperty(String propertyName, AutoBean<?> value, - PropertyContext ctx) { - value = AutoBeanUtils.getAutoBean(values.get(propertyName)); + public boolean visitReferenceProperty(String propertyName, + AutoBean<?> value, PropertyContext ctx) { if (value != null) { - if (isValueType(ctx.getType()) || isEntityType(ctx.getType())) { + if (isValueType(ctx.getType())) { /* * Value proxies must be cloned upfront, since the value is replaced * outright. */ @SuppressWarnings("unchecked") - AutoBean<BaseProxy> valueBean = (AutoBean<BaseProxy>) value; + AutoBean<ValueProxy> valueBean = (AutoBean<ValueProxy>) value; ctx.set(editProxy(valueBean.as())); - } else { - ctx.set(value.as()); } } return false; } - - @Override - public boolean visitValueProperty(String propertyName, Object value, PropertyContext ctx) { - ctx.set(values.get(propertyName)); - return false; - } }); return clone; } @@ -996,7 +988,8 @@ } // Parameter values or references - List<Splittable> parameters = new ArrayList<Splittable>(data.getOrderedParameters().length); + List<Splittable> parameters = new ArrayList<Splittable>( + data.getOrderedParameters().length); for (Object param : data.getOrderedParameters()) { parameters.add(EntityCodex.encode(this, param)); } @@ -1015,8 +1008,8 @@ private List<OperationMessage> makePayloadOperations() { List<OperationMessage> operations = new ArrayList<OperationMessage>(); for (AutoBean<? extends BaseProxy> currentView : editedProxies.values()) { - OperationMessage operation = - makeOperationMessage(BaseProxyCategory.stableId(currentView), currentView, true).as(); + OperationMessage operation = makeOperationMessage( + BaseProxyCategory.stableId(currentView), currentView, true).as(); operations.add(operation); } return operations; @@ -1087,7 +1080,7 @@ */ private <T extends BaseProxy> T takeOwnership(AutoBean<T> bean) { editedProxies.put(stableId(bean), bean); - bean.setTag(REQUEST_CONTEXT, this); + bean.setTag(REQUEST_CONTEXT, AbstractRequestContext.this); return bean.as(); } }
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/impl/EntityCodex.java b/user/src/com/google/web/bindery/requestfactory/shared/impl/EntityCodex.java index 8aca80f..5566589 100644 --- a/user/src/com/google/web/bindery/requestfactory/shared/impl/EntityCodex.java +++ b/user/src/com/google/web/bindery/requestfactory/shared/impl/EntityCodex.java
@@ -19,6 +19,7 @@ import com.google.gwt.autobean.shared.AutoBeanUtils; import com.google.gwt.autobean.shared.Splittable; import com.google.gwt.autobean.shared.ValueCodex; +import com.google.gwt.autobean.shared.impl.LazySplittable; import com.google.gwt.autobean.shared.impl.StringQuoter; import com.google.web.bindery.requestfactory.shared.BaseProxy; import com.google.web.bindery.requestfactory.shared.EntityProxyId; @@ -41,7 +42,8 @@ * Expects an encoded * {@link com.google.web.bindery.requestfactory.shared.messages.IdMessage}. */ - <Q extends BaseProxy> AutoBean<Q> getBeanForPayload(Splittable serializedIdMessage); + <Q extends BaseProxy> AutoBean<Q> getBeanForPayload( + Splittable serializedIdMessage); /** * Should return an encoded @@ -57,9 +59,9 @@ /** * Collection support is limited to value types and resolving ids. */ - public static Object decode(EntitySource source, Class<?> type, Class<?> elementType, - Splittable split) { - if (split == null || split == Splittable.NULL) { + public static Object decode(EntitySource source, Class<?> type, + Class<?> elementType, Splittable split) { + if (split == null || split == LazySplittable.NULL) { return null; } @@ -97,7 +99,8 @@ return collection; } - if (source.isEntityType(type) || source.isValueType(type) || EntityProxyId.class.equals(type)) { + if (source.isEntityType(type) || source.isValueType(type) + || EntityProxyId.class.equals(type)) { return source.getBeanForPayload(split).as(); } @@ -108,8 +111,8 @@ /** * Collection support is limited to value types and resolving ids. */ - public static Object decode(EntitySource source, Class<?> type, Class<?> elementType, - String jsonPayload) { + public static Object decode(EntitySource source, Class<?> type, + Class<?> elementType, String jsonPayload) { Splittable split = StringQuoter.split(jsonPayload); return decode(source, type, elementType, split); } @@ -119,7 +122,7 @@ */ public static Splittable encode(EntitySource source, Object value) { if (value == null) { - return Splittable.NULL; + return LazySplittable.NULL; } if (value instanceof Poser<?>) { @@ -143,7 +146,7 @@ } } toReturn.append(']'); - return StringQuoter.split(toReturn.toString()); + return new LazySplittable(toReturn.toString()); } if (value instanceof BaseProxy) {
diff --git a/user/super/com/google/gwt/autobean/super/com/google/gwt/autobean/shared/impl/StringQuoter.java b/user/super/com/google/gwt/autobean/super/com/google/gwt/autobean/shared/impl/StringQuoter.java index bfd0300..cb331b6 100644 --- a/user/super/com/google/gwt/autobean/super/com/google/gwt/autobean/shared/impl/StringQuoter.java +++ b/user/super/com/google/gwt/autobean/super/com/google/gwt/autobean/shared/impl/StringQuoter.java
@@ -17,8 +17,6 @@ import com.google.gwt.autobean.client.impl.JsoSplittable; import com.google.gwt.autobean.shared.Splittable; -import com.google.gwt.core.client.GWT; -import com.google.gwt.core.client.GwtScriptOnly; import com.google.gwt.core.client.JavaScriptException; import com.google.gwt.core.client.JsDate; import com.google.gwt.core.client.JsonUtils; @@ -29,44 +27,18 @@ /** * This a super-source version with a client-only implementation. */ -@GwtScriptOnly public class StringQuoter { - public static Splittable create(boolean value) { - return JsoSplittable.create(value); - } - - public static Splittable create(double value) { - return JsoSplittable.create(value); - } - - public static Splittable create(String value) { - return JsoSplittable.create(value); - } - - public static Splittable createIndexed() { - return JsoSplittable.createIndexed(); - } - - public static Splittable createSplittable() { - return JsoSplittable.create(); - } - - public static Splittable nullValue() { - return JsoSplittable.nullValue(); - } - public static String quote(String raw) { return JsonUtils.escapeValue(raw); } public static Splittable split(String payload) { - char c = payload.charAt(0); - boolean isSimple = c != '{' && c != '['; - if (isSimple) { + boolean isString = payload.charAt(0) == '\"'; + if (isString) { payload = "[" + payload + "]"; } - Splittable toReturn = JsonUtils.safeEval(payload).<JsoSplittable> cast(); - if (isSimple) { + Splittable toReturn = JsoSplittable.create(JsonUtils.safeEval(payload)); + if (isString) { toReturn = toReturn.get(0); } return toReturn;
diff --git a/user/test/com/google/gwt/autobean/AutoBeanSuite.java b/user/test/com/google/gwt/autobean/AutoBeanSuite.java index f8f8d6e..491e91c 100644 --- a/user/test/com/google/gwt/autobean/AutoBeanSuite.java +++ b/user/test/com/google/gwt/autobean/AutoBeanSuite.java
@@ -18,9 +18,7 @@ import com.google.gwt.autobean.client.AutoBeanTest; import com.google.gwt.autobean.server.AutoBeanCodexJreTest; import com.google.gwt.autobean.server.AutoBeanJreTest; -import com.google.gwt.autobean.server.SplittableJreTest; import com.google.gwt.autobean.shared.AutoBeanCodexTest; -import com.google.gwt.autobean.shared.SplittableTest; import com.google.gwt.junit.tools.GWTTestSuite; import junit.framework.Test; @@ -36,8 +34,6 @@ suite.addTestSuite(AutoBeanCodexTest.class); suite.addTestSuite(AutoBeanJreTest.class); suite.addTestSuite(AutoBeanTest.class); - suite.addTestSuite(SplittableJreTest.class); - suite.addTestSuite(SplittableTest.class); return suite; } }
diff --git a/user/test/com/google/gwt/autobean/client/AutoBeanTest.java b/user/test/com/google/gwt/autobean/client/AutoBeanTest.java index e83e46d..cc61684 100644 --- a/user/test/com/google/gwt/autobean/client/AutoBeanTest.java +++ b/user/test/com/google/gwt/autobean/client/AutoBeanTest.java
@@ -263,6 +263,46 @@ assertEquals("Blah", more.as().getString()); } + public void testClone() { + AutoBean<Intf> a1 = factory.intf(); + + Intf i1 = a1.as(); + i1.setInt(42); + + AutoBean<Intf> a2 = a1.clone(false); + Intf i2 = a2.as(); + + // Copies have the same values + assertNotSame(i1, i2); + assertFalse(i1.equals(i2)); + assertEquals(i1.getInt(), i2.getInt()); + assertTrue(AutoBeanUtils.deepEquals(a1, a2)); + + // Cloned instances do not affect one another + i1.setInt(41); + assertEquals(41, i1.getInt()); + assertEquals(42, i2.getInt()); + } + + public void testCloneDeep() { + AutoBean<OtherIntf> a1 = factory.otherIntf(); + OtherIntf o1 = a1.as(); + + o1.setIntf(factory.intf().as()); + o1.getIntf().setInt(42); + + AutoBean<OtherIntf> a2 = a1.clone(true); + assertTrue(AutoBeanUtils.deepEquals(a1, a2)); + + OtherIntf o2 = a2.as(); + + assertNotSame(o1.getIntf(), o2.getIntf()); + assertEquals(o1.getIntf().getInt(), o2.getIntf().getInt()); + + o1.getIntf().setInt(41); + assertEquals(42, o2.getIntf().getInt()); + } + public void testDiff() { AutoBean<Intf> a1 = factory.intf(); AutoBean<Intf> a2 = factory.intf(); @@ -275,6 +315,24 @@ assertEquals(42, diff.get("int")); } + /** + * Tests that lists are in fact aliased between AutoBeans after a shallow + * clone. + */ + public void testDiffWithCloneAndListMutation() { + AutoBean<HasList> a1 = factory.hasList(); + List<Intf> list = new ArrayList<Intf>(); + list.add(factory.intf().as()); + a1.as().setList(list); + + AutoBean<HasList> a2 = a1.clone(false); + assertTrue(AutoBeanUtils.diff(a1, a2).isEmpty()); + + // Lists are aliased between AutoBeans + assertSame(a1.as().getList(), a2.as().getList()); + assertEquals(a1.as().getList(), a2.as().getList()); + } + public void testDiffWithListPropertyAssignment() { AutoBean<HasList> a1 = factory.hasList(); AutoBean<HasList> a2 = factory.hasList(); @@ -377,14 +435,14 @@ } @Override - public void endVisitCollectionProperty(String propertyName, AutoBean<Collection<?>> value, - CollectionPropertyContext ctx) { + public void endVisitCollectionProperty(String propertyName, + AutoBean<Collection<?>> value, CollectionPropertyContext ctx) { check(propertyName, ctx); } @Override - public void endVisitMapProperty(String propertyName, AutoBean<Map<?, ?>> value, - MapPropertyContext ctx) { + public void endVisitMapProperty(String propertyName, + AutoBean<Map<?, ?>> value, MapPropertyContext ctx) { check(propertyName, ctx); } @@ -400,12 +458,14 @@ } else if ("listOfMap".equals(propertyName)) { // List<Map<String, Intf>> assertEquals(List.class.getName() + "<" + Map.class.getName() + "<" - + String.class.getName() + "," + Intf.class.getName() + ">>", sb.toString()); + + String.class.getName() + "," + Intf.class.getName() + ">>", + sb.toString()); } else if ("map".equals(propertyName)) { // Map<Map<String, String>, List<List<Intf>>> assertEquals(Map.class.getName() + "<" + Map.class.getName() + "<" - + String.class.getName() + "," + String.class.getName() + ">," + List.class.getName() - + "<" + List.class.getName() + "<" + Intf.class.getName() + ">>>", sb.toString()); + + String.class.getName() + "," + String.class.getName() + ">," + + List.class.getName() + "<" + List.class.getName() + "<" + + Intf.class.getName() + ">>>", sb.toString()); } else { throw new RuntimeException(propertyName); } @@ -452,8 +512,8 @@ boolean seenOther; @Override - public void endVisitReferenceProperty(String propertyName, AutoBean<?> value, - PropertyContext ctx) { + public void endVisitReferenceProperty(String propertyName, + AutoBean<?> value, PropertyContext ctx) { if ("hasBoolean".equals(propertyName)) { assertSame(hasBoolean, value); assertEquals(HasBoolean.class, ctx.getType()); @@ -469,7 +529,8 @@ } @Override - public void endVisitValueProperty(String propertyName, Object value, PropertyContext ctx) { + public void endVisitValueProperty(String propertyName, Object value, + PropertyContext ctx) { if ("int".equals(propertyName)) { assertEquals(42, value); assertEquals(int.class, ctx.getType());
diff --git a/user/test/com/google/gwt/autobean/server/SplittableJreTest.java b/user/test/com/google/gwt/autobean/server/SplittableJreTest.java deleted file mode 100644 index af1c93a..0000000 --- a/user/test/com/google/gwt/autobean/server/SplittableJreTest.java +++ /dev/null
@@ -1,28 +0,0 @@ -/* - * Copyright 2011 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.autobean.server; - -import com.google.gwt.autobean.shared.SplittableTest; - -/** - * A JRE-only version of SplittableTest. - */ -public class SplittableJreTest extends SplittableTest { - @Override - public String getModuleName() { - return null; - } -}
diff --git a/user/test/com/google/gwt/autobean/shared/AutoBeanCodexTest.java b/user/test/com/google/gwt/autobean/shared/AutoBeanCodexTest.java index 43bfb35..8e686b8 100644 --- a/user/test/com/google/gwt/autobean/shared/AutoBeanCodexTest.java +++ b/user/test/com/google/gwt/autobean/shared/AutoBeanCodexTest.java
@@ -200,7 +200,7 @@ assertEquals(MyEnum.BAR, map.getEnum(MyEnum.class, "BAR")); assertEquals(MyEnum.BAZ, map.getEnum(MyEnum.class, "quux")); - List<MyEnum> arrayValue = Arrays.asList(MyEnum.FOO, MyEnum.BAR, null, MyEnum.BAZ); + List<MyEnum> arrayValue = Arrays.asList(MyEnum.FOO, MyEnum.BAR, MyEnum.BAZ); Map<MyEnum, Integer> mapValue = new HashMap<MyEnum, Integer>(); mapValue.put(MyEnum.FOO, 0); mapValue.put(MyEnum.BAR, 1); @@ -219,13 +219,6 @@ assertEquals(MyEnum.BAZ, decoded.as().getEnum()); assertEquals(arrayValue, decoded.as().getEnums()); assertEquals(mapValue, decoded.as().getMap()); - - assertEquals(MyEnum.BAZ, AutoBeanUtils.getAllProperties(bean).get("enum")); - bean.as().setEnum(null); - assertNull(bean.as().getEnum()); - assertNull(AutoBeanUtils.getAllProperties(bean).get("enum")); - decoded = checkEncode(bean); - assertNull(decoded.as().getEnum()); } /** @@ -236,13 +229,14 @@ EnumMap map = (EnumMap) f; assertEquals("FOO_LIST", map.getToken(EnumReachableThroughList.FOO_LIST)); assertEquals("FOO_KEY", map.getToken(EnumReachableThroughMapKey.FOO_KEY)); - assertEquals("FOO_VALUE", map.getToken(EnumReachableThroughMapValue.FOO_VALUE)); - assertEquals(EnumReachableThroughList.FOO_LIST, map.getEnum(EnumReachableThroughList.class, - "FOO_LIST")); - assertEquals(EnumReachableThroughMapKey.FOO_KEY, map.getEnum(EnumReachableThroughMapKey.class, - "FOO_KEY")); - assertEquals(EnumReachableThroughMapValue.FOO_VALUE, map.getEnum( - EnumReachableThroughMapValue.class, "FOO_VALUE")); + assertEquals("FOO_VALUE", + map.getToken(EnumReachableThroughMapValue.FOO_VALUE)); + assertEquals(EnumReachableThroughList.FOO_LIST, + map.getEnum(EnumReachableThroughList.class, "FOO_LIST")); + assertEquals(EnumReachableThroughMapKey.FOO_KEY, + map.getEnum(EnumReachableThroughMapKey.class, "FOO_KEY")); + assertEquals(EnumReachableThroughMapValue.FOO_VALUE, + map.getEnum(EnumReachableThroughMapValue.class, "FOO_VALUE")); } public void testMap() { @@ -273,7 +267,8 @@ } assertEquals(5, complex.size()); for (Map.Entry<Simple, Simple> entry : complex.entrySet()) { - assertEquals(entry.getKey().getString(), String.valueOf(entry.getValue().getInt())); + assertEquals(entry.getKey().getString(), + String.valueOf(entry.getValue().getInt())); } } @@ -287,8 +282,7 @@ Map<String, String> value = new HashMap<String, String>(); value.put("c", "d"); - Map<Map<String, String>, Map<String, String>> test = - new HashMap<Map<String, String>, Map<String, String>>(); + Map<Map<String, String>, Map<String, String>> test = new HashMap<Map<String, String>, Map<String, String>>(); test.put(key, value); AutoBean<HasMap> bean = f.hasMap(); @@ -322,8 +316,8 @@ AutoBean<HasSimple> decodedBean2 = checkEncode(bean2); assertNotNull(decodedBean2.as().getSimple()); - assertTrue(AutoBeanUtils.diff(bean, AutoBeanUtils.getAutoBean(decodedBean2.as().getSimple())) - .isEmpty()); + assertTrue(AutoBeanUtils.diff(bean, + AutoBeanUtils.getAutoBean(decodedBean2.as().getSimple())).isEmpty()); AutoBean<HasList> bean3 = f.hasList(); bean3.as().setIntList(Arrays.asList(1, 2, 3, null, 4, 5)); @@ -331,7 +325,8 @@ AutoBean<HasList> decodedBean3 = checkEncode(bean3); assertNotNull(decodedBean3.as().getIntList()); - assertEquals(Arrays.asList(1, 2, 3, null, 4, 5), decodedBean3.as().getIntList()); + assertEquals(Arrays.asList(1, 2, 3, null, 4, 5), + decodedBean3.as().getIntList()); assertNotNull(decodedBean3.as().getList()); assertEquals(1, decodedBean3.as().getList().size()); assertTrue(AutoBeanUtils.diff(bean, @@ -344,25 +339,30 @@ AutoBean<HasSplittable> bean = f.hasAutoBean(); bean.as().setSimple(AutoBeanCodex.encode(simple)); bean.as().setString(ValueCodex.encode("Hello ['\"] world")); - List<Splittable> testList = - Arrays.asList(AutoBeanCodex.encode(simple), null, AutoBeanCodex.encode(simple)); + List<Splittable> testList = Arrays.asList(AutoBeanCodex.encode(simple), + null, AutoBeanCodex.encode(simple)); bean.as().setSimpleList(testList); - Map<Splittable, Splittable> testMap = - Collections.singletonMap(ValueCodex.encode("12345"), ValueCodex.encode("5678")); + Map<Splittable, Splittable> testMap = Collections.singletonMap( + ValueCodex.encode("12345"), ValueCodex.encode("5678")); bean.as().setSplittableMap(testMap); AutoBean<HasSplittable> decoded = checkEncode(bean); Splittable toDecode = decoded.as().getSimple(); - AutoBean<Simple> decodedSimple = AutoBeanCodex.decode(f, Simple.class, toDecode); + AutoBean<Simple> decodedSimple = AutoBeanCodex.decode(f, Simple.class, + toDecode); assertEquals("Simple", decodedSimple.as().getString()); - assertEquals("Hello ['\"] world", ValueCodex.decode(String.class, decoded.as().getString())); - assertEquals("12345", decoded.as().getSplittableMap().keySet().iterator().next().asString()); - assertEquals("5678", decoded.as().getSplittableMap().values().iterator().next().asString()); + assertEquals("Hello ['\"] world", + ValueCodex.decode(String.class, decoded.as().getString())); + assertEquals("12345", + decoded.as().getSplittableMap().keySet().iterator().next().asString()); + assertEquals("5678", + decoded.as().getSplittableMap().values().iterator().next().asString()); List<Splittable> list = decoded.as().getSimpleList(); assertEquals(3, list.size()); assertNull(list.get(1)); - assertEquals("Simple", AutoBeanCodex.decode(f, Simple.class, list.get(2)).as().getString()); + assertEquals("Simple", + AutoBeanCodex.decode(f, Simple.class, list.get(2)).as().getString()); } @Override
diff --git a/user/test/com/google/gwt/autobean/shared/SplittableTest.java b/user/test/com/google/gwt/autobean/shared/SplittableTest.java deleted file mode 100644 index 17fd846..0000000 --- a/user/test/com/google/gwt/autobean/shared/SplittableTest.java +++ /dev/null
@@ -1,318 +0,0 @@ -/* - * Copyright 2011 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.autobean.shared; - -import com.google.gwt.autobean.client.impl.JsoSplittable; -import com.google.gwt.autobean.shared.impl.AutoBeanCodexImpl; -import com.google.gwt.autobean.shared.impl.AutoBeanCodexImpl.Coder; -import com.google.gwt.autobean.shared.impl.AutoBeanCodexImpl.EncodeState; -import com.google.gwt.autobean.shared.impl.SplittableList; -import com.google.gwt.autobean.shared.impl.SplittableSimpleMap; -import com.google.gwt.autobean.shared.impl.StringQuoter; -import com.google.gwt.core.client.GWT; -import com.google.gwt.junit.client.GWTTestCase; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Map; - -/** - * Tests for the underlying Splittable implementation. This test class is not - * indicative of code that users would write, it's simply doing spot-checks of - * functionality that AbstractAutoBean depends on. - */ -public class SplittableTest extends GWTTestCase { - - /** - * - */ - private static final EncodeState testState = EncodeState.forTesting(); - - @Override - public String getModuleName() { - return "com.google.gwt.autobean.AutoBean"; - } - - public void testBasicProperties() { - Splittable data = StringQuoter.split("{\"a\":true, \"b\":3, \"c\":\"string\", \"d\":null}"); - assertTrue("isBoolean", data.get("a").isBoolean()); - assertTrue("asBoolean", data.get("a").asBoolean()); - assertTrue("isNumber", data.get("b").isNumber()); - assertEquals(3.0, data.get("b").asNumber()); - assertTrue("isString", data.get("c").isString()); - assertEquals("string", data.get("c").asString()); - assertTrue("isNull", data.isNull("d")); - assertNull("should be null", data.get("d")); - } - - /** - * Splittables are implemented by a couple of different concrete types. We'll - * use this method to make sure that the correct implementation type is being - * used in various circumstances. - */ - public void testImplementationChoice() { - Splittable s = StringQuoter.split("[1,false,\"true\"]"); - if (GWT.isScript()) { - assertTrue("s should be JsoSplittable", s instanceof JsoSplittable); - assertTrue("s[0] should be JsoSplittable", s.get(0) instanceof JsoSplittable); - assertTrue("s[1] should be JsoSplittable", s.get(1) instanceof JsoSplittable); - assertTrue("s[2] should be JsoSplittable", s.get(2) instanceof JsoSplittable); - } else { - // Using the same types in both pure-JRE and DevMode to avoid JSNI - // overhead - assertTrue("s should be JsonSplittable", s.getClass().getName().endsWith("JsonSplittable")); - assertTrue("s[0] should be JsonSplittable", s.get(0).getClass().getName().endsWith( - "JsonSplittable")); - assertTrue("s[1] should be JsonSplittable", s.get(1).getClass().getName().endsWith( - "JsonSplittable")); - assertTrue("s[2] should be JsonSplittable", s.get(2).getClass().getName().endsWith( - "JsonSplittable")); - } - } - - public void testIndexed() { - Splittable s = StringQuoter.createIndexed(); - assertTrue(s.isIndexed()); - assertFalse(s.isKeyed()); - assertFalse(s.isString()); - assertEquals(0, s.size()); - - string("foo").assign(s, 0); - string("bar").assign(s, 1); - string("baz").assign(s, 2); - - assertEquals(3, s.size()); - assertEquals("[\"foo\",\"bar\",\"baz\"]", s.getPayload()); - - string("quux").assign(s, 1); - assertEquals("[\"foo\",\"quux\",\"baz\"]", s.getPayload()); - - Splittable s2 = s.deepCopy(); - assertNotSame(s, s2); - assertEquals(s.size(), s2.size()); - for (int i = 0, j = s.size(); i < j; i++) { - assertEquals(s.get(i).asString(), s2.get(i).asString()); - } - - s.setSize(2); - assertEquals(2, s.size()); - assertEquals("[\"foo\",\"quux\"]", s.getPayload()); - - // Make sure reified values aren't in the payload - Object o = new Object(); - s.setReified("reified", o); - assertFalse(s.getPayload().contains("reified")); - assertFalse(s.getPayload().contains("__s")); - assertSame(o, s.getReified("reified")); - } - - public void testKeyed() { - Splittable s = StringQuoter.createSplittable(); - assertFalse(s.isIndexed()); - assertTrue(s.isKeyed()); - assertFalse(s.isString()); - assertTrue(s.getPropertyKeys().isEmpty()); - - string("bar").assign(s, "foo"); - string("quux").assign(s, "baz"); - - // Actual iteration order is undefined - assertEquals(new HashSet<String>(Arrays.asList("foo", "baz")), new HashSet<String>(s - .getPropertyKeys())); - - assertFalse(s.isNull("foo")); - assertTrue(s.isNull("bar")); - - assertEquals("bar", s.get("foo").asString()); - assertEquals("quux", s.get("baz").asString()); - - String payload = s.getPayload(); - assertTrue(payload.startsWith("{")); - assertTrue(payload.endsWith("}")); - assertTrue(payload.contains("\"foo\":\"bar\"")); - assertTrue(payload.contains("\"baz\":\"quux\"")); - - Splittable s2 = s.deepCopy(); - assertNotSame(s, s2); - assertEquals("bar", s2.get("foo").asString()); - assertEquals("quux", s2.get("baz").asString()); - - // Make sure reified values aren't in the payload - Object o = new Object(); - s.setReified("reified", o); - assertFalse("Should not see reified in " + s.getPayload(), s.getPayload().contains("reified")); - assertFalse("Should not see __s in " + s.getPayload(), s.getPayload().contains("__s")); - assertSame(o, s.getReified("reified")); - } - - public void testNested() { - Splittable s = StringQuoter.split("{\"a\":{\"foo\":\"bar\"}}"); - Splittable a = s.get("a"); - assertNotNull(a); - assertEquals("bar", a.get("foo").asString()); - assertSame(a, s.get("a")); - assertEquals(a, s.get("a")); - } - - /** - * Tests attributes of the {@link Splittable#NULL} field. - */ - public void testNull() { - Splittable n = Splittable.NULL; - if (GWT.isScript()) { - assertNull(n); - } else { - assertNotNull(n); - } - assertFalse("boolean", n.isBoolean()); - assertFalse("indexed", n.isIndexed()); - assertFalse("keyed", n.isKeyed()); - assertFalse("string", n.isString()); - assertEquals("null", n.getPayload()); - } - - /** - * Extra tests in here due to potential to confuse {@code false} and - * {@code null} values. - */ - public void testSplittableListBoolean() { - Coder boolCoder = AutoBeanCodexImpl.valueCoder(Boolean.class); - Splittable s = StringQuoter.createIndexed(); - bool(false).assign(s, 0); - assertFalse("0 should not be null", s.isNull(0)); - assertTrue("s[0] should be a boolean", s.get(0).isBoolean()); - assertFalse("s[0] should be false", s.get(0).asBoolean()); - assertNotNull("Null decode", ValueCodex.decode(Boolean.class, s.get(0))); - Object decodedBoolean = boolCoder.decode(testState, s.get(0)); - assertNotNull("decode should not return null", decodedBoolean); - assertFalse("decoded value should be false", (Boolean) decodedBoolean); - - bool(true).assign(s, 1); - assertTrue("s[1] should be a boolean", s.get(1).isBoolean()); - assertTrue("s[1] should be true", s.get(1).asBoolean()); - assertTrue("boolCoder 1", (Boolean) boolCoder.decode(testState, s.get(1))); - - Splittable.NULL.assign(s, 2); - assertTrue("3 should be null", s.isNull(3)); - assertEquals("payload", "[false,true,null]", s.getPayload()); - List<Boolean> boolList = new SplittableList<Boolean>(s, boolCoder, testState); - assertEquals("boolList", Arrays.<Boolean> asList(false, true, null), boolList); - } - - /** - * Extra tests in here due to potential to confuse 0 and {@code null} values. - */ - public void testSplittableListNumbers() { - Coder intCoder = AutoBeanCodexImpl.valueCoder(Integer.class); - Coder doubleCoder = AutoBeanCodexImpl.valueCoder(Double.class); - Splittable s = StringQuoter.createIndexed(); - number(0).assign(s, 0); - assertFalse("0 should not be null", s.isNull(0)); - assertTrue("s[0] should be a number", s.get(0).isNumber()); - assertNotNull("Null decode", ValueCodex.decode(Integer.class, s.get(0))); - Object decodedInt = intCoder.decode(testState, s.get(0)); - assertNotNull("decode should not return null", decodedInt); - assertEquals("intCoder 0", Integer.valueOf(0), decodedInt); - assertEquals("doubleCoder 0", Double.valueOf(0), doubleCoder.decode(testState, s.get(0))); - - number(3.141592).assign(s, 1); - assertEquals("intCoder 1", Integer.valueOf(3), intCoder.decode(testState, s.get(1))); - assertEquals("doubleCoder 1", Double.valueOf(3.141592), doubleCoder.decode(testState, s.get(1))); - - number(42).assign(s, 2); - Splittable.NULL.assign(s, 3); - assertTrue("3 should be null", s.isNull(3)); - assertEquals("payload", "[0,3.141592,42,null]", s.getPayload()); - List<Double> doubleList = new SplittableList<Double>(s, doubleCoder, testState); - assertEquals(Double.valueOf(0), doubleList.get(0)); - assertEquals("doubleList", Arrays.<Double> asList(0d, 3.141592, 42d, null), doubleList); - - // Don't share backing data between lists - s = StringQuoter.split("[0,3.141592,42,null]"); - List<Integer> intList = new SplittableList<Integer>(s, intCoder, testState); - assertEquals("intList", Arrays.<Integer> asList(0, 3, 42, null), intList); - } - - public void testSplittableListString() { - Splittable data = StringQuoter.split("[\"Hello\",\"World\"]"); - SplittableList<String> list = - new SplittableList<String>(data, AutoBeanCodexImpl.valueCoder(String.class), testState); - assertEquals(2, list.size()); - assertEquals(Arrays.asList("Hello", "World"), list); - list.set(0, "Goodbye"); - assertEquals(Arrays.asList("Goodbye", "World"), list); - assertEquals("[\"Goodbye\",\"World\"]", data.getPayload()); - list.remove(0); - assertEquals(Arrays.asList("World"), list); - assertEquals("[\"World\"]", data.getPayload()); - list.add("Wide"); - list.add("Web"); - assertEquals(Arrays.asList("World", "Wide", "Web"), list); - assertEquals("[\"World\",\"Wide\",\"Web\"]", data.getPayload()); - } - - public void testSplittableMapStringString() { - Splittable data = StringQuoter.split("{\"foo\":\"bar\",\"baz\":\"quux\",\"isNull\":null}"); - assertTrue("isNull should be null", data.isNull("isNull")); - assertFalse("isNull should not be undefined", data.isUndefined("isNull")); - Map<String, String> map = - new SplittableSimpleMap<String, String>(data, AutoBeanCodexImpl.valueCoder(String.class), - AutoBeanCodexImpl.valueCoder(String.class), testState); - assertEquals(3, map.size()); - assertEquals("bar", map.get("foo")); - assertEquals("quux", map.get("baz")); - assertTrue("Map should have isNull key", map.containsKey("isNull")); - assertNull(map.get("isNull")); - assertFalse("Map should not have unknown key", map.containsKey("unknown")); - - map.put("bar", "foo2"); - assertEquals("foo2", map.get("bar")); - String payload = data.getPayload(); - assertTrue(payload.contains("\"bar\":\"foo2\"")); - assertTrue(payload.contains("\"isNull\":null")); - } - - public void testString() { - Splittable s = string("Hello '\" World!"); - assertFalse(s.isIndexed()); - assertFalse(s.isKeyed()); - assertTrue(s.isString()); - assertEquals("Hello '\" World!", s.asString()); - assertEquals("\"Hello '\\\" World!\"", s.getPayload()); - } - - public void testStringEmpty() { - Splittable s = string(""); - assertFalse(s.isIndexed()); - assertFalse(s.isKeyed()); - assertTrue(s.isString()); - assertEquals("", s.asString()); - assertEquals("\"\"", s.getPayload()); - } - - private Splittable bool(boolean value) { - return StringQuoter.split(String.valueOf(value)); - } - - private Splittable number(double number) { - return StringQuoter.split(String.valueOf(number)); - } - - private Splittable string(String value) { - return StringQuoter.split(StringQuoter.quote(value)); - } -}