Revision: 9215
Author: [email protected]
Date: Wed Nov 10 11:04:47 2010
Log: Improve support for using AutoBeans as a general-purpose JSON payload
consumer.
Add support for boolean isFoo() methods.
Copy code hygene from RequestFactory and always call ensureBaseType() when
generating references to class literals.
Add AutoBean.getFactory().
Ensure AutoBean types referenced only via List or Map parameterizations are
creatable.
Use string constants for encoding enums and allow control over the field
token used.
Patch by: bobv
Review by: rchandia, jasonhall
Suggested by: jasonhall
Review at http://gwt-code-reviews.appspot.com/1096801
http://code.google.com/p/google-web-toolkit/source/detail?r=9215
Added:
/trunk/user/src/com/google/gwt/autobean/shared/impl/EnumMap.java
Modified:
/trunk/user/src/com/google/gwt/autobean/client/impl/AbstractAutoBeanFactory.java
/trunk/user/src/com/google/gwt/autobean/rebind/AutoBeanFactoryGenerator.java
/trunk/user/src/com/google/gwt/autobean/rebind/model/AutoBeanFactoryModel.java
/trunk/user/src/com/google/gwt/autobean/rebind/model/AutoBeanMethod.java
/trunk/user/src/com/google/gwt/autobean/server/AutoBeanFactoryMagic.java
/trunk/user/src/com/google/gwt/autobean/server/BeanMethod.java
/trunk/user/src/com/google/gwt/autobean/server/FactoryHandler.java
/trunk/user/src/com/google/gwt/autobean/server/ProxyAutoBean.java
/trunk/user/src/com/google/gwt/autobean/server/ShimHandler.java
/trunk/user/src/com/google/gwt/autobean/shared/AutoBean.java
/trunk/user/src/com/google/gwt/autobean/shared/AutoBeanCodex.java
/trunk/user/src/com/google/gwt/autobean/shared/impl/AbstractAutoBean.java
/trunk/user/test/com/google/gwt/autobean/client/AutoBeanTest.java
/trunk/user/test/com/google/gwt/autobean/shared/AutoBeanCodexTest.java
=======================================
--- /dev/null
+++ /trunk/user/src/com/google/gwt/autobean/shared/impl/EnumMap.java Wed
Nov 10 11:04:47 2010
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+/**
+ * This interface is implemented by our generated AutoBeanFactory types to
+ * convert enum values to strings.
+ */
+public interface EnumMap {
+ public <E extends Enum<E>> E getEnum(Class<E> clazz, String token);
+
+ public String getToken(Enum<?> e);
+}
=======================================
---
/trunk/user/src/com/google/gwt/autobean/client/impl/AbstractAutoBeanFactory.java
Fri Nov 5 04:25:19 2010
+++
/trunk/user/src/com/google/gwt/autobean/client/impl/AbstractAutoBeanFactory.java
Wed Nov 10 11:04:47 2010
@@ -17,14 +17,17 @@
import com.google.gwt.autobean.shared.AutoBean;
import com.google.gwt.autobean.shared.AutoBeanFactory;
+import com.google.gwt.autobean.shared.impl.EnumMap;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
* Provides base implementations of AutoBeanFactory methods.
*/
-public abstract class AbstractAutoBeanFactory implements AutoBeanFactory {
+public abstract class AbstractAutoBeanFactory implements AutoBeanFactory,
+ EnumMap {
/**
* Implementations generated by subtypes. Used to implement the dynamic
create
* methods.
@@ -36,16 +39,60 @@
}
protected final Map<Class<?>, Creator> creators = new HashMap<Class<?>,
Creator>();
+ protected Map<Enum<?>, String> enumToStringMap;
+ // This map is almost always one-to-one
+ protected Map<String, List<Enum<?>>> stringsToEnumsMap;
@SuppressWarnings("unchecked")
- public <T, U extends T> AutoBean<T> create(Class<T> clazz, U delegate) {
+ public <T> AutoBean<T> create(Class<T> clazz) {
Creator c = creators.get(clazz);
- return c == null ? null : (AutoBean<T>) c.create(delegate);
+ return c == null ? null : (AutoBean<T>) c.create();
}
@SuppressWarnings("unchecked")
- public <T> AutoBean<T> create(Class<T> clazz) {
+ public <T, U extends T> AutoBean<T> create(Class<T> clazz, U delegate) {
Creator c = creators.get(clazz);
- return c == null ? null : (AutoBean<T>) c.create();
+ return c == null ? null : (AutoBean<T>) c.create(delegate);
+ }
+
+ /**
+ * EnumMap support.
+ */
+ public <E extends Enum<E>> E getEnum(Class<E> clazz, String token) {
+ maybeInitializeEnumMap();
+ List<Enum<?>> list = stringsToEnumsMap.get(token);
+ if (list == null) {
+ throw new IllegalArgumentException(token);
+ }
+ for (Enum<?> e : list) {
+ if (e.getDeclaringClass().equals(clazz)) {
+ @SuppressWarnings("unchecked")
+ E toReturn = (E) e;
+ return toReturn;
+ }
+ }
+ throw new IllegalArgumentException(clazz.getName());
+ }
+
+ /**
+ * EnumMap support.
+ */
+ public String getToken(Enum<?> e) {
+ maybeInitializeEnumMap();
+ String toReturn = enumToStringMap.get(e);
+ if (toReturn == null) {
+ throw new IllegalArgumentException(e.toString());
+ }
+ return toReturn;
+ }
+
+ protected abstract void initializeEnumMap();
+
+ private void maybeInitializeEnumMap() {
+ if (enumToStringMap == null) {
+ enumToStringMap = new HashMap<Enum<?>, String>();
+ stringsToEnumsMap = new HashMap<String, List<Enum<?>>>();
+ initializeEnumMap();
+ }
}
}
=======================================
---
/trunk/user/src/com/google/gwt/autobean/rebind/AutoBeanFactoryGenerator.java
Fri Nov 5 04:25:19 2010
+++
/trunk/user/src/com/google/gwt/autobean/rebind/AutoBeanFactoryGenerator.java
Wed Nov 10 11:04:47 2010
@@ -22,6 +22,7 @@
import com.google.gwt.autobean.rebind.model.AutoBeanMethod.Action;
import com.google.gwt.autobean.rebind.model.AutoBeanType;
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.CollectionPropertyContext;
@@ -35,16 +36,24 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JEnumConstant;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameter;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.generator.NameFactory;
import com.google.gwt.editor.rebind.model.ModelUtils;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
/**
* Generates implementations of AutoBeanFactory.
@@ -52,6 +61,7 @@
public class AutoBeanFactoryGenerator extends Generator {
private GeneratorContext context;
+ private String simpleSourceName;
private TreeLogger logger;
private AutoBeanFactoryModel model;
@@ -69,7 +79,7 @@
}
String packageName = toGenerate.getPackage().getName();
- String simpleSourceName = toGenerate.getName().replace('.', '_')
+ "Impl";
+ simpleSourceName = toGenerate.getName().replace('.', '_') + "Impl";
PrintWriter pw = context.tryCreate(logger, packageName,
simpleSourceName);
if (pw == null) {
return packageName + "." + simpleSourceName;
@@ -82,7 +92,11 @@
factory.setSuperclass(AbstractAutoBeanFactory.class.getCanonicalName());
factory.addImplementedInterface(typeName);
SourceWriter sw = factory.createSourceWriter(context, pw);
+ for (AutoBeanType type : model.getAllTypes()) {
+ writeAutoBean(type);
+ }
writeDynamicMethods(sw);
+ writeEnumSetup(sw);
writeMethods(sw);
sw.commit(logger);
@@ -157,8 +171,9 @@
// Only simple wrappers have a default constructor
if (type.isSimpleBean()) {
- // public FooIntfAutoBean() {}
- sw.println("public %s() {}", type.getSimpleSourceName());
+ // public FooIntfAutoBean(AutoBeanFactory factory) {}
+ sw.println("public %s(%s factory) {super(factory);}",
+ type.getSimpleSourceName(),
AutoBeanFactory.class.getCanonicalName());
}
// Clone constructor
@@ -169,10 +184,11 @@
sw.println("}");
// Wrapping constructor
- // public FooIntfAutoBean(FooIntfo wrapped) {
- sw.println("public %s(%s wrapped) {", type.getSimpleSourceName(),
+ // 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);");
+ sw.indentln("super(factory, wrapped);");
sw.println("}");
// public FooIntf as() {return shim;}
@@ -187,7 +203,7 @@
// public Class<Intf> getType() {return Intf.class;}
sw.println("public Class<%1$s> getType() {return %1$s.class;}",
- type.getPeerType().getQualifiedSourceName());
+
ModelUtils.ensureBaseType(type.getPeerType()).getQualifiedSourceName());
if (type.isSimpleBean()) {
writeCreateSimpleBean(sw, type);
@@ -279,11 +295,12 @@
if (type.isNoWrap()) {
continue;
}
- sw.println("creators.put(%s.class, new Creator() {",
- type.getPeerType().getQualifiedSourceName());
+ sw.println(
+ "creators.put(%s.class, new Creator() {",
+
ModelUtils.ensureBaseType(type.getPeerType()).getQualifiedSourceName());
if (type.isSimpleBean()) {
- sw.indentln("public %1$s create() { return new %1$s(); }",
- type.getQualifiedSourceName());
+ sw.indentln("public %1$s create() { return new %1$s(%2$s.this); }",
+ type.getQualifiedSourceName(), simpleSourceName);
} else {
sw.indentln("public %1$s create() { return null; }",
type.getQualifiedSourceName());
@@ -291,20 +308,70 @@
// public FooAutoBean create(Object delegate) {
// return new FooAutoBean((Foo) delegate); }
sw.indentln("public %1$s create(Object delegate) {"
- + " return new %1$s((%2$s) delegate); }",
- type.getQualifiedSourceName(),
+ + " return new %1$s(%2$s.this, (%3$s) delegate); }",
+ type.getQualifiedSourceName(), simpleSourceName,
type.getPeerType().getQualifiedSourceName());
sw.println("});");
}
sw.outdent();
sw.println("}");
}
+
+ private void writeEnumSetup(SourceWriter sw) {
+ // Make the deobfuscation model
+ Map<String, List<JEnumConstant>> map = new HashMap<String,
List<JEnumConstant>>();
+ for (Map.Entry<JEnumConstant, String> entry :
model.getEnumTokenMap().entrySet()) {
+ List<JEnumConstant> list = map.get(entry.getValue());
+ if (list == null) {
+ list = new ArrayList<JEnumConstant>();
+ map.put(entry.getValue(), list);
+ }
+ list.add(entry.getKey());
+ }
+
+ sw.println("@Override protected void initializeEnumMap() {");
+ sw.indent();
+ for (Map.Entry<JEnumConstant, String> entry :
model.getEnumTokenMap().entrySet()) {
+ // enumToStringMap.put(Enum.FOO, "FOO");
+ sw.println("enumToStringMap.put(%s.%s, \"%s\");",
+ entry.getKey().getEnclosingType().getQualifiedSourceName(),
+ entry.getKey().getName(), entry.getValue());
+ }
+ for (Map.Entry<String, List<JEnumConstant>> entry : map.entrySet()) {
+ String listExpr;
+ if (entry.getValue().size() == 1) {
+ JEnumConstant e = entry.getValue().get(0);
+ // Collections.singletonList(Enum.FOO)
+ listExpr = String.format("%s.<%s<?>> singletonList(%s.%s)",
+ Collections.class.getCanonicalName(),
+ Enum.class.getCanonicalName(),
+ e.getEnclosingType().getQualifiedSourceName(), e.getName());
+ } else {
+ // Arrays.asList(Enum.FOO, OtherEnum.FOO, ThirdEnum,FOO)
+ StringBuilder sb = new StringBuilder();
+ boolean needsComma = false;
+ sb.append(String.format("%s.<%s<?>> asList(",
+ Arrays.class.getCanonicalName(),
Enum.class.getCanonicalName()));
+ for (JEnumConstant e : entry.getValue()) {
+ if (needsComma) {
+ sb.append(",");
+ }
+ needsComma = true;
+
sb.append(e.getEnclosingType().getQualifiedSourceName()).append(".").append(
+ e.getName());
+ }
+ sb.append(")");
+ listExpr = sb.toString();
+ }
+ sw.println("stringsToEnumsMap.put(\"%s\", %s);", entry.getKey(),
listExpr);
+ }
+ sw.outdent();
+ sw.println("}");
+ }
private void writeMethods(SourceWriter sw) throws
UnableToCompleteException {
for (AutoBeanFactoryMethod method : model.getMethods()) {
AutoBeanType autoBeanType = method.getAutoBeanType();
-
- writeAutoBean(autoBeanType);
// public AutoBean<Foo> foo(FooSubtype wrapped) {
sw.println("public %s %s(%s) {",
method.getReturnType().getQualifiedSourceName(),
method.getName(),
@@ -318,13 +385,14 @@
method.getReturnType().getParameterizedQualifiedSourceName(),
AutoBeanUtils.class.getCanonicalName());
sw.println("if (toReturn != null) {return toReturn;}");
- // return new FooAutoBean(wrapped);
- sw.println("return new %s(wrapped);",
- autoBeanType.getQualifiedSourceName());
+ // return new FooAutoBean(Factory.this, wrapped);
+ sw.println("return new %s(%s.this, wrapped);",
+ autoBeanType.getQualifiedSourceName(), simpleSourceName);
sw.outdent();
} else {
- // return new FooAutoBean();
- sw.indentln("return new %s();",
autoBeanType.getQualifiedSourceName());
+ // return new FooAutoBean(Factory.this);
+ sw.indentln("return new %s(%s.this);",
+ autoBeanType.getQualifiedSourceName(), simpleSourceName);
}
sw.println("}");
}
@@ -346,10 +414,8 @@
sw.println("} else {");
sw.indent();
if (peer != null) {
- // Make sure we generate the potentially unreferenced peer type
- writeAutoBean(peer);
- // toReturn = new FooAutoBean(toReturn).as();
- sw.println("toReturn = new %s(toReturn).as();",
+ // toReturn = new FooAutoBean(getFactory(), toReturn).as();
+ sw.println("toReturn = new %s(getFactory(), toReturn).as();",
peer.getQualifiedSourceName());
}
sw.outdent();
@@ -485,11 +551,13 @@
* Generate traversal logic.
*/
private void writeTraversal(SourceWriter sw, AutoBeanType type) {
+ NameFactory names = new NameFactory();
sw.println(
"@Override protected void traverseProperties(%s visitor, %s ctx)
{",
AutoBeanVisitor.class.getCanonicalName(),
OneShotContext.class.getCanonicalName());
sw.indent();
+
for (AutoBeanMethod method : type.getMethods()) {
if (!method.getAction().equals(Action.GET)) {
continue;
@@ -530,8 +598,14 @@
propertyContextType = PropertyContext.class;
}
- // Make the PropertyContext that lets us call the setter
- String propertyContextName = method.getPropertyName()
+ "PropertyContext";
+ /*
+ * Make the PropertyContext that lets us call the setter. We allow
+ * multiple methods to be bound to the same property (e.g. to allow
JSON
+ * payloads to be interpreted as different types). The leading
underscore
+ * allows purely numeric property names, which are valid JSON map
keys.
+ */
+ String propertyContextName = names.createName("_"
+ + method.getPropertyName() + "PropertyContext");
sw.println("class %s implements %s {", propertyContextName,
propertyContextType.getCanonicalName());
sw.indent();
@@ -539,19 +613,23 @@
|| setter != null);
if (method.isCollection()) {
// Will return the collection's element type or null if not a
collection
- sw.println("public Class<?> getElementType() { return %s; }",
- method.getElementType().getQualifiedSourceName() + ".class");
+ sw.println(
+ "public Class<?> getElementType() { return %s.class; }",
+
ModelUtils.ensureBaseType(method.getElementType()).getQualifiedSourceName());
} else if (method.isMap()) {
// Will return the map's value type
- sw.println("public Class<?> getValueType() { return %s; }",
- method.getValueType().getQualifiedSourceName() + ".class");
+ sw.println(
+ "public Class<?> getValueType() { return %s.class; }",
+
ModelUtils.ensureBaseType(method.getValueType()).getQualifiedSourceName());
// Will return the map's key type
- sw.println("public Class<?> getKeyType() { return %s; }",
- method.getKeyType().getQualifiedSourceName() + ".class");
+ sw.println(
+ "public Class<?> getKeyType() { return %s.class; }",
+
ModelUtils.ensureBaseType(method.getKeyType()).getQualifiedSourceName());
}
// Return the property type
- sw.println("public Class<?> getType() { return %s.class; }",
- method.getMethod().getReturnType().getQualifiedSourceName());
+ sw.println(
+ "public Class<?> getType() { return %s.class; }",
+
ModelUtils.ensureBaseType(method.getMethod().getReturnType()).getQualifiedSourceName());
sw.println("public void set(Object obj) { ");
if (setter != null) {
// Prefer the setter if one exists
@@ -559,7 +637,8 @@
sw.indentln(
"as().%s((%s) obj);",
setter.getMethod().getName(),
-
setter.getMethod().getParameters()[0].getType().getQualifiedSourceName());
+ ModelUtils.ensureBaseType(
+
setter.getMethod().getParameters()[0].getType()).getQualifiedSourceName());
} else if (type.isSimpleBean()) {
// Otherwise, fall back to a map assignment
sw.indentln("values.put(\"%s\", obj);", method.getPropertyName());
=======================================
---
/trunk/user/src/com/google/gwt/autobean/rebind/model/AutoBeanFactoryModel.java
Fri Nov 5 04:25:19 2010
+++
/trunk/user/src/com/google/gwt/autobean/rebind/model/AutoBeanFactoryModel.java
Wed Nov 10 11:04:47 2010
@@ -23,6 +23,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JEnumConstant;
import com.google.gwt.core.ext.typeinfo.JGenericType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameter;
@@ -36,6 +37,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -49,6 +51,7 @@
private final JGenericType autoBeanInterface;
private final JClassType autoBeanFactoryInterface;
+ private final Map<JEnumConstant, String> allEnumConstants = new
LinkedHashMap<JEnumConstant, String>();
private final List<JClassType> categoryTypes;
private final List<JClassType> noWrapTypes;
private final TreeLogger logger;
@@ -77,7 +80,7 @@
*/
JClassType objectType = oracle.getJavaLangObject();
objectMethods = Arrays.asList(
- objectType.findMethod("equals", new JType[]{objectType}),
+ objectType.findMethod("equals", new JType[] {objectType}),
objectType.findMethod("hashCode", EMPTY_JTYPE),
objectType.findMethod("toString", EMPTY_JTYPE));
@@ -188,6 +191,10 @@
public List<JClassType> getCategoryTypes() {
return categoryTypes;
}
+
+ public Map<JEnumConstant, String> getEnumTokenMap() {
+ return Collections.unmodifiableMap(allEnumConstants);
+ }
public List<AutoBeanFactoryMethod> getMethods() {
return Collections.unmodifiableList(methods);
@@ -210,16 +217,13 @@
continue;
}
AutoBeanMethod.Builder builder = new AutoBeanMethod.Builder();
- String name = method.getName();
builder.setMethod(method);
// See if this method shouldn't have its return type wrapped
// TODO: Allow class return types?
JClassType classReturn = method.getReturnType().isInterface();
if (classReturn != null) {
- if (!peers.containsKey(classReturn)) {
- toCalculate.add(classReturn);
- }
+ maybeCalculate(classReturn);
if (noWrapTypes != null) {
for (JClassType noWrap : noWrapTypes) {
if (noWrap.isAssignableFrom(classReturn)) {
@@ -230,27 +234,34 @@
}
}
- if (name.startsWith("get") && name.length() >= 4
- && method.getParameters().length == 0) {
- // Found a getter
- builder.setAction(Action.GET);
-
- } else if (name.startsWith("set") && name.length() >= 4
- && method.getParameters().length == 1) {
- // Found a setter
- builder.setAction(Action.SET);
- } else {
- // Found something else
- builder.setAction(Action.CALL);
+ // GET, SET, or CALL
+ Action action = Action.which(method);
+ builder.setAction(action);
+ if (Action.CALL.equals(action)) {
JMethod staticImpl = findStaticImpl(beanType, method);
if (staticImpl == null && objectMethods.contains(method)) {
- // Don't complain about lack of implemenation for Object methods
+ // Don't complain about lack of implementation for Object methods
continue;
}
builder.setStaticImp(staticImpl);
}
- toReturn.add(builder.build());
+ AutoBeanMethod toAdd = builder.build();
+
+ // Collect referenced enums
+ if (toAdd.isEnum()) {
+ allEnumConstants.putAll(toAdd.getEnumMap());
+ }
+
+ // See if parameterizations will pull in more types
+ if (toAdd.isCollection()) {
+ maybeCalculate(toAdd.getElementType());
+ } else if (toAdd.isMap()) {
+ maybeCalculate(toAdd.getKeyType());
+ maybeCalculate(toAdd.getValueType());
+ }
+
+ toReturn.add(toAdd);
}
return toReturn;
}
@@ -380,6 +391,19 @@
}
return toReturn;
}
+
+ /**
+ * Enqueue a type in {...@link #toCalculate} if {...@link #peers} does not
already
+ * contain an entry.
+ */
+ private void maybeCalculate(JClassType type) {
+ if (type.isInterface() == null || ModelUtils.isValueType(oracle,
type)) {
+ return;
+ }
+ if (!peers.containsKey(type)) {
+ toCalculate.add(type);
+ }
+ }
private boolean methodAcceptsAutoBeanAsFirstParam(JClassType beanType,
JMethod method) {
@@ -397,7 +421,7 @@
// Check using base types to account for erasure semantics
JParameterizedType expectedFirst = oracle.getParameterizedType(
autoBeanInterface,
- new JClassType[]{ModelUtils.ensureBaseType(beanType)});
+ new JClassType[] {ModelUtils.ensureBaseType(beanType)});
return expectedFirst.isAssignableTo(paramAsClass);
}
=======================================
---
/trunk/user/src/com/google/gwt/autobean/rebind/model/AutoBeanMethod.java
Fri Nov 5 04:25:19 2010
+++
/trunk/user/src/com/google/gwt/autobean/rebind/model/AutoBeanMethod.java
Wed Nov 10 11:04:47 2010
@@ -17,11 +17,15 @@
import com.google.gwt.autobean.shared.AutoBean.PropertyName;
import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JEnumConstant;
+import com.google.gwt.core.ext.typeinfo.JEnumType;
import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.editor.rebind.model.ModelUtils;
import java.util.Collection;
+import java.util.LinkedHashMap;
import java.util.Map;
/**
@@ -32,8 +36,96 @@
* Describes the type of method that was invoked.
*/
public enum Action {
- GET, SET, CALL
- }
+ GET {
+ @Override
+ String inferName(JMethod method) {
+ if (JPrimitiveType.BOOLEAN.equals(method.getReturnType())) {
+ String name = method.getName();
+ if (name.startsWith("is") && name.length() > 2) {
+ name = Character.toLowerCase(name.charAt(2))
+ + (name.length() >= 4 ? name.substring(3) : "");
+ return name;
+ }
+ }
+ return super.inferName(method);
+ }
+
+ @Override
+ boolean matches(JMethod method) {
+ if (method.getParameters().length > 0) {
+ return false;
+ }
+ String name = method.getName();
+
+ // Allow boolean isFoo() or boolean hasFoo();
+ if (JPrimitiveType.BOOLEAN.equals(method.getReturnType())) {
+ if (name.startsWith("is") && name.length() > 2) {
+ return true;
+ }
+ if (name.startsWith("has") && name.length() > 3) {
+ return true;
+ }
+ }
+ if (name.startsWith("get") && name.length() > 3) {
+ return true;
+ }
+ return false;
+ }
+ },
+ SET {
+ @Override
+ boolean matches(JMethod method) {
+ if (!JPrimitiveType.VOID.equals(method.getReturnType())) {
+ return false;
+ }
+ if (method.getParameters().length != 1) {
+ return false;
+ }
+ String name = method.getName();
+ if (name.startsWith("set") && name.length() > 3) {
+ return true;
+ }
+ return false;
+ }
+ },
+ CALL {
+ /**
+ * Matches all leftover methods.
+ */
+ @Override
+ boolean matches(JMethod method) {
+ return true;
+ }
+ };
+
+ /**
+ * Determine which Action a method maps to.
+ */
+ public static Action which(JMethod method) {
+ for (Action action : Action.values()) {
+ if (action.matches(method)) {
+ return action;
+ }
+ }
+ throw new RuntimeException("CALL should have matched");
+ }
+
+ /**
+ * Infer the name of a property from the method.
+ */
+ String inferName(JMethod method) {
+ String name = method.getName();
+ name = Character.toLowerCase(name.charAt(3))
+ + (name.length() >= 5 ? name.substring(4) : "");
+ return name;
+ }
+
+ /**
+ * Returns {...@code true} if the Action matches the method.
+ */
+ abstract boolean matches(JMethod method);
+ }
+
/**
* Creates AutoBeanMethods.
*/
@@ -47,10 +139,7 @@
if (annotation != null) {
toReturn.propertyName = annotation.value();
} else {
- String name = toReturn.method.getName();
- // setFoo
- toReturn.propertyName = Character.toLowerCase(name.charAt(3))
- + (name.length() >= 5 ? name.substring(4) : "");
+ toReturn.propertyName =
toReturn.action.inferName(toReturn.method);
}
}
@@ -88,6 +177,22 @@
toReturn.valueType = parameterizations[1];
}
}
+
+ JEnumType enumType = method.getReturnType().isEnum();
+ if (enumType != null) {
+ Map<JEnumConstant, String> map = new LinkedHashMap<JEnumConstant,
String>();
+ for (JEnumConstant e : enumType.getEnumConstants()) {
+ String name;
+ PropertyName annotation = e.getAnnotation(PropertyName.class);
+ if (annotation == null) {
+ name = e.getName();
+ } else {
+ name = annotation.value();
+ }
+ map.put(e, name);
+ }
+ toReturn.enumMap = map;
+ }
}
public void setNoWrap(boolean noWrap) {
@@ -101,6 +206,7 @@
private Action action;
private JClassType elementType;
+ private Map<JEnumConstant, String> enumMap;
private JClassType keyType;
private JMethod method;
private boolean isNoWrap;
@@ -119,6 +225,10 @@
public JClassType getElementType() {
return elementType;
}
+
+ public Map<JEnumConstant, String> getEnumMap() {
+ return enumMap;
+ }
public JClassType getKeyType() {
return keyType;
@@ -148,6 +258,10 @@
public boolean isCollection() {
return elementType != null;
}
+
+ public boolean isEnum() {
+ return enumMap != null;
+ }
public boolean isMap() {
return keyType != null;
=======================================
---
/trunk/user/src/com/google/gwt/autobean/server/AutoBeanFactoryMagic.java
Fri Nov 5 04:25:19 2010
+++
/trunk/user/src/com/google/gwt/autobean/server/AutoBeanFactoryMagic.java
Wed Nov 10 11:04:47 2010
@@ -19,6 +19,7 @@
import com.google.gwt.autobean.shared.AutoBeanFactory;
import com.google.gwt.autobean.shared.AutoBeanFactory.Category;
import com.google.gwt.autobean.shared.AutoBeanFactory.NoWrap;
+import com.google.gwt.autobean.shared.impl.EnumMap;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
@@ -37,6 +38,8 @@
* AutoBeanFactoyModel.
*/
public class AutoBeanFactoryMagic {
+ private static final AutoBeanFactory EMPTY =
create(AutoBeanFactory.class);
+
/**
* Create an instance of an AutoBeanFactory.
*
@@ -55,7 +58,7 @@
builder.setNoWrap(noWrap.value());
}
- return makeProxy(clazz, new FactoryHandler(builder.build()));
+ return makeProxy(clazz, new FactoryHandler(builder.build()),
EnumMap.class);
}
/**
@@ -67,7 +70,7 @@
*/
public static <T> AutoBean<T> createBean(Class<T> clazz,
Configuration configuration) {
- return new ProxyAutoBean<T>(clazz, configuration);
+ return new ProxyAutoBean<T>(EMPTY, clazz, configuration);
}
/**
@@ -76,11 +79,22 @@
* @param <T> the interface type to be implemented by the Proxy
* @param intf the Class representing the interface type
* @param handler the implementation of the interface
+ * @param extraInterfaces additional interface types the Proxy should
+ * implement
* @return a Proxy instance
*/
- static <T> T makeProxy(Class<T> intf, InvocationHandler handler) {
+ static <T> T makeProxy(Class<T> intf, InvocationHandler handler,
+ Class<?>... extraInterfaces) {
+ Class<?>[] intfs;
+ if (extraInterfaces == null) {
+ 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(
- Thread.currentThread().getContextClassLoader(), new
Class<?>[]{intf},
- handler));
+ Thread.currentThread().getContextClassLoader(), intfs, handler));
}
}
=======================================
--- /trunk/user/src/com/google/gwt/autobean/server/BeanMethod.java Fri Nov
5 04:25:19 2010
+++ /trunk/user/src/com/google/gwt/autobean/server/BeanMethod.java Wed Nov
10 11:04:47 2010
@@ -50,8 +50,15 @@
GET {
@Override
Object invoke(SimpleBeanHandler<?> handler, Method method, Object[]
args) {
- Object toReturn = handler.getBean().getValues().get(
- method.getName().substring(3));
+ String propertyName;
+ String name = method.getName();
+ if (Boolean.TYPE.equals(method.getReturnType()) &&
name.startsWith("is")) {
+ propertyName = name.substring(2);
+ } else {
+ // A regular getter or a boolean hasFoo()
+ propertyName = name.substring(3);
+ }
+ Object toReturn = handler.getBean().getValues().get(propertyName);
if (toReturn == null && method.getReturnType().isPrimitive()) {
toReturn =
TypeUtils.getDefaultPrimitiveValue(method.getReturnType());
}
@@ -60,9 +67,20 @@
@Override
boolean matches(SimpleBeanHandler<?> handler, Method method) {
- return method.getName().startsWith("get")
- && method.getParameterTypes().length == 0
- && !method.getReturnType().equals(Void.TYPE);
+ Class<?> returnType = method.getReturnType();
+ if (method.getParameterTypes().length != 0
+ || Void.TYPE.equals(returnType)) {
+ return false;
+ }
+
+ String name = method.getName();
+ if (Boolean.TYPE.equals(returnType)) {
+ if (name.startsWith("is") && name.length() > 2
+ || name.startsWith("has") && name.length() > 3) {
+ return true;
+ }
+ }
+ return name.startsWith("get") && name.length() > 3;
}
},
/**
@@ -77,7 +95,8 @@
@Override
boolean matches(SimpleBeanHandler<?> handler, Method method) {
- return method.getName().startsWith("set")
+ String name = method.getName();
+ return name.startsWith("set") && name.length() > 3
&& method.getParameterTypes().length == 1
&& method.getReturnType().equals(Void.TYPE);
}
=======================================
--- /trunk/user/src/com/google/gwt/autobean/server/FactoryHandler.java Fri
Nov 5 04:25:19 2010
+++ /trunk/user/src/com/google/gwt/autobean/server/FactoryHandler.java Wed
Nov 10 11:04:47 2010
@@ -15,8 +15,11 @@
*/
package com.google.gwt.autobean.server;
+import com.google.gwt.autobean.shared.AutoBean.PropertyName;
+import com.google.gwt.autobean.shared.AutoBeanFactory;
import com.google.gwt.autobean.shared.AutoBeanUtils;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
@@ -45,13 +48,21 @@
Class<?> beanType;
Object toWrap = null;
- if (method.getName().equals("create")) {
+ String name = method.getName();
+ if (name.equals("create")) {
// Dynamic create. Guaranteed to have at least one argument
// create(clazz); or create(clazz, toWrap);
beanType = (Class<?>) args[0];
if (args.length == 2) {
toWrap = args[1];
}
+ } else if (name.equals("getEnum")) {
+ Class<?> clazz = (Class<?>) args[0];
+ String token = (String) args[1];
+ return getEnum(clazz, token);
+ } else if (name.equals("getToken")) {
+ Enum<?> e = (Enum<?>) args[0];
+ return getToken(e);
} else {
// Declared factory method, use the parameterization
// AutoBean<Foo> foo(); or Autobean<foo> foo(Foo toWrap);
@@ -68,12 +79,50 @@
if (toReturn == null) {
// Create the implementation bean
if (toWrap == null) {
- toReturn = new ProxyAutoBean<Object>(beanType, configuration);
+ toReturn = new ProxyAutoBean<Object>((AutoBeanFactory) proxy,
beanType,
+ configuration);
} else {
- toReturn = new ProxyAutoBean<Object>(beanType, configuration,
toWrap);
+ toReturn = new ProxyAutoBean<Object>((AutoBeanFactory) proxy,
beanType,
+ configuration, toWrap);
}
}
return toReturn;
}
-}
+
+ /**
+ * EnumMap support.
+ */
+ private Object getEnum(Class<?> clazz, String token)
+ throws IllegalAccessException {
+ for (Field f : clazz.getFields()) {
+ String fieldName;
+ PropertyName annotation = f.getAnnotation(PropertyName.class);
+ if (annotation != null) {
+ fieldName = annotation.value();
+ } else {
+ fieldName = f.getName();
+ }
+ if (token.equals(fieldName)) {
+ f.setAccessible(true);
+ return f.get(null);
+ }
+ }
+ throw new IllegalArgumentException("Cannot find enum " + token
+ + " in type " + clazz.getCanonicalName());
+ }
+
+ /**
+ * EnumMap support.
+ */
+ private Object getToken(Enum<?> e) throws NoSuchFieldException {
+ // Remember enum constants are fields
+ PropertyName annotation =
e.getDeclaringClass().getField(e.name()).getAnnotation(
+ PropertyName.class);
+ if (annotation != null) {
+ return annotation.value();
+ } else {
+ return e.name();
+ }
+ }
+}
=======================================
--- /trunk/user/src/com/google/gwt/autobean/server/ProxyAutoBean.java Fri
Nov 5 04:25:19 2010
+++ /trunk/user/src/com/google/gwt/autobean/server/ProxyAutoBean.java Wed
Nov 10 11:04:47 2010
@@ -17,6 +17,7 @@
import com.google.gwt.autobean.server.impl.TypeUtils;
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.impl.AbstractAutoBean;
@@ -44,8 +45,9 @@
// These constructors mirror the generated constructors.
@SuppressWarnings("unchecked")
- public ProxyAutoBean(Class<?> beanType, Configuration configuration) {
- super();
+ public ProxyAutoBean(AutoBeanFactory factory, Class<?> beanType,
+ Configuration configuration) {
+ super(factory);
this.beanType = (Class<T>) beanType;
this.configuration = configuration;
this.getters = calculateGetters();
@@ -53,8 +55,9 @@
}
@SuppressWarnings("unchecked")
- public ProxyAutoBean(Class<?> beanType, Configuration configuration, T
toWrap) {
- super(toWrap);
+ public ProxyAutoBean(AutoBeanFactory factory, Class<?> beanType,
+ Configuration configuration, T toWrap) {
+ super(factory, toWrap);
if (Proxy.isProxyClass(toWrap.getClass())) {
System.out.println("blah");
}
=======================================
--- /trunk/user/src/com/google/gwt/autobean/server/ShimHandler.java Fri
Nov 5 04:25:19 2010
+++ /trunk/user/src/com/google/gwt/autobean/server/ShimHandler.java Wed Nov
10 11:04:47 2010
@@ -126,8 +126,8 @@
}
return toReturn;
}
- ProxyAutoBean<Object> newBean = new ProxyAutoBean<Object>(intf,
- bean.getConfiguration(), toReturn);
+ ProxyAutoBean<Object> newBean = new ProxyAutoBean<Object>(
+ bean.getFactory(), intf, bean.getConfiguration(), toReturn);
return newBean.as();
}
}
=======================================
--- /trunk/user/src/com/google/gwt/autobean/shared/AutoBean.java Fri Nov 5
04:25:19 2010
+++ /trunk/user/src/com/google/gwt/autobean/shared/AutoBean.java Wed Nov 10
11:04:47 2010
@@ -36,7 +36,7 @@
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
+ @Target(value = {ElementType.METHOD, ElementType.FIELD})
public @interface PropertyName {
String value();
}
@@ -69,6 +69,13 @@
*/
AutoBean<T> clone(boolean deep);
+ /**
+ * Returns the AutoBeanFactory that created the AutoBean.
+ *
+ * @return an AutoBeanFactory
+ */
+ AutoBeanFactory getFactory();
+
/**
* Retrieve a tag value that was previously provided to
* {...@link #setTag(String, Object)}.
=======================================
--- /trunk/user/src/com/google/gwt/autobean/shared/AutoBeanCodex.java Fri
Nov 5 04:25:19 2010
+++ /trunk/user/src/com/google/gwt/autobean/shared/AutoBeanCodex.java Wed
Nov 10 11:04:47 2010
@@ -15,6 +15,7 @@
*/
package com.google.gwt.autobean.shared;
+import com.google.gwt.autobean.shared.impl.EnumMap;
import com.google.gwt.autobean.shared.impl.LazySplittable;
import com.google.gwt.autobean.shared.impl.StringQuoter;
@@ -75,8 +76,7 @@
collection.add(null);
} else {
if (isValue) {
- collection.add(ValueCodex.decode(ctx.getElementType(),
- listData.get(i)));
+ collection.add(decodeValue(ctx.getElementType(),
listData.get(i)));
} else if (isEncoded) {
collection.add(listData.get(i));
} else {
@@ -129,8 +129,11 @@
public boolean visitValueProperty(String propertyName, Object value,
PropertyContext ctx) {
if (!data.isNull(propertyName)) {
+ Object object;
Splittable propertyValue = data.get(propertyName);
- ctx.set(ValueCodex.decode(ctx.getType(), propertyValue));
+ Class<?> type = ctx.getType();
+ object = decodeValue(type, propertyValue);
+ ctx.set(object);
}
return false;
}
@@ -160,7 +163,7 @@
} else if (isEncodedValue) {
value = keyList.get(i);
} else if (isValueValue) {
- value = ValueCodex.decode(valueType, keyList.get(i));
+ value = decodeValue(valueType, keyList.get(i));
} else {
value = decode(valueList.get(i), valueType).as();
}
@@ -169,6 +172,26 @@
}
return toReturn;
}
+
+ private Object decodeValue(Class<?> type, Splittable propertyValue) {
+ return decodeValue(type, propertyValue.asString());
+ }
+
+ private Object decodeValue(Class<?> type, String propertyValue) {
+ Object object;
+ if (type.isEnum() && bean.getFactory() instanceof EnumMap) {
+ // The generics kind of get in the way here
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ Class<Enum> enumType = (Class<Enum>) type;
+ @SuppressWarnings("unchecked")
+ Enum<?> e = ((EnumMap) bean.getFactory()).getEnum(enumType,
+ propertyValue);
+ object = e;
+ } else {
+ object = ValueCodex.decode(type, propertyValue);
+ }
+ return object;
+ }
private Map<?, ?> decodeValueKeyMap(Splittable map, Class<?> keyType,
Class<?> valueType) {
@@ -177,14 +200,14 @@
boolean isEncodedValue = Splittable.class.equals(valueType);
boolean isValueValue = ValueCodex.canDecode(valueType);
for (String encodedKey : map.getPropertyKeys()) {
- Object key = ValueCodex.decode(keyType, encodedKey);
+ Object key = decodeValue(keyType, encodedKey);
Object value;
if (map.isNull(encodedKey)) {
value = null;
} else if (isEncodedValue) {
value = map.get(encodedKey);
} else if (isValueValue) {
- value = ValueCodex.decode(valueType, map.get(encodedKey));
+ value = decodeValue(valueType, map.get(encodedKey));
} else {
value = decode(map.get(encodedKey), valueType).as();
}
@@ -213,16 +236,27 @@
private void push(Splittable data, Class<?> type) {
this.data = data;
bean = factory.create(type);
+ if (bean == null) {
+ throw new IllegalArgumentException(
+ "The AutoBeanFactory cannot create a " + type.getName());
+ }
dataStack.push(data);
beanStack.push(bean);
}
}
static class Encoder extends AutoBeanVisitor {
+ private EnumMap enumMap;
private Set<AutoBean<?>> seen = new HashSet<AutoBean<?>>();
private Stack<StringBuilder> stack = new Stack<StringBuilder>();
private StringBuilder sb;
+ public Encoder(AutoBeanFactory factory) {
+ if (factory instanceof EnumMap) {
+ enumMap = (EnumMap) factory;
+ }
+ }
+
@Override
public void endVisit(AutoBean<?> bean, Context ctx) {
if (sb.length() == 0) {
@@ -261,7 +295,7 @@
if (ValueCodex.canDecode(ctx.getElementType())) {
for (Object element : collection) {
- sb.append(",").append(ValueCodex.encode(element).getPayload());
+ sb.append(",").append(encodeValue(element).getPayload());
}
} else {
boolean isEncoded = Splittable.class.equals(ctx.getElementType());
@@ -335,13 +369,18 @@
public boolean visitValueProperty(String propertyName, Object value,
PropertyContext ctx) {
// Skip primitive types whose values are uninteresting.
+ Class<?> type = ctx.getType();
if (value != null) {
- if
(value.equals(ValueCodex.getUninitializedFieldValue(ctx.getType()))) {
+ if (value.equals(ValueCodex.getUninitializedFieldValue(type))) {
return false;
}
}
+
+ // Special handling for enums if we have an obfuscation map
+ Splittable split;
+ split = encodeValue(value);
sb.append(",\"").append(propertyName).append("\":").append(
- ValueCodex.encode(value).getPayload());
+ split.getPayload());
return false;
}
@@ -366,6 +405,20 @@
accumulator.append(pop().toString());
seen.remove(bean);
}
+
+ /**
+ * Encodes a value, with special handling for enums to allow the field
name
+ * to be overridden.
+ */
+ private Splittable encodeValue(Object value) {
+ Splittable split;
+ if (value instanceof Enum<?> && enumMap != null) {
+ split = ValueCodex.encode(enumMap.getToken((Enum<?>) value));
+ } else {
+ split = ValueCodex.encode(value);
+ }
+ return split;
+ }
private void haltOnCycle() {
throw new HaltException(new UnsupportedOperationException(
@@ -393,8 +446,7 @@
values.append(",").append(
((Splittable) entry.getValue()).getPayload());
} else if (isValueValue) {
- values.append(",").append(
- ValueCodex.encode(entry.getValue()).getPayload());
+
values.append(",").append(encodeValue(entry.getValue()).getPayload());
} else {
encodeToStringBuilder(values.append(","), entry.getValue());
}
@@ -414,12 +466,12 @@
private void writeValueKeyMap(Map<?, ?> map, boolean isEncodedValue,
boolean isValueValue) {
for (Map.Entry<?, ?> entry : map.entrySet()) {
-
sb.append(",").append(ValueCodex.encode(entry.getKey()).getPayload()).append(
+
sb.append(",").append(encodeValue(entry.getKey()).getPayload()).append(
":");
if (isEncodedValue) {
sb.append(((Splittable) entry.getValue()).getPayload());
} else if (isValueValue) {
- sb.append(ValueCodex.encode(entry.getValue()).getPayload());
+ sb.append(encodeValue(entry.getValue()).getPayload());
} else {
encodeToStringBuilder(sb, entry.getValue());
}
@@ -483,7 +535,7 @@
// ["prop",value,"prop",value, ...]
private static void encodeForJsoPayload(StringBuilder sb, AutoBean<?>
bean) {
- Encoder e = new Encoder();
+ Encoder e = new Encoder(bean.getFactory());
e.push(sb);
try {
bean.accept(e);
=======================================
---
/trunk/user/src/com/google/gwt/autobean/shared/impl/AbstractAutoBean.java
Fri Nov 5 04:25:19 2010
+++
/trunk/user/src/com/google/gwt/autobean/shared/impl/AbstractAutoBean.java
Wed Nov 10 11:04:47 2010
@@ -16,6 +16,7 @@
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.Context;
@@ -50,6 +51,7 @@
*/
protected final Map<String, Object> values;
+ private final AutoBeanFactory factory;
private boolean frozen;
/**
@@ -63,7 +65,8 @@
/**
* Constructor that will use a generated simple peer.
*/
- protected AbstractAutoBean() {
+ protected AbstractAutoBean(AutoBeanFactory factory) {
+ this.factory = factory;
usingSimplePeer = true;
values = new HashMap<String, Object>();
}
@@ -72,6 +75,7 @@
* Clone constructor.
*/
protected AbstractAutoBean(AbstractAutoBean<T> toClone, boolean deep) {
+ this.factory = toClone.factory;
if (!toClone.usingSimplePeer) {
throw new IllegalStateException("Cannot clone wrapped bean");
}
@@ -94,7 +98,8 @@
/**
* Constructor that wraps an existing object.
*/
- protected AbstractAutoBean(T wrapped) {
+ protected AbstractAutoBean(AutoBeanFactory factory, T wrapped) {
+ this.factory = factory;
usingSimplePeer = false;
values = null;
this.wrapped = wrapped;
@@ -111,6 +116,10 @@
public abstract AutoBean<T> clone(boolean deep);
+ public AutoBeanFactory getFactory() {
+ return factory;
+ }
+
@SuppressWarnings("unchecked")
public <Q> Q getTag(String tagName) {
return tags == null ? null : (Q) tags.get(tagName);
=======================================
--- /trunk/user/test/com/google/gwt/autobean/client/AutoBeanTest.java Fri
Nov 5 04:25:19 2010
+++ /trunk/user/test/com/google/gwt/autobean/client/AutoBeanTest.java Wed
Nov 10 11:04:47 2010
@@ -38,15 +38,15 @@
public static class CallImpl {
public static Object seen;
- public static int add(AutoBean<HasCall> bean, int a, int b) {
- assertNotNull(bean);
- return ((Integer) bean.getTag("offset")) + a + b;
- }
-
public static <T> T __intercept(AutoBean<HasCall> bean, T value) {
seen = value;
return value;
}
+
+ public static int add(AutoBean<HasCall> bean, int a, int b) {
+ assertNotNull(bean);
+ return ((Integer) bean.getTag("offset")) + a + b;
+ }
}
/**
@@ -54,6 +54,8 @@
*/
@Category(CallImpl.class)
protected interface Factory extends AutoBeanFactory {
+ AutoBean<HasBoolean> hasBoolean();
+
AutoBean<HasCall> hasCall();
AutoBean<HasList> hasList();
@@ -64,6 +66,20 @@
AutoBean<OtherIntf> otherIntf();
}
+
+ interface HasBoolean {
+ boolean isIs();
+
+ boolean getGet();
+
+ boolean hasHas();
+
+ void setIs(boolean value);
+
+ void setGet(boolean value);
+
+ void setHas(boolean value);
+ }
interface HasCall {
int add(int a, int b);
@@ -94,8 +110,8 @@
}
static class RealIntf implements Intf {
- String string;
int i;
+ String string;
@Override
public boolean equals(Object o) {
@@ -137,6 +153,21 @@
public String getModuleName() {
return "com.google.gwt.autobean.AutoBean";
}
+
+ public void testBooleanIsHasMethods() {
+ HasBoolean b = factory.hasBoolean().as();
+ assertFalse(b.getGet());
+ assertFalse(b.hasHas());
+ assertFalse(b.isIs());
+
+ b.setGet(true);
+ b.setHas(true);
+ b.setIs(true);
+
+ assertTrue(b.getGet());
+ assertTrue(b.hasHas());
+ assertTrue(b.isIs());
+ }
public void testCategory() {
AutoBean<HasCall> call = factory.hasCall();
@@ -268,6 +299,11 @@
assertEquals(real.toString(), w.as().toString());
assertEquals(w.as(), real);
}
+
+ public void testFactory() {
+ AutoBean<Intf> auto = factory.intf();
+ assertSame(factory, auto.getFactory());
+ }
public void testFreezing() {
AutoBean<Intf> auto = factory.intf();
=======================================
--- /trunk/user/test/com/google/gwt/autobean/shared/AutoBeanCodexTest.java
Fri Nov 5 04:25:19 2010
+++ /trunk/user/test/com/google/gwt/autobean/shared/AutoBeanCodexTest.java
Wed Nov 10 11:04:47 2010
@@ -15,6 +15,8 @@
*/
package com.google.gwt.autobean.shared;
+import com.google.gwt.autobean.shared.AutoBean.PropertyName;
+import com.google.gwt.autobean.shared.impl.EnumMap;
import com.google.gwt.core.client.GWT;
import com.google.gwt.junit.client.GWTTestCase;
@@ -34,16 +36,12 @@
protected interface Factory extends AutoBeanFactory {
AutoBean<HasAutoBean> hasAutoBean();
- /**
- * @return
- */
AutoBean<HasCycle> hasCycle();
+ AutoBean<HasEnum> hasEnum();
+
AutoBean<HasList> hasList();
- /**
- * @return
- */
AutoBean<HasMap> hasMap();
AutoBean<HasSimple> hasSimple();
@@ -73,6 +71,20 @@
void setCycle(List<HasCycle> cycle);
}
+
+ interface HasEnum {
+ MyEnum getEnum();
+
+ List<MyEnum> getEnums();
+
+ Map<MyEnum, Integer> getMap();
+
+ void setEnum(MyEnum value);
+
+ void setEnums(List<MyEnum> value);
+
+ void setMap(Map<MyEnum, Integer> value);
+ }
interface HasList {
List<Integer> getIntList();
@@ -89,6 +101,9 @@
Map<String, Simple> getSimpleMap();
+ @AutoBean.PropertyName("simpleMap")
+ Map<String, ReachableOnlyFromParameterization> getSimpleMapAltType();
+
void setComplexMap(Map<Simple, Simple> map);
void setSimpleMap(Map<String, Simple> map);
@@ -99,6 +114,16 @@
void setSimple(Simple s);
}
+
+ enum MyEnum {
+ FOO, BAR,
+ // The eclipse formatter wants to put this annotation inline
+ @PropertyName("quux")
+ BAZ;
+ }
+
+ interface ReachableOnlyFromParameterization extends Simple {
+ }
interface Simple {
int getInt();
@@ -136,6 +161,34 @@
assertNotNull(decodedBean.as().getList());
assertTrue(decodedBean.as().getList().isEmpty());
}
+
+ public void testEnum() {
+ EnumMap map = (EnumMap) f;
+ assertEquals("BAR", map.getToken(MyEnum.BAR));
+ assertEquals("quux", map.getToken(MyEnum.BAZ));
+ 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,
MyEnum.BAZ);
+ Map<MyEnum, Integer> mapValue = new HashMap<MyEnum, Integer>();
+ mapValue.put(MyEnum.FOO, 0);
+ mapValue.put(MyEnum.BAR, 1);
+ mapValue.put(MyEnum.BAZ, 2);
+
+ AutoBean<HasEnum> bean = f.hasEnum();
+ bean.as().setEnum(MyEnum.BAZ);
+ bean.as().setEnums(arrayValue);
+ bean.as().setMap(mapValue);
+
+ Splittable split = AutoBeanCodex.encode(bean);
+ // Make sure the overridden form is always used
+ assertFalse(split.getPayload().contains("BAZ"));
+
+ AutoBean<HasEnum> decoded = AutoBeanCodex.decode(f, HasEnum.class,
split);
+ assertEquals(MyEnum.BAZ, decoded.as().getEnum());
+ assertEquals(arrayValue, decoded.as().getEnums());
+ assertEquals(mapValue, decoded.as().getMap());
+ }
public void testMap() {
AutoBean<HasMap> bean = f.hasMap();
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors