Move AutoBean package to com.google.web.bindery.autobean package.
http://code.google.com/p/google-web-toolkit/issues/detail?id=6253
Review at http://gwt-code-reviews.appspot.com/1414803
Patch by: bobv
Review by: rjrjr
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10007 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/web/bindery/autobean/AutoBean.gwt.xml b/user/src/com/google/web/bindery/autobean/AutoBean.gwt.xml
new file mode 100644
index 0000000..88b8981
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/AutoBean.gwt.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" ?>
+<!--
+ 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.
+-->
+
+<!-- AutoBean framework -->
+<module>
+ <inherits name="com.google.gwt.core.Core" />
+ <inherits name="com.google.gwt.user.User" />
+ <source path="gwt/client" />
+ <source path="shared" />
+ <super-source path="super" />
+ <generate-with class="com.google.web.bindery.autobean.gwt.rebind.AutoBeanFactoryGenerator">
+ <when-type-assignable class="com.google.web.bindery.autobean.shared.AutoBeanFactory" />
+ </generate-with>
+</module>
diff --git a/user/src/com/google/web/bindery/autobean/gwt/client/impl/AbstractAutoBeanFactory.java b/user/src/com/google/web/bindery/autobean/gwt/client/impl/AbstractAutoBeanFactory.java
new file mode 100644
index 0000000..fc3a0cd
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/gwt/client/impl/AbstractAutoBeanFactory.java
@@ -0,0 +1,95 @@
+/*
+ * 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.web.bindery.autobean.gwt.client.impl;
+
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory;
+import com.google.web.bindery.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, EnumMap {
+
+ protected Map<Enum<?>, String> enumToStringMap;
+ // This map is almost always one-to-one
+ protected Map<String, List<Enum<?>>> stringsToEnumsMap;
+ private JsniCreatorMap creatorMap;
+
+ public <T> AutoBean<T> create(Class<T> clazz) {
+ maybeInitializeCreatorMap();
+ return creatorMap.create(clazz, this);
+ }
+
+ public <T, U extends T> AutoBean<T> create(Class<T> clazz, U delegate) {
+ maybeInitializeCreatorMap();
+ return creatorMap.create(clazz, this, delegate);
+ }
+
+ /**
+ * EnumMap support.
+ */
+ public <E extends Enum<?>> 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 initializeCreatorMap(JsniCreatorMap creatorMap);
+
+ protected abstract void initializeEnumMap();
+
+ private void maybeInitializeCreatorMap() {
+ if (creatorMap == null) {
+ creatorMap = JsniCreatorMap.createMap();
+ initializeCreatorMap(creatorMap);
+ }
+ }
+
+ private void maybeInitializeEnumMap() {
+ if (enumToStringMap == null) {
+ enumToStringMap = new HashMap<Enum<?>, String>();
+ stringsToEnumsMap = new HashMap<String, List<Enum<?>>>();
+ initializeEnumMap();
+ }
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/gwt/client/impl/ClientPropertyContext.java b/user/src/com/google/web/bindery/autobean/gwt/client/impl/ClientPropertyContext.java
new file mode 100644
index 0000000..08c753c
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/gwt/client/impl/ClientPropertyContext.java
@@ -0,0 +1,161 @@
+/*
+ * 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.web.bindery.autobean.gwt.client.impl;
+
+import com.google.web.bindery.autobean.shared.AutoBeanVisitor.CollectionPropertyContext;
+import com.google.web.bindery.autobean.shared.AutoBeanVisitor.MapPropertyContext;
+import com.google.web.bindery.autobean.shared.AutoBeanVisitor.ParameterizationVisitor;
+import com.google.web.bindery.autobean.shared.AutoBeanVisitor.PropertyContext;
+import com.google.web.bindery.autobean.shared.impl.AbstractAutoBean;
+import com.google.gwt.core.client.JavaScriptObject;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Provides base methods for generated implementations of PropertyContext.
+ */
+public final class ClientPropertyContext implements PropertyContext, 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()}
+ * .
+ */
+ public static native Setter beanSetter(AbstractAutoBean<?> bean, String key) /*-{
+ return function(value) {
+ bean.@com.google.web.bindery.autobean.shared.impl.AbstractAutoBean::setProperty(*)(key, value);
+ };
+ }-*/;
+
+ protected Setter() {
+ }
+
+ public native void call(Object instance, Object value) /*-{
+ this.call(instance, value);
+ }-*/;
+ }
+
+ private final Object instance;
+ private final int[] paramCounts;
+ private final Class<?>[] paramTypes;
+ private final Setter setter;
+ private final Class<?> simpleType;
+
+ public ClientPropertyContext(Object instance, Setter setter, Class<?> type) {
+ this.instance = instance;
+ this.setter = setter;
+ this.simpleType = type;
+ this.paramTypes = null;
+ this.paramCounts = null;
+ }
+
+ public ClientPropertyContext(Object instance, Setter setter, Class<?>[] types, int[] paramCounts) {
+ this.instance = instance;
+ this.setter = setter;
+ this.simpleType = null;
+ this.paramTypes = types;
+ this.paramCounts = paramCounts;
+
+ /*
+ * Verify input arrays of same length and that the total parameter count,
+ * plus one for the root type, equals the total number of types passed in.
+ */
+ if (ClientPropertyContext.class.desiredAssertionStatus()) {
+ assert types.length == paramCounts.length : "Length mismatch " + types.length + " != "
+ + paramCounts.length;
+ int count = 1;
+ for (int i = 0, j = paramCounts.length; i < j; i++) {
+ count += paramCounts[i];
+ }
+ assert count == types.length : "Mismatch in total parameter count " + count + " != "
+ + types.length;
+ }
+ }
+
+ public void accept(ParameterizationVisitor visitor) {
+ traverse(visitor, 0);
+ }
+
+ public boolean canSet() {
+ return setter != null;
+ }
+
+ public Class<?> getElementType() {
+ if (paramTypes == null || paramTypes.length < 2) {
+ return null;
+ }
+ if (List.class.equals(paramTypes[0]) || Set.class.equals(paramTypes[0])) {
+ return paramTypes[1];
+ }
+ return null;
+ }
+
+ public Class<?> getKeyType() {
+ if (paramTypes == null || paramTypes.length < 3) {
+ return null;
+ }
+ if (Map.class.equals(paramTypes[0])) {
+ return paramTypes[1];
+ }
+ return null;
+ }
+
+ public Class<?> getType() {
+ return simpleType == null ? paramTypes[0] : simpleType;
+ }
+
+ public Class<?> getValueType() {
+ if (paramTypes == null || paramTypes.length < 3) {
+ return null;
+ }
+ if (Map.class.equals(paramTypes[0])) {
+ return paramTypes[2];
+ }
+ return null;
+ }
+
+ public void set(Object value) {
+ setter.call(instance, value);
+ }
+
+ private int traverse(ParameterizationVisitor visitor, int count) {
+ if (simpleType != null) {
+ visitor.visitType(simpleType);
+ visitor.endVisitType(simpleType);
+ return 0;
+ }
+
+ Class<?> type = paramTypes[count];
+ int paramCount = paramCounts[count];
+ ++count;
+ if (visitor.visitType(type)) {
+ for (int i = 0; i < paramCount; i++) {
+ if (visitor.visitParameter()) {
+ count = traverse(visitor, count);
+ }
+ visitor.endVisitParameter();
+ }
+ }
+ visitor.endVisitType(type);
+ return count;
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/gwt/client/impl/JsniCreatorMap.java b/user/src/com/google/web/bindery/autobean/gwt/client/impl/JsniCreatorMap.java
new file mode 100644
index 0000000..d6e4468
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/gwt/client/impl/JsniCreatorMap.java
@@ -0,0 +1,71 @@
+/*
+ * 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.web.bindery.autobean.gwt.client.impl;
+
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
+
+/**
+ * Used in prod-mode code to create instances of generated AutoBean subtypes via
+ * JSNI references to their constructor methods.
+ */
+public final class JsniCreatorMap extends JavaScriptObject {
+ public static JsniCreatorMap createMap() {
+ return JavaScriptObject.createObject().cast();
+ }
+
+ /*
+ * Structure is a string map of class literal names to the no-arg and one-arg
+ * constructors of a generated AutoBean subtype.
+ */
+ protected JsniCreatorMap() {
+ }
+
+ public void add(Class<?> clazz, JsArray<JavaScriptObject> constructors) {
+ assert constructors.length() == 2 : "Expecting two constructor references";
+ set(clazz.getName(), constructors);
+ }
+
+ public <T> AutoBean<T> create(Class<T> clazz, AbstractAutoBeanFactory factory) {
+ JsArray<JavaScriptObject> arr = get(clazz.getName());
+ if (arr != null && arr.get(0) != null) {
+ return invoke(arr.get(0), factory, null);
+ }
+ return null;
+ }
+
+ public <T> AutoBean<T> create(Class<T> clazz, AbstractAutoBeanFactory factory, Object delegate) {
+ JsArray<JavaScriptObject> arr = get(clazz.getName());
+ if (arr != null) {
+ assert arr.get(1) != null : "No delegate-based constructor";
+ return invoke(arr.get(1), factory, delegate);
+ }
+ return null;
+ }
+
+ private native JsArray<JavaScriptObject> get(String key) /*-{
+ return this[key];
+ }-*/;
+
+ private native <T> AutoBean<T> invoke(JavaScriptObject fn, Object arg1, Object arg2)/*-{
+ return fn(arg1, arg2);
+ }-*/;
+
+ private native void set(String key, JsArray<JavaScriptObject> arr) /*-{
+ this[key] = arr;
+ }-*/;
+}
diff --git a/user/src/com/google/web/bindery/autobean/gwt/client/impl/JsoSplittable.java b/user/src/com/google/web/bindery/autobean/gwt/client/impl/JsoSplittable.java
new file mode 100644
index 0000000..33eba24
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/gwt/client/impl/JsoSplittable.java
@@ -0,0 +1,343 @@
+/*
+ * 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.web.bindery.autobean.gwt.client.impl;
+
+import com.google.web.bindery.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.impl.HasSplittable;
+import com.google.web.bindery.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;
+import java.util.List;
+
+/**
+ * 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 {
+ private static boolean stringifyFastTested;
+ private static boolean stringifyFastResult;
+
+ public static native JsoSplittable create() /*-{
+ return {};
+ }-*/;
+
+ public static Splittable create(boolean value) {
+ return create0(value);
+ }
+
+ public static Splittable create(double value) {
+ return create0(value);
+ }
+
+ 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) /*-{
+ return Boolean(object);
+ }-*/;
+
+ private static native Splittable create0(double object) /*-{
+ return Number(object);
+ }-*/;
+
+ private static native Splittable create0(String object) /*-{
+ return {
+ __s : object
+ };
+ }-*/;
+
+ private static native boolean isUnwrappedString(JavaScriptObject obj) /*-{
+ return Object.prototype.toString.call(obj) == '[object String]';
+ }-*/;
+
+ private static boolean stringifyFastSupported() {
+ if (stringifyFastTested) {
+ return stringifyFastResult;
+ }
+ stringifyFastTested = true;
+ return stringifyFastResult = stringifyFastSupported0();
+ }
+
+ /**
+ * Test that the JSON api is available and that it does not add function
+ * objects to the output. The test for function objects is for old versions of
+ * Safari.
+ */
+ private static native boolean stringifyFastSupported0() /*-{
+ return $wnd.JSON && $wnd.JSON.stringify && $wnd.JSON.stringify({
+ b : function() {
+ }
+ }) == '{}';
+ }-*/;
+
+ 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;
+ }-*/;
+
+ public Splittable deepCopy() {
+ return StringQuoter.split(getPayload());
+ }
+
+ public JsoSplittable get(int index) {
+ return getRaw(index);
+ }
+
+ public JsoSplittable get(String key) {
+ return getRaw(key);
+ }
+
+ public String getPayload() {
+ if (isString()) {
+ return JsonUtils.escapeValue(asString());
+ }
+ if (stringifyFastSupported()) {
+ return stringifyFast();
+ }
+ return stringifySlow();
+ }
+
+ public List<String> getPropertyKeys() {
+ List<String> toReturn = new ArrayList<String>();
+ getPropertyKeys0(toReturn);
+ 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 Object.prototype.toString.call(this) == '[object Boolean]';
+ }-*/;
+
+ public native boolean isFunction() /*-{
+ return Object.prototype.toString.call(this) == '[object Function]';
+ }-*/;
+
+ public native boolean isIndexed() /*-{
+ return Object.prototype.toString.call(this) == '[object Array]';
+ }-*/;
+
+ public boolean isKeyed() {
+ return this != NULL && !isString() && !isIndexed() && !isFunction();
+ }
+
+ public native boolean isNull(int index) /*-{
+ return this[index] == null;
+ }-*/;
+
+ public native boolean isNull(String key) /*-{
+ return this[key] == null;
+ }-*/;
+
+ public native boolean isNumber() /*-{
+ return Object.prototype.toString.call(this) == '[object Number]';
+ }-*/;
+
+ public native boolean isReified(String key) /*-{
+ return !!(this.__reified && this.__reified.hasOwnProperty(':' + key));
+ }-*/;
+
+ /**
+ * Returns whether or not the current object is a string-carrier.
+ */
+ 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;
+ }-*/;
+
+ public native int size() /*-{
+ return this.length;
+ }-*/;
+
+ private native void assign0(Splittable parent, int index, Splittable value) /*-{
+ parent[index] = value;
+ }-*/;
+
+ 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 void getPropertyKeys0(List<String> list) /*-{
+ for (key in this) {
+ if (this.hasOwnProperty(key)) {
+ list.@java.util.List::add(Ljava/lang/Object;)(key);
+ }
+ }
+ }-*/;
+
+ private native JsoSplittable getRaw(int index) /*-{
+ _ = this[index];
+ if (_ == null) {
+ return null;
+ }
+ if (@com.google.web.bindery.autobean.gwt.client.impl.JsoSplittable::isUnwrappedString(*)(_)) {
+ return @com.google.web.bindery.autobean.gwt.client.impl.JsoSplittable::create(Ljava/lang/String;)(_);
+ }
+ return Object(_);
+ }-*/;
+
+ private native JsoSplittable getRaw(String index) /*-{
+ _ = this[index];
+ if (_ == null) {
+ return null;
+ }
+ if (@com.google.web.bindery.autobean.gwt.client.impl.JsoSplittable::isUnwrappedString(*)(_)) {
+ return @com.google.web.bindery.autobean.gwt.client.impl.JsoSplittable::create(Ljava/lang/String;)(_);
+ }
+ return Object(_);
+ }-*/;
+
+ /**
+ * The test for {@code $H} removes the key in the emitted JSON, however making
+ * a similar test for {@code __reified} causes the key to be emitted with an
+ * explicit {@code null} value.
+ */
+ private native String stringifyFast() /*-{
+ return $wnd.JSON.stringify(this, function(key, value) {
+ if (key == "$H") {
+ return;
+ }
+ return value;
+ });
+ }-*/;
+
+ 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()) {
+ if ("$H".equals(key)) {
+ // Ignore hashcode
+ continue;
+ }
+ sb.append(JsonUtils.escapeValue(key));
+ sb.append(":");
+ value.stringifySlow(sb);
+ }
+ }
+ sb.append("}");
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/gwt/rebind/AutoBeanFactoryGenerator.java b/user/src/com/google/web/bindery/autobean/gwt/rebind/AutoBeanFactoryGenerator.java
new file mode 100644
index 0000000..0ec39b4
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/gwt/rebind/AutoBeanFactoryGenerator.java
@@ -0,0 +1,726 @@
+/*
+ * 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.web.bindery.autobean.gwt.rebind;
+
+import com.google.web.bindery.autobean.gwt.client.impl.AbstractAutoBeanFactory;
+import com.google.web.bindery.autobean.gwt.client.impl.ClientPropertyContext;
+import com.google.web.bindery.autobean.gwt.client.impl.JsniCreatorMap;
+import com.google.web.bindery.autobean.gwt.rebind.model.AutoBeanFactoryMethod;
+import com.google.web.bindery.autobean.gwt.rebind.model.AutoBeanFactoryModel;
+import com.google.web.bindery.autobean.gwt.rebind.model.AutoBeanMethod;
+import com.google.web.bindery.autobean.gwt.rebind.model.AutoBeanType;
+import com.google.web.bindery.autobean.gwt.rebind.model.JBeanMethod;
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory;
+import com.google.web.bindery.autobean.shared.AutoBeanUtils;
+import com.google.web.bindery.autobean.shared.AutoBeanVisitor;
+import com.google.web.bindery.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.impl.AbstractAutoBean;
+import com.google.web.bindery.autobean.shared.impl.AbstractAutoBean.OneShotContext;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.core.client.impl.WeakMapping;
+import com.google.gwt.core.ext.Generator;
+import com.google.gwt.core.ext.GeneratorContext;
+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.JParameterizedType;
+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.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.
+ */
+public class AutoBeanFactoryGenerator extends Generator {
+
+ private GeneratorContext context;
+ private String simpleSourceName;
+ private TreeLogger logger;
+ private AutoBeanFactoryModel model;
+
+ @Override
+ public String generate(TreeLogger logger, GeneratorContext context, String typeName)
+ throws UnableToCompleteException {
+ this.context = context;
+ this.logger = logger;
+
+ TypeOracle oracle = context.getTypeOracle();
+ JClassType toGenerate = oracle.findType(typeName).isInterface();
+ if (toGenerate == null) {
+ logger.log(TreeLogger.ERROR, typeName + " is not an interface type");
+ throw new UnableToCompleteException();
+ }
+
+ String packageName = toGenerate.getPackage().getName();
+ simpleSourceName = toGenerate.getName().replace('.', '_') + "Impl";
+ PrintWriter pw = context.tryCreate(logger, packageName, simpleSourceName);
+ if (pw == null) {
+ return packageName + "." + simpleSourceName;
+ }
+
+ model = new AutoBeanFactoryModel(logger, toGenerate);
+
+ ClassSourceFileComposerFactory factory =
+ new ClassSourceFileComposerFactory(packageName, simpleSourceName);
+ 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);
+
+ return factory.getCreatedClassName();
+ }
+
+ /**
+ * Flattens a parameterized type into a simple list of types.
+ */
+ private void createTypeList(List<JType> accumulator, JType type) {
+ accumulator.add(type);
+ JParameterizedType hasParams = type.isParameterized();
+ if (hasParams != null) {
+ for (JClassType arg : hasParams.getTypeArgs()) {
+ createTypeList(accumulator, arg);
+ }
+ }
+ }
+
+ private String getBaseMethodDeclaration(JMethod jmethod) {
+ // Foo foo, Bar bar, Baz baz
+ StringBuilder parameters = new StringBuilder();
+ for (JParameter param : jmethod.getParameters()) {
+ parameters.append(",").append(ModelUtils.getQualifiedBaseSourceName(param.getType())).append(
+ " ").append(param.getName());
+ }
+ if (parameters.length() > 0) {
+ parameters = parameters.deleteCharAt(0);
+ }
+
+ StringBuilder throwsDeclaration = new StringBuilder();
+ if (jmethod.getThrows().length > 0) {
+ for (JType thrown : jmethod.getThrows()) {
+ throwsDeclaration.append(". ").append(ModelUtils.getQualifiedBaseSourceName(thrown));
+ }
+ throwsDeclaration.deleteCharAt(0);
+ throwsDeclaration.insert(0, "throws ");
+ }
+ String returnName = ModelUtils.getQualifiedBaseSourceName(jmethod.getReturnType());
+ assert !returnName.contains("extends");
+ return String.format("%s %s(%s) %s", returnName, jmethod.getName(), parameters,
+ throwsDeclaration);
+ }
+
+ /**
+ * Used by {@link #writeShim} to avoid duplicate declarations of Object
+ * methods.
+ */
+ private boolean isObjectMethodImplementedByShim(JMethod jmethod) {
+ String methodName = jmethod.getName();
+ JParameter[] parameters = jmethod.getParameters();
+ switch (parameters.length) {
+ case 0:
+ return methodName.equals("hashCode") || methodName.equals("toString");
+ case 1:
+ return methodName.equals("equals")
+ && parameters[0].getType().equals(context.getTypeOracle().getJavaLangObject());
+ }
+ return false;
+ }
+
+ private void writeAutoBean(AutoBeanType type) throws UnableToCompleteException {
+ PrintWriter pw = context.tryCreate(logger, type.getPackageNome(), type.getSimpleSourceName());
+ if (pw == null) {
+ // Previously-created
+ return;
+ }
+
+ ClassSourceFileComposerFactory factory =
+ new ClassSourceFileComposerFactory(type.getPackageNome(), type.getSimpleSourceName());
+ factory.setSuperclass(AbstractAutoBean.class.getCanonicalName() + "<"
+ + type.getPeerType().getQualifiedSourceName() + ">");
+ SourceWriter sw = factory.createSourceWriter(context, pw);
+
+ writeShim(sw, type);
+
+ // Instance initializer code to set the shim's association
+ sw.println("{ %s.set(shim, %s.class.getName(), this); }", WeakMapping.class.getCanonicalName(),
+ AutoBean.class.getCanonicalName());
+
+ // Only simple wrappers have a default constructor
+ if (type.isSimpleBean()) {
+ // public FooIntfAutoBean(AutoBeanFactory factory) {}
+ sw.println("public %s(%s factory) {super(factory);}", type.getSimpleSourceName(),
+ AutoBeanFactory.class.getCanonicalName());
+ }
+
+ // 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.println("}");
+
+ // public FooIntf as() {return shim;}
+ sw.println("public %s as() {return shim;}", type.getPeerType().getQualifiedSourceName());
+
+ // public Class<Intf> getType() {return Intf.class;}
+ sw.println("public Class<%1$s> getType() {return %1$s.class;}", ModelUtils.ensureBaseType(
+ type.getPeerType()).getQualifiedSourceName());
+
+ if (type.isSimpleBean()) {
+ writeCreateSimpleBean(sw, type);
+ }
+ writeTraversal(sw, type);
+ sw.commit(logger);
+ }
+
+ /**
+ * For interfaces that consist of nothing more than getters and setters,
+ * create a map-based implementation that will allow the AutoBean's internal
+ * state to be easily consumed.
+ */
+ private void writeCreateSimpleBean(SourceWriter sw, AutoBeanType type) {
+ sw.println("@Override protected %s createSimplePeer() {", type.getPeerType()
+ .getQualifiedSourceName());
+ sw.indent();
+ // 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());
+ } else {
+ // return (ReturnType) values.getOrReify(\"foo\");
+ castType = ModelUtils.getQualifiedBaseSourceName(returnType);
+ sw.println("return (%s) getOrReify(\"%s\");", castType, 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());
+ 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)) {
+ sw.print("return ");
+ }
+ sw.print("%s.%s(%s.this", staticImpl.getEnclosingType().getQualifiedSourceName(),
+ staticImpl.getName(), type.getSimpleSourceName());
+ for (JParameter param : jmethod.getParameters()) {
+ sw.print(", %s", param.getName());
+ }
+ sw.println(");");
+ break;
+ default:
+ throw new RuntimeException();
+ }
+ sw.outdent();
+ sw.println("}");
+ }
+ sw.outdent();
+ sw.println("};");
+ sw.outdent();
+ sw.println("}");
+ }
+
+ /**
+ * Write an instance initializer block to populate the creators map.
+ */
+ private void writeDynamicMethods(SourceWriter sw) {
+ List<JClassType> privatePeers = new ArrayList<JClassType>();
+ sw.println("@Override protected void initializeCreatorMap(%s map) {", JsniCreatorMap.class
+ .getCanonicalName());
+ sw.indent();
+ for (AutoBeanType type : model.getAllTypes()) {
+ if (type.isNoWrap()) {
+ continue;
+ }
+ String classLiteralAccessor;
+ JClassType peer = type.getPeerType();
+ String peerName = ModelUtils.ensureBaseType(peer).getQualifiedSourceName();
+ if (peer.isPublic()) {
+ classLiteralAccessor = peerName + ".class";
+ } else {
+ privatePeers.add(peer);
+ classLiteralAccessor = "classLit_" + peerName.replace('.', '_') + "()";
+ }
+ // map.add(Foo.class, getConstructors_com_foo_Bar());
+ sw.println("map.add(%s, getConstructors_%s());", classLiteralAccessor, peerName.replace('.',
+ '_'));
+ }
+ sw.outdent();
+ sw.println("}");
+
+ /*
+ * Create a native method for each peer type that isn't public since Java
+ * class literal references are scoped.
+ */
+ for (JClassType peer : privatePeers) {
+ String peerName = ModelUtils.ensureBaseType(peer).getQualifiedSourceName();
+ sw.println("private native Class<?> classLit_%s() /*-{return @%s::class;}-*/;", peerName
+ .replace('.', '_'), peerName);
+ }
+
+ /*
+ * Create a method that returns an array containing references to the
+ * constructors.
+ */
+ String factoryJNIName =
+ context.getTypeOracle().findType(AutoBeanFactory.class.getCanonicalName())
+ .getJNISignature();
+ for (AutoBeanType type : model.getAllTypes()) {
+ String peerName = ModelUtils.ensureBaseType(type.getPeerType()).getQualifiedSourceName();
+ String peerJNIName = ModelUtils.ensureBaseType(type.getPeerType()).getJNISignature();
+ /*-
+ * private native JsArray<JSO> getConstructors_com_foo_Bar() {
+ * return [
+ * BarProxyImpl::new(ABFactory),
+ * BarProxyImpl::new(ABFactory, DelegateType)
+ * ];
+ * }
+ */
+ sw.println("private native %s<%s> getConstructors_%s() /*-{", JsArray.class
+ .getCanonicalName(), JavaScriptObject.class.getCanonicalName(), peerName
+ .replace('.', '_'));
+ sw.indent();
+ sw.println("return [");
+ if (type.isSimpleBean()) {
+ sw.indentln("@%s::new(%s),", type.getQualifiedSourceName(), factoryJNIName);
+ } else {
+ sw.indentln(",");
+ }
+ sw.indentln("@%s::new(%s%s)", type.getQualifiedSourceName(), factoryJNIName, peerJNIName);
+ 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();
+ // public AutoBean<Foo> foo(FooSubtype wrapped) {
+ sw.println("public %s %s(%s) {", method.getReturnType().getQualifiedSourceName(), method
+ .getName(), method.isWrapper()
+ ? (method.getWrappedType().getQualifiedSourceName() + " wrapped") : "");
+ if (method.isWrapper()) {
+ sw.indent();
+ // AutoBean<Foo> toReturn = AutoBeanUtils.getAutoBean(wrapped);
+ sw.println("%s toReturn = %s.getAutoBean(wrapped);", method.getReturnType()
+ .getParameterizedQualifiedSourceName(), AutoBeanUtils.class.getCanonicalName());
+ sw.println("if (toReturn != null) {return toReturn;}");
+ // return new FooAutoBean(Factory.this, wrapped);
+ sw.println("return new %s(%s.this, wrapped);", autoBeanType.getQualifiedSourceName(),
+ simpleSourceName);
+ sw.outdent();
+ } else {
+ // return new FooAutoBean(Factory.this);
+ sw.indentln("return new %s(%s.this);", autoBeanType.getQualifiedSourceName(),
+ simpleSourceName);
+ }
+ sw.println("}");
+ }
+ }
+
+ private void writeReturnWrapper(SourceWriter sw, AutoBeanType type, AutoBeanMethod method)
+ throws UnableToCompleteException {
+ if (!method.isValueType() && !method.isNoWrap()) {
+ JMethod jmethod = method.getMethod();
+ JClassType returnClass = jmethod.getReturnType().isClassOrInterface();
+ AutoBeanType peer = model.getPeer(returnClass);
+
+ sw.println("if (toReturn != null) {");
+ sw.indent();
+ sw.println("if (%s.this.isWrapped(toReturn)) {", type.getSimpleSourceName());
+ sw.indentln("toReturn = %s.this.getFromWrapper(toReturn);", type.getSimpleSourceName());
+ sw.println("} else {");
+ sw.indent();
+ if (peer != null) {
+ // toReturn = new FooAutoBean(getFactory(), toReturn).as();
+ sw.println("toReturn = new %s(getFactory(), toReturn).as();", peer.getQualifiedSourceName());
+ }
+ sw.outdent();
+ sw.println("}");
+
+ sw.outdent();
+ sw.println("}");
+ }
+ // Allow return values to be intercepted
+ JMethod interceptor = type.getInterceptor();
+ if (interceptor != null) {
+ // toReturn = FooCategory.__intercept(FooAutoBean.this, toReturn);
+ sw.println("toReturn = %s.%s(%s.this, toReturn);", interceptor.getEnclosingType()
+ .getQualifiedSourceName(), interceptor.getName(), type.getSimpleSourceName());
+ }
+ }
+
+ /**
+ * Create the shim instance of the AutoBean's peer type that lets us hijack
+ * the method calls. Using a shim type, as opposed to making the AutoBean
+ * implement the peer type directly, means that there can't be any conflicts
+ * between methods in the peer type and methods declared in the AutoBean
+ * implementation.
+ */
+ private void writeShim(SourceWriter sw, AutoBeanType type) throws UnableToCompleteException {
+ // private final FooImpl shim = new FooImpl() {
+ sw.println("private final %1$s shim = new %1$s() {", type.getPeerType()
+ .getQualifiedSourceName());
+ sw.indent();
+ for (AutoBeanMethod method : type.getMethods()) {
+ JMethod jmethod = method.getMethod();
+ String methodName = jmethod.getName();
+ JParameter[] parameters = jmethod.getParameters();
+ if (isObjectMethodImplementedByShim(jmethod)) {
+ // Skip any methods declared on Object, since we have special handling
+ continue;
+ }
+
+ // foo, bar, baz
+ StringBuilder arguments = new StringBuilder();
+ {
+ for (JParameter param : parameters) {
+ arguments.append(",").append(param.getName());
+ }
+ if (arguments.length() > 0) {
+ arguments = arguments.deleteCharAt(0);
+ }
+ }
+
+ sw.println("public %s {", getBaseMethodDeclaration(jmethod));
+ sw.indent();
+
+ 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);
+
+ // Non-value types might need to be wrapped
+ writeReturnWrapper(sw, type, method);
+ sw.println("return toReturn;");
+ break;
+ case SET:
+ case SET_BUILDER:
+ // getWrapped().setFoo(foo);
+ sw.println("%s.this.getWrapped().%s(%s);", type.getSimpleSourceName(), methodName,
+ parameters[0].getName());
+ // FooAutoBean.this.set("setFoo", foo);
+ sw.println("%s.this.set(\"%s\", %s);", type.getSimpleSourceName(), methodName,
+ parameters[0].getName());
+ if (JBeanMethod.SET_BUILDER.equals(method.getAction())) {
+ sw.println("return this;");
+ }
+ break;
+ case CALL:
+ // XXX How should freezing and calls work together?
+ // sw.println("checkFrozen();");
+ if (JPrimitiveType.VOID.equals(jmethod.getReturnType())) {
+ // getWrapped().doFoo(params);
+ sw.println("%s.this.getWrapped().%s(%s);", type.getSimpleSourceName(), methodName,
+ arguments);
+ // call("doFoo", null, params);
+ sw.println("%s.this.call(\"%s\", null%s %s);", type.getSimpleSourceName(), methodName,
+ arguments.length() > 0 ? "," : "", arguments);
+ } else {
+ // Type toReturn = getWrapped().doFoo(params);
+ sw.println("%s toReturn = %s.this.getWrapped().%s(%s);", ModelUtils.ensureBaseType(
+ jmethod.getReturnType()).getQualifiedSourceName(), type.getSimpleSourceName(),
+ methodName, arguments);
+ // Non-value types might need to be wrapped
+ writeReturnWrapper(sw, type, method);
+ // call("doFoo", toReturn, params);
+ sw.println("%s.this.call(\"%s\", toReturn%s %s);", type.getSimpleSourceName(),
+ methodName, arguments.length() > 0 ? "," : "", arguments);
+ sw.println("return toReturn;");
+ }
+ break;
+ default:
+ throw new RuntimeException();
+ }
+ sw.outdent();
+ sw.println("}");
+ }
+
+ // Delegate equals(), hashCode(), and toString() to wrapped object
+ sw.println("@Override public boolean equals(Object o) {");
+ sw.indentln("return this == o || getWrapped().equals(o);");
+ sw.println("}");
+ sw.println("@Override public int hashCode() {");
+ sw.indentln("return getWrapped().hashCode();");
+ sw.println("}");
+ sw.println("@Override public String toString() {");
+ sw.indentln("return getWrapped().toString();");
+ sw.println("}");
+
+ // End of shim field declaration and assignment
+ sw.outdent();
+ sw.println("};");
+ }
+
+ /**
+ * Generate traversal logic.
+ */
+ private void writeTraversal(SourceWriter sw, AutoBeanType type) {
+ List<AutoBeanMethod> referencedSetters = new ArrayList<AutoBeanMethod>();
+ sw.println("@Override protected void traverseProperties(%s visitor, %s ctx) {",
+ AutoBeanVisitor.class.getCanonicalName(), OneShotContext.class.getCanonicalName());
+ sw.indent();
+ sw.println("%s bean;", AbstractAutoBean.class.getCanonicalName());
+ sw.println("Object value;");
+ sw.println("%s propertyContext;", ClientPropertyContext.class.getCanonicalName());
+ // Local variable ref cleans up emitted js
+ sw.println("%1$s as = as();", type.getPeerType().getQualifiedSourceName());
+
+ for (AutoBeanMethod method : type.getMethods()) {
+ if (!method.getAction().equals(JBeanMethod.GET)) {
+ continue;
+ }
+
+ AutoBeanMethod setter = null;
+ // If it's not a simple bean type, try to find a real setter method
+ if (!type.isSimpleBean()) {
+ for (AutoBeanMethod maybeSetter : type.getMethods()) {
+ boolean isASetter =
+ maybeSetter.getAction().equals(JBeanMethod.SET)
+ || maybeSetter.getAction().equals(JBeanMethod.SET_BUILDER);
+ if (isASetter && maybeSetter.getPropertyName().equals(method.getPropertyName())) {
+ setter = maybeSetter;
+ break;
+ }
+ }
+ }
+
+ // The type of property influences the visitation
+ String valueExpression =
+ String.format("bean = (%1$s) %2$s.getAutoBean(as.%3$s());", AbstractAutoBean.class
+ .getCanonicalName(), AutoBeanUtils.class.getCanonicalName(), method.getMethod()
+ .getName());
+ String visitMethod;
+ String visitVariable = "bean";
+ if (method.isCollection()) {
+ visitMethod = "Collection";
+ } else if (method.isMap()) {
+ visitMethod = "Map";
+ } else if (method.isValueType()) {
+ valueExpression = String.format("value = as.%s();", method.getMethod().getName());
+ visitMethod = "Value";
+ visitVariable = "value";
+ } else {
+ visitMethod = "Reference";
+ }
+ sw.println(valueExpression);
+
+ // Map<List<Foo>, Bar> --> Map, List, Foo, Bar
+ List<JType> typeList = new ArrayList<JType>();
+ createTypeList(typeList, method.getMethod().getReturnType());
+ assert typeList.size() > 0;
+
+ /*
+ * 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.
+ */
+ // propertyContext = new CPContext(.....);
+ sw.println("propertyContext = new %s(", ClientPropertyContext.class.getCanonicalName());
+ sw.indent();
+ // The instance on which the context is nominally operating
+ sw.println("as,");
+ // Produce a JSNI reference to a setter function to call
+ {
+ if (setter != null) {
+ // Call a method that returns a JSNI reference to the method to call
+ // setFooMethodReference(),
+ sw.println("%sMethodReference(as),", setter.getMethod().getName());
+ 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());
+ }
+ }
+ if (typeList.size() == 1) {
+ sw.println("%s.class", ModelUtils.ensureBaseType(typeList.get(0)).getQualifiedSourceName());
+ } else {
+ // Produce the array of parameter types
+ sw.print("new Class<?>[] {");
+ boolean first = true;
+ for (JType lit : typeList) {
+ if (first) {
+ first = false;
+ } else {
+ sw.print(", ");
+ }
+ sw.print("%s.class", ModelUtils.ensureBaseType(lit).getQualifiedSourceName());
+ }
+ sw.println("},");
+
+ // Produce the array of parameter counts
+ sw.print("new int[] {");
+ first = true;
+ for (JType lit : typeList) {
+ if (first) {
+ first = false;
+ } else {
+ sw.print(", ");
+ }
+ JParameterizedType hasParam = lit.isParameterized();
+ if (hasParam == null) {
+ sw.print("0");
+ } else {
+ sw.print(String.valueOf(hasParam.getTypeArgs().length));
+ }
+ }
+ sw.println("}");
+ }
+ sw.outdent();
+ sw.println(");");
+
+ // if (visitor.visitReferenceProperty("foo", value, ctx))
+ sw.println("if (visitor.visit%sProperty(\"%s\", %s, propertyContext)) {", visitMethod, method
+ .getPropertyName(), visitVariable);
+ if (!method.isValueType()) {
+ // Cycle-detection in AbstractAutoBean.traverse
+ sw.indentln("if (bean != null) { bean.traverse(visitor, ctx); }");
+ }
+ sw.println("}");
+ // visitor.endVisitorReferenceProperty("foo", value, ctx);
+ sw.println("visitor.endVisit%sProperty(\"%s\", %s, propertyContext);", visitMethod, method
+ .getPropertyName(), visitVariable);
+ }
+ sw.outdent();
+ sw.println("}");
+
+ for (AutoBeanMethod method : referencedSetters) {
+ JMethod jmethod = method.getMethod();
+ assert jmethod.getParameters().length == 1;
+
+ /*-
+ * Setter setFooMethodReference(Object instance) {
+ * return instance.@com.example.Blah::setFoo(Lcom/example/Foo;);
+ * }
+ */
+ sw.println("public static native %s %sMethodReference(Object instance) /*-{",
+ ClientPropertyContext.Setter.class.getCanonicalName(), jmethod.getName());
+ sw.indentln("return instance.@%s::%s(%s);", jmethod.getEnclosingType()
+ .getQualifiedSourceName(), jmethod.getName(), jmethod.getParameters()[0].getType()
+ .getJNISignature());
+ sw.println("}-*/;");
+ }
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/gwt/rebind/model/AutoBeanFactoryMethod.java b/user/src/com/google/web/bindery/autobean/gwt/rebind/model/AutoBeanFactoryMethod.java
new file mode 100644
index 0000000..4069a57
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/gwt/rebind/model/AutoBeanFactoryMethod.java
@@ -0,0 +1,99 @@
+/*
+ * 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.web.bindery.autobean.gwt.rebind.model;
+
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+
+/**
+ * Represents a single method in an AutoBeanFactory interface.
+ */
+public class AutoBeanFactoryMethod {
+ /**
+ * Builds AutoBeanFactoryMethods.
+ */
+ public static class Builder {
+ private AutoBeanFactoryMethod toReturn = new AutoBeanFactoryMethod();
+
+ public AutoBeanFactoryMethod build() {
+ try {
+ return toReturn;
+ } finally {
+ toReturn = null;
+ }
+ }
+
+ public void setAutoBeanType(AutoBeanType type) {
+ toReturn.autoBeanType = type;
+ }
+
+ public void setMethod(JMethod method) {
+ setName(method.getName());
+ setReturnType(method.getReturnType().isClassOrInterface());
+ if (method.getParameters().length == 1) {
+ setWrappedType(method.getParameters()[0].getType().isClassOrInterface());
+ }
+ }
+
+ public void setName(String name) {
+ toReturn.name = name;
+ }
+
+ public void setReturnType(JClassType returnType) {
+ toReturn.returnType = returnType;
+ }
+
+ public void setWrappedType(JClassType wrapped) {
+ toReturn.wrappedType = wrapped;
+ }
+ }
+
+ private AutoBeanType autoBeanType;
+ private JClassType wrappedType;
+ private String name;
+ private JClassType returnType;
+
+ private AutoBeanFactoryMethod() {
+ }
+
+ public AutoBeanType getAutoBeanType() {
+ return autoBeanType;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public JClassType getReturnType() {
+ return returnType;
+ }
+
+ public JClassType getWrappedType() {
+ return wrappedType;
+ }
+
+ public boolean isWrapper() {
+ return wrappedType != null;
+ }
+
+ /**
+ * For debugging use only.
+ */
+ @Override
+ public String toString() {
+ return name;
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/gwt/rebind/model/AutoBeanFactoryModel.java b/user/src/com/google/web/bindery/autobean/gwt/rebind/model/AutoBeanFactoryModel.java
new file mode 100644
index 0000000..6122dfc
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/gwt/rebind/model/AutoBeanFactoryModel.java
@@ -0,0 +1,464 @@
+/*
+ * 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.web.bindery.autobean.gwt.rebind.model;
+
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory.Category;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory.NoWrap;
+import com.google.web.bindery.autobean.shared.impl.EnumMap.ExtraEnums;
+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.JEnumType;
+import com.google.gwt.core.ext.typeinfo.JGenericType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JParameter;
+import com.google.gwt.core.ext.typeinfo.JParameterizedType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.editor.rebind.model.ModelUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *
+ */
+public class AutoBeanFactoryModel {
+ private static final JType[] EMPTY_JTYPE = new JType[0];
+
+ 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;
+ private final List<AutoBeanFactoryMethod> methods = new ArrayList<AutoBeanFactoryMethod>();
+ private final List<JMethod> objectMethods;
+ private final TypeOracle oracle;
+ private final Map<JClassType, AutoBeanType> peers = new LinkedHashMap<JClassType, AutoBeanType>();
+ private boolean poisoned;
+
+ /**
+ * Accumulates bean types that are reachable through the type graph.
+ */
+ private Set<JClassType> toCalculate = new LinkedHashSet<JClassType>();
+
+ public AutoBeanFactoryModel(TreeLogger logger, JClassType factoryType)
+ throws UnableToCompleteException {
+ this.logger = logger;
+ oracle = factoryType.getOracle();
+ autoBeanInterface = oracle.findType(AutoBean.class.getCanonicalName()).isGenericType();
+ autoBeanFactoryInterface = oracle.findType(
+ AutoBeanFactory.class.getCanonicalName()).isInterface();
+
+ /*
+ * We want to allow the user to override some of the useful Object methods,
+ * so we'll extract them here.
+ */
+ JClassType objectType = oracle.getJavaLangObject();
+ objectMethods = Arrays.asList(
+ objectType.findMethod("equals", new JType[] {objectType}),
+ objectType.findMethod("hashCode", EMPTY_JTYPE),
+ objectType.findMethod("toString", EMPTY_JTYPE));
+
+ // Process annotations
+ {
+ Category categoryAnnotation = factoryType.getAnnotation(Category.class);
+ if (categoryAnnotation != null) {
+ categoryTypes = new ArrayList<JClassType>(
+ categoryAnnotation.value().length);
+ processClassArrayAnnotation(categoryAnnotation.value(), categoryTypes);
+ } else {
+ categoryTypes = null;
+ }
+
+ noWrapTypes = new ArrayList<JClassType>();
+ noWrapTypes.add(oracle.findType(AutoBean.class.getCanonicalName()));
+ NoWrap noWrapAnnotation = factoryType.getAnnotation(NoWrap.class);
+ if (noWrapAnnotation != null) {
+ processClassArrayAnnotation(noWrapAnnotation.value(), noWrapTypes);
+ }
+
+ ExtraEnums extraEnumsAnnotation = factoryType.getAnnotation(ExtraEnums.class);
+ if (extraEnumsAnnotation != null) {
+ for (Class<?> clazz : extraEnumsAnnotation.value()) {
+ JEnumType asEnum = oracle.findType(clazz.getCanonicalName()).isEnum();
+ assert asEnum != null;
+ for (JEnumConstant value : asEnum.getEnumConstants()) {
+ allEnumConstants.put(value, AutoBeanMethod.getEnumName(value));
+ }
+ }
+ }
+ }
+
+ for (JMethod method : factoryType.getOverridableMethods()) {
+ if (method.getEnclosingType().equals(autoBeanFactoryInterface)) {
+ // Ignore methods in AutoBeanFactory
+ continue;
+ }
+
+ JClassType returnType = method.getReturnType().isInterface();
+ if (returnType == null) {
+ poison("The return type of method %s is a primitive type",
+ method.getName());
+ continue;
+ }
+
+ // AutoBean<FooIntf> blah() --> beanType = FooIntf
+ JClassType beanType = ModelUtils.findParameterizationOf(
+ autoBeanInterface, returnType)[0];
+ if (beanType.isInterface() == null) {
+ poison("The %s parameterization is not an interface",
+ beanType.getQualifiedSourceName());
+ continue;
+ }
+
+ // AutoBean<FooIntf> blah(FooIntfSub foo) --> toWrap = FooIntfSub
+ JClassType toWrap;
+ if (method.getParameters().length == 0) {
+ toWrap = null;
+ } else if (method.getParameters().length == 1) {
+ toWrap = method.getParameters()[0].getType().isClassOrInterface();
+ if (!beanType.isAssignableFrom(toWrap)) {
+ poison(
+ "The %s parameterization %s is not assignable from the delegate"
+ + " type %s", autoBeanInterface.getSimpleSourceName(),
+ toWrap.getQualifiedSourceName());
+ continue;
+ }
+ } else {
+ poison("Unexpecetd parameters in method %s", method.getName());
+ continue;
+ }
+
+ AutoBeanType autoBeanType = getAutoBeanType(beanType);
+
+ // Must wrap things that aren't simple interfaces
+ if (!autoBeanType.isSimpleBean() && toWrap == null) {
+ if (categoryTypes != null) {
+ poison("The %s parameterization is not simple and the following"
+ + " methods did not have static implementations:",
+ beanType.getQualifiedSourceName());
+ for (AutoBeanMethod missing : autoBeanType.getMethods()) {
+ if (missing.getAction().equals(JBeanMethod.CALL)
+ && missing.getStaticImpl() == null) {
+ poison(missing.getMethod().getReadableDeclaration());
+ }
+ }
+ } else {
+ poison("The %s parameterization is not simple, but the %s method"
+ + " does not provide a delegate",
+ beanType.getQualifiedSourceName(), method.getName());
+ }
+ continue;
+ }
+
+ AutoBeanFactoryMethod.Builder builder = new AutoBeanFactoryMethod.Builder();
+ builder.setAutoBeanType(autoBeanType);
+ builder.setMethod(method);
+ methods.add(builder.build());
+ }
+
+ while (!toCalculate.isEmpty()) {
+ Set<JClassType> examine = toCalculate;
+ toCalculate = new LinkedHashSet<JClassType>();
+ for (JClassType beanType : examine) {
+ getAutoBeanType(beanType);
+ }
+ }
+
+ if (poisoned) {
+ die("Unable to complete due to previous errors");
+ }
+ }
+
+ public Collection<AutoBeanType> getAllTypes() {
+ return Collections.unmodifiableCollection(peers.values());
+ }
+
+ public List<JClassType> getCategoryTypes() {
+ return categoryTypes;
+ }
+
+ public Map<JEnumConstant, String> getEnumTokenMap() {
+ return Collections.unmodifiableMap(allEnumConstants);
+ }
+
+ public List<AutoBeanFactoryMethod> getMethods() {
+ return Collections.unmodifiableList(methods);
+ }
+
+ public AutoBeanType getPeer(JClassType beanType) {
+ beanType = ModelUtils.ensureBaseType(beanType);
+ return peers.get(beanType);
+ }
+
+ private List<AutoBeanMethod> computeMethods(JClassType beanType) {
+ List<JMethod> toExamine = new ArrayList<JMethod>();
+ toExamine.addAll(Arrays.asList(beanType.getInheritableMethods()));
+ toExamine.addAll(objectMethods);
+ List<AutoBeanMethod> toReturn = new ArrayList<AutoBeanMethod>(
+ toExamine.size());
+ for (JMethod method : toExamine) {
+ if (method.isPrivate()) {
+ // Ignore private methods
+ continue;
+ }
+ AutoBeanMethod.Builder builder = new AutoBeanMethod.Builder();
+ 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) {
+ maybeCalculate(classReturn);
+ if (noWrapTypes != null) {
+ for (JClassType noWrap : noWrapTypes) {
+ if (noWrap.isAssignableFrom(classReturn)) {
+ builder.setNoWrap(true);
+ break;
+ }
+ }
+ }
+ }
+
+ // GET, SET, or CALL
+ JBeanMethod action = JBeanMethod.which(method);
+ builder.setAction(action);
+ if (JBeanMethod.CALL.equals(action)) {
+ JMethod staticImpl = findStaticImpl(beanType, method);
+ if (staticImpl == null && objectMethods.contains(method)) {
+ // Don't complain about lack of implementation for Object methods
+ continue;
+ }
+ builder.setStaticImp(staticImpl);
+ }
+
+ AutoBeanMethod toAdd = builder.build();
+
+ // Collect referenced enums
+ if (toAdd.hasEnumMap()) {
+ 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;
+ }
+
+ private void die(String message) throws UnableToCompleteException {
+ poison(message);
+ throw new UnableToCompleteException();
+ }
+
+ /**
+ * Find <code>Object __intercept(AutoBean<?> bean, Object value);</code> in
+ * the category types.
+ */
+ private JMethod findInterceptor(JClassType beanType) {
+ if (categoryTypes == null) {
+ return null;
+ }
+ for (JClassType category : categoryTypes) {
+ for (JMethod method : category.getOverloads("__intercept")) {
+ // Ignore non-static, non-public methods
+ // TODO: Implement visibleFrom() to allow package-protected categories
+ if (!method.isStatic() || !method.isPublic()) {
+ continue;
+ }
+
+ JParameter[] params = method.getParameters();
+ if (params.length != 2) {
+ continue;
+ }
+ if (!methodAcceptsAutoBeanAsFirstParam(beanType, method)) {
+ continue;
+ }
+ JClassType value = params[1].getType().isClassOrInterface();
+ if (value == null) {
+ continue;
+ }
+ if (!oracle.getJavaLangObject().isAssignableTo(value)) {
+ continue;
+ }
+ return method;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Search the category types for a static implementation of an interface
+ * method. Given the interface method declaration:
+ *
+ * <pre>
+ * Foo bar(Baz baz);
+ * </pre>
+ *
+ * this will search the types in {@link #categoryTypes} for the following
+ * method:
+ *
+ * <pre>
+ * public static Foo bar(AutoBean<Intf> bean, Baz baz) {}
+ * </pre>
+ */
+ private JMethod findStaticImpl(JClassType beanType, JMethod method) {
+ if (categoryTypes == null) {
+ return null;
+ }
+
+ for (JClassType category : categoryTypes) {
+ // One extra argument for the AutoBean
+ JParameter[] methodParams = method.getParameters();
+ int requiredArgs = methodParams.length + 1;
+ overload : for (JMethod overload : category.getOverloads(method.getName())) {
+ if (!overload.isStatic() || !overload.isPublic()) {
+ // Ignore non-static, non-public methods
+ continue;
+ }
+
+ JParameter[] overloadParams = overload.getParameters();
+ if (overloadParams.length != requiredArgs) {
+ continue;
+ }
+
+ if (!methodAcceptsAutoBeanAsFirstParam(beanType, overload)) {
+ // Ignore if the first parameter is a primitive or not assignable
+ continue;
+ }
+
+ // Match the rest of the parameters
+ for (int i = 1; i < requiredArgs; i++) {
+ JType methodType = methodParams[i - 1].getType();
+ JType overloadType = overloadParams[i].getType();
+ if (methodType.equals(overloadType)) {
+ // Match; exact, the usual case
+ } else if (methodType.isClassOrInterface() != null
+ && overloadType.isClassOrInterface() != null
+ && methodType.isClassOrInterface().isAssignableTo(
+ overloadType.isClassOrInterface())) {
+ // Match; assignment-compatible
+ } else {
+ // No match, keep looking
+ continue overload;
+ }
+ }
+ return overload;
+ }
+ }
+ return null;
+ }
+
+ private AutoBeanType getAutoBeanType(JClassType beanType) {
+ beanType = ModelUtils.ensureBaseType(beanType);
+ AutoBeanType toReturn = peers.get(beanType);
+ if (toReturn == null) {
+ AutoBeanType.Builder builder = new AutoBeanType.Builder();
+ builder.setOwnerFactory(this);
+ builder.setPeerType(beanType);
+ builder.setMethods(computeMethods(beanType));
+ builder.setInterceptor(findInterceptor(beanType));
+ if (noWrapTypes != null) {
+ for (JClassType noWrap : noWrapTypes) {
+ if (noWrap.isAssignableFrom(beanType)) {
+ builder.setNoWrap(true);
+ break;
+ }
+ }
+ }
+ toReturn = builder.build();
+ peers.put(beanType, toReturn);
+ }
+ 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) {
+ JParameter[] params = method.getParameters();
+ if (params.length == 0) {
+ return false;
+ }
+ JClassType paramAsClass = params[0].getType().isClassOrInterface();
+
+ // First parameter is a primitive
+ if (paramAsClass == null) {
+ return false;
+ }
+
+ // Check using base types to account for erasure semantics
+ JParameterizedType expectedFirst = oracle.getParameterizedType(
+ autoBeanInterface,
+ new JClassType[] {ModelUtils.ensureBaseType(beanType)});
+ return expectedFirst.isAssignableTo(paramAsClass);
+ }
+
+ private void poison(String message, Object... args) {
+ logger.log(TreeLogger.ERROR, String.format(message, args));
+ poisoned = true;
+ }
+
+ private void processClassArrayAnnotation(Class<?>[] classes,
+ Collection<JClassType> accumulator) {
+ for (Class<?> clazz : classes) {
+ JClassType category = oracle.findType(clazz.getCanonicalName());
+ if (category == null) {
+ poison("Could not find @%s type %s in the TypeOracle",
+ Category.class.getSimpleName(), clazz.getCanonicalName());
+ continue;
+ } else if (!category.isPublic()) {
+ poison("Category type %s is not public",
+ category.getQualifiedSourceName());
+ continue;
+ } else if (!category.isStatic() && category.isMemberType()) {
+ poison("Category type %s must be static",
+ category.getQualifiedSourceName());
+ continue;
+ }
+ accumulator.add(category);
+ }
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/gwt/rebind/model/AutoBeanMethod.java b/user/src/com/google/web/bindery/autobean/gwt/rebind/model/AutoBeanMethod.java
new file mode 100644
index 0000000..43763df
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/gwt/rebind/model/AutoBeanMethod.java
@@ -0,0 +1,219 @@
+/*
+ * 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.web.bindery.autobean.gwt.rebind.model;
+
+import com.google.web.bindery.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.JType;
+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;
+
+/**
+ * Describes a method implemented by an AutoBean.
+ */
+public class AutoBeanMethod {
+ /**
+ * Creates AutoBeanMethods.
+ */
+ public static class Builder {
+ private AutoBeanMethod toReturn = new AutoBeanMethod();
+
+ public AutoBeanMethod build() {
+ if (toReturn.action.equals(JBeanMethod.GET)
+ || toReturn.action.equals(JBeanMethod.SET)
+ || toReturn.action.equals(JBeanMethod.SET_BUILDER)) {
+ PropertyName annotation = toReturn.method.getAnnotation(PropertyName.class);
+ if (annotation != null) {
+ toReturn.propertyName = annotation.value();
+ } else {
+ toReturn.propertyName = toReturn.action.inferName(toReturn.method);
+ }
+ }
+
+ try {
+ return toReturn;
+ } finally {
+ toReturn = null;
+ }
+ }
+
+ public void setAction(JBeanMethod action) {
+ toReturn.action = action;
+ }
+
+ public void setMethod(JMethod method) {
+ toReturn.method = method;
+ TypeOracle oracle = method.getEnclosingType().getOracle();
+
+ JType returnType = method.getReturnType();
+ toReturn.isValueType = ModelUtils.isValueType(oracle, returnType);
+
+ if (!toReturn.isValueType) {
+ // See if it's a collection or a map
+ JClassType returnClass = returnType.isClassOrInterface();
+ JClassType collectionInterface = oracle.findType(Collection.class.getCanonicalName());
+ JClassType mapInterface = oracle.findType(Map.class.getCanonicalName());
+ if (collectionInterface.isAssignableFrom(returnClass)) {
+ JClassType[] parameterizations = ModelUtils.findParameterizationOf(
+ collectionInterface, returnClass);
+ toReturn.elementType = parameterizations[0];
+ maybeProcessEnumType(toReturn.elementType);
+ } else if (mapInterface.isAssignableFrom(returnClass)) {
+ JClassType[] parameterizations = ModelUtils.findParameterizationOf(
+ mapInterface, returnClass);
+ toReturn.keyType = parameterizations[0];
+ toReturn.valueType = parameterizations[1];
+ maybeProcessEnumType(toReturn.keyType);
+ maybeProcessEnumType(toReturn.valueType);
+ }
+ } else {
+ maybeProcessEnumType(returnType);
+ }
+ }
+
+ public void setNoWrap(boolean noWrap) {
+ toReturn.isNoWrap = noWrap;
+ }
+
+ public void setStaticImp(JMethod staticImpl) {
+ toReturn.staticImpl = staticImpl;
+ }
+
+ /**
+ * Call {@link #processEnumType(JEnumType)} if {@code type} is a
+ * {@link JEnumType}.
+ */
+ private void maybeProcessEnumType(JType type) {
+ assert type != null : "type == null";
+ JEnumType enumType = type.isEnum();
+ if (enumType != null) {
+ processEnumType(enumType);
+ }
+ }
+
+ /**
+ * Adds a JEnumType to the AutoBeanMethod's enumMap so that the
+ * AutoBeanFactoryGenerator can embed extra metadata about the enum values.
+ */
+ private void processEnumType(JEnumType enumType) {
+ Map<JEnumConstant, String> map = toReturn.enumMap;
+ if (map == null) {
+ map = toReturn.enumMap = new LinkedHashMap<JEnumConstant, String>();
+ }
+ for (JEnumConstant e : enumType.getEnumConstants()) {
+ String name = getEnumName(e);
+ map.put(e, name);
+ }
+ }
+ }
+
+ static String getEnumName(JEnumConstant e) {
+ String name;
+ PropertyName annotation = e.getAnnotation(PropertyName.class);
+ if (annotation == null) {
+ name = e.getName();
+ } else {
+ name = annotation.value();
+ }
+ return name;
+ }
+
+ private JBeanMethod action;
+ private JClassType elementType;
+ private Map<JEnumConstant, String> enumMap;
+ private JClassType keyType;
+ private JMethod method;
+ private boolean isNoWrap;
+ private boolean isValueType;
+ private String propertyName;
+ private JMethod staticImpl;
+ private JClassType valueType;
+
+ private AutoBeanMethod() {
+ }
+
+ public JBeanMethod getAction() {
+ return action;
+ }
+
+ public JClassType getElementType() {
+ return elementType;
+ }
+
+ public Map<JEnumConstant, String> getEnumMap() {
+ return enumMap;
+ }
+
+ public JClassType getKeyType() {
+ return keyType;
+ }
+
+ public JMethod getMethod() {
+ return method;
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ /**
+ * If the AutoBean method was declared in a type containing a
+ * {@link com.google.gwt.editor.client.AutoBean.Category Category} annotation,
+ * this method will return the static implementation.
+ */
+ public JMethod getStaticImpl() {
+ return staticImpl;
+ }
+
+ public JClassType getValueType() {
+ return valueType;
+ }
+
+ public boolean hasEnumMap() {
+ return enumMap != null;
+ }
+
+ public boolean isCollection() {
+ return elementType != null;
+ }
+
+ public boolean isMap() {
+ return keyType != null;
+ }
+
+ public boolean isNoWrap() {
+ return isNoWrap;
+ }
+
+ public boolean isValueType() {
+ return isValueType;
+ }
+
+ /**
+ * For debugging use only.
+ */
+ @Override
+ public String toString() {
+ return method.toString();
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/gwt/rebind/model/AutoBeanType.java b/user/src/com/google/web/bindery/autobean/gwt/rebind/model/AutoBeanType.java
new file mode 100644
index 0000000..da40afe
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/gwt/rebind/model/AutoBeanType.java
@@ -0,0 +1,168 @@
+/*
+ * 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.web.bindery.autobean.gwt.rebind.model;
+
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Describes an AutoBean.
+ */
+public class AutoBeanType {
+
+ /**
+ * Builder.
+ */
+ public static class Builder {
+ private boolean affectedByCategories;
+ private String beanSimpleSourceName;
+ private String categorySuffix;
+ private AutoBeanType toReturn = new AutoBeanType();
+
+ public AutoBeanType build() {
+ // Different implementations necessary for category-affected impls
+ toReturn.simpleSourceName = beanSimpleSourceName
+ + (affectedByCategories ? categorySuffix : "");
+ try {
+ return toReturn;
+ } finally {
+ toReturn = null;
+ }
+ }
+
+ public void setInterceptor(JMethod interceptor) {
+ affectedByCategories = interceptor != null;
+ toReturn.interceptor = interceptor;
+ }
+
+ public void setMethods(List<AutoBeanMethod> methods) {
+ toReturn.methods = new ArrayList<AutoBeanMethod>(methods);
+ Collections.sort(toReturn.methods, new Comparator<AutoBeanMethod>() {
+ public int compare(AutoBeanMethod o1, AutoBeanMethod o2) {
+ int c = o1.getAction().compareTo(o2.getAction());
+ if (c != 0) {
+ return c;
+ }
+ // Name alone would cause overload conflicts
+ return o1.getMethod().getReadableDeclaration().compareTo(
+ o2.getMethod().getReadableDeclaration());
+ }
+ });
+ toReturn.methods = Collections.unmodifiableList(toReturn.methods);
+
+ toReturn.simpleBean = true;
+ for (AutoBeanMethod method : methods) {
+ if (method.getAction().equals(JBeanMethod.CALL)) {
+ if (method.getStaticImpl() == null) {
+ toReturn.simpleBean = false;
+ } else {
+ affectedByCategories = true;
+ }
+ }
+ }
+ }
+
+ public void setNoWrap(boolean noWrap) {
+ toReturn.noWrap = noWrap;
+ }
+
+ public void setOwnerFactory(AutoBeanFactoryModel autoBeanFactoryModel) {
+ if (autoBeanFactoryModel.getCategoryTypes() == null) {
+ return;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (JClassType category : autoBeanFactoryModel.getCategoryTypes()) {
+ sb.append("_").append(
+ category.getQualifiedSourceName().replace('.', '_'));
+ }
+ categorySuffix = sb.toString();
+ }
+
+ public void setPeerType(JClassType type) {
+ assert type.isParameterized() == null && type.isRawType() == null;
+ toReturn.peerType = type;
+ String packageName = type.getPackage().getName();
+ if (packageName.startsWith("java")) {
+ packageName = "emul." + packageName;
+ }
+ toReturn.packageName = packageName;
+ beanSimpleSourceName = type.getName().replace('.', '_') + "AutoBean";
+ }
+ }
+
+ private JMethod interceptor;
+ private List<AutoBeanMethod> methods;
+ private boolean noWrap;
+ private String packageName;
+ private JClassType peerType;
+ private boolean simpleBean;
+ private String simpleSourceName;
+
+ private AutoBeanType() {
+ }
+
+ /**
+ * A method that is allowed to intercept and modify return values from
+ * getters.
+ */
+ public JMethod getInterceptor() {
+ return interceptor;
+ }
+
+ public List<AutoBeanMethod> getMethods() {
+ return methods;
+ }
+
+ public String getPackageNome() {
+ return packageName;
+ }
+
+ public JClassType getPeerType() {
+ return peerType;
+ }
+
+ public String getQualifiedSourceName() {
+ return getPackageNome() + "." + getSimpleSourceName();
+ }
+
+ public String getSimpleSourceName() {
+ return simpleSourceName;
+ }
+
+ public boolean isNoWrap() {
+ return noWrap;
+ }
+
+ /**
+ * A simple bean has only getters and setters.
+ */
+ public boolean isSimpleBean() {
+ return simpleBean;
+ }
+
+ /**
+ * For debugging use only.
+ */
+ @Override
+ public String toString() {
+ return peerType.toString();
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/gwt/rebind/model/JBeanMethod.java b/user/src/com/google/web/bindery/autobean/gwt/rebind/model/JBeanMethod.java
new file mode 100644
index 0000000..d600c19
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/gwt/rebind/model/JBeanMethod.java
@@ -0,0 +1,154 @@
+/*
+ * 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.web.bindery.autobean.gwt.rebind.model;
+
+import static com.google.web.bindery.autobean.vm.impl.BeanMethod.GET_PREFIX;
+import static com.google.web.bindery.autobean.vm.impl.BeanMethod.HAS_PREFIX;
+import static com.google.web.bindery.autobean.vm.impl.BeanMethod.IS_PREFIX;
+import static com.google.web.bindery.autobean.vm.impl.BeanMethod.SET_PREFIX;
+
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JType;
+
+import java.beans.Introspector;
+
+/**
+ * Common utility code for matching {@link JMethod} and against bean-style
+ * accessor semantics.
+ *
+ * @see com.google.web.bindery.autobean.vm.impl.BeanMethod
+ */
+public enum JBeanMethod {
+ GET {
+ @Override
+ public String inferName(JMethod method) {
+ if (isBooleanProperty(method) && method.getName().startsWith(IS_PREFIX)) {
+ return Introspector.decapitalize(method.getName().substring(2));
+ }
+ return super.inferName(method);
+ }
+
+ @Override
+ public boolean matches(JMethod method) {
+ if (method.getParameters().length > 0) {
+ return false;
+ }
+
+ if (isBooleanProperty(method)) {
+ return true;
+ }
+
+ String name = method.getName();
+ if (name.startsWith(GET_PREFIX) && name.length() > 3) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns {@code true} if the method matches {@code boolean isFoo()} or
+ * {@code boolean hasFoo()} property accessors.
+ */
+ private boolean isBooleanProperty(JMethod method) {
+ JType returnType = method.getReturnType();
+ if (JPrimitiveType.BOOLEAN.equals(returnType)
+ || method.getEnclosingType().getOracle().findType(
+ Boolean.class.getCanonicalName()).equals(returnType)) {
+ String name = method.getName();
+ if (name.startsWith(IS_PREFIX) && name.length() > 2) {
+ return true;
+ }
+ if (name.startsWith(HAS_PREFIX) && name.length() > 3) {
+ return true;
+ }
+ }
+ return false;
+ }
+ },
+ SET {
+ @Override
+ public 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_PREFIX) && name.length() > 3) {
+ return true;
+ }
+ return false;
+ }
+ },
+ SET_BUILDER {
+ @Override
+ public boolean matches(JMethod method) {
+ JClassType returnClass = method.getReturnType().isClassOrInterface();
+ if (returnClass == null
+ || !returnClass.isAssignableFrom(method.getEnclosingType())) {
+ return false;
+ }
+ if (method.getParameters().length != 1) {
+ return false;
+ }
+ String name = method.getName();
+ if (name.startsWith(SET_PREFIX) && name.length() > 3) {
+ return true;
+ }
+ return false;
+ }
+ },
+ CALL {
+ /**
+ * Matches all leftover methods.
+ */
+ @Override
+ public boolean matches(JMethod method) {
+ return true;
+ }
+ };
+
+ /**
+ * Determine which Action a method maps to.
+ */
+ public static JBeanMethod which(JMethod method) {
+ for (JBeanMethod action : JBeanMethod.values()) {
+ if (action.matches(method)) {
+ return action;
+ }
+ }
+ throw new RuntimeException("CALL should have matched");
+ }
+
+ /**
+ * Infer the name of a property from the method.
+ */
+ public String inferName(JMethod method) {
+ if (this == CALL) {
+ throw new UnsupportedOperationException(
+ "Cannot infer a property name for a CALL-type method");
+ }
+ return Introspector.decapitalize(method.getName().substring(3));
+ }
+
+ /**
+ * Returns {@code true} if the BeanLikeMethod matches the method.
+ */
+ public abstract boolean matches(JMethod method);
+}
diff --git a/user/src/com/google/web/bindery/autobean/shared/AutoBean.java b/user/src/com/google/web/bindery/autobean/shared/AutoBean.java
new file mode 100644
index 0000000..9aa94fc
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/shared/AutoBean.java
@@ -0,0 +1,141 @@
+/*
+ * 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.web.bindery.autobean.shared;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A controller for an implementation of a bean interface. Instances of
+ * AutoBeans are obtained from an {@link AutoBeanFactory}.
+ *
+ * @param <T> the type of interface that will be wrapped.
+ */
+public interface AutoBean<T> {
+ /**
+ * An annotation that allows inferred property names to be overridden.
+ * <p>
+ * This annotation is asymmetric, applying it to a getter will not affect the
+ * setter. The asymmetry allows existing users of an interface to read old
+ * {@link AutoBeanCodex} messages, but write new ones.
+ */
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
+ public @interface PropertyName {
+ String value();
+ }
+
+ /**
+ * Accept an AutoBeanVisitor.
+ *
+ * @param visitor an {@link AutoBeanVisitor}
+ */
+ void accept(AutoBeanVisitor visitor);
+
+ /**
+ * Returns a proxy implementation of the <code>T</code> interface which will
+ * delegate to the underlying wrapped object, if any.
+ *
+ * @return a proxy that delegates to the wrapped object
+ */
+ 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.
+ * <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}.
+ *
+ * @throws UnsupportedOperationException
+ * @deprecated with no replacement
+ */
+ @Deprecated
+ 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)}.
+ *
+ * @param tagName the tag name
+ * @return the tag value
+ * @see #setTag(String, Object)
+ */
+ <Q> Q getTag(String tagName);
+
+ /**
+ * Returns the wrapped interface type.
+ */
+ Class<T> getType();
+
+ /**
+ * Returns the value most recently passed to {@link #setFrozen}, or
+ * {@code false} if it has never been called.
+ *
+ * @return {@code true} if this instance is frozen
+ */
+ boolean isFrozen();
+
+ /**
+ * Returns {@code true} if the AutoBean was provided with an external object.
+ *
+ * @return {@code true} if this instance is a wrapper
+ */
+ boolean isWrapper();
+
+ /**
+ * Disallows any method calls other than getters. All setter and call
+ * operations will throw an {@link IllegalStateException}.
+ *
+ * @param frozen if {@code true}, freeze this instance
+ */
+ void setFrozen(boolean frozen);
+
+ /**
+ * A tag is an arbitrary piece of external metadata to be associated with the
+ * wrapped value.
+ *
+ * @param tagName the tag name
+ * @param value the wrapped value
+ * @see #getTag(String)
+ */
+ void setTag(String tagName, Object value);
+
+ /**
+ * If the AutoBean wraps an object, return the underlying object. The AutoBean
+ * will no longer function once unwrapped.
+ *
+ * @return the previously-wrapped object
+ * @throws IllegalStateException if the AutoBean is not a wrapper
+ */
+ T unwrap();
+}
diff --git a/user/src/com/google/web/bindery/autobean/shared/AutoBeanCodex.java b/user/src/com/google/web/bindery/autobean/shared/AutoBeanCodex.java
new file mode 100644
index 0000000..2f07b1a
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/shared/AutoBeanCodex.java
@@ -0,0 +1,86 @@
+/*
+ * 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.web.bindery.autobean.shared;
+
+import com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl;
+import com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl.EncodeState;
+import com.google.web.bindery.autobean.shared.impl.StringQuoter;
+
+/**
+ * Utility methods for encoding an AutoBean graph into a JSON-compatible string.
+ * This codex intentionally does not preserve object identity, nor does it
+ * encode cycles, but it will detect them.
+ */
+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
+ */
+ public static <T> AutoBean<T> decode(AutoBeanFactory factory, Class<T> clazz, Splittable data) {
+ return AutoBeanCodexImpl.doDecode(EncodeState.forDecode(factory), clazz, data);
+ }
+
+ /**
+ * 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 payload a payload string previously generated by
+ * {@link #encode(AutoBean)}{@link Splittable#getPayload()
+ * .getPayload()}.
+ * @return an AutoBean containing the payload contents
+ */
+ public static <T> AutoBean<T> decode(AutoBeanFactory factory, Class<T> clazz, String payload) {
+ Splittable data = StringQuoter.split(payload);
+ return decode(factory, clazz, data);
+ }
+
+ /**
+ * Copy data from a {@link Splittable} into an AutoBean. Unset values in the
+ * Splittable will not nullify data that already exists in the AutoBean.
+ *
+ * @param data the source data to copy
+ * @param bean the target AutoBean
+ */
+ public static void decodeInto(Splittable data, AutoBean<?> bean) {
+ AutoBeanCodexImpl.doDecodeInto(EncodeState.forDecode(bean.getFactory()), data, bean);
+ }
+
+ /**
+ * Encodes an AutoBean. The actual payload contents can be retrieved through
+ * {@link Splittable#getPayload()}.
+ *
+ * @param bean the bean to encode
+ * @return a Splittable that encodes the state of the AutoBean
+ */
+ public static Splittable encode(AutoBean<?> bean) {
+ if (bean == null) {
+ return Splittable.NULL;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ EncodeState state = EncodeState.forEncode(bean.getFactory(), sb);
+ AutoBeanCodexImpl.doEncode(state, bean);
+ return StringQuoter.split(sb.toString());
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/shared/AutoBeanFactory.java b/user/src/com/google/web/bindery/autobean/shared/AutoBeanFactory.java
new file mode 100644
index 0000000..44496a5
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/shared/AutoBeanFactory.java
@@ -0,0 +1,120 @@
+/*
+ * 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.web.bindery.autobean.shared;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A tag interface for the AutoBean generator. Instances of AutoBeans are
+ * created by declaring factory methods on a subtype of this interface.
+ * <p>
+ * Simple interfaces, consisting of only getters and setters, can be constructed
+ * with a no-arg method. Non-simple interfaces must provide a delegate object to
+ * implement a non-simple interface or use a {@link Category}.
+ *
+ * <pre>
+ * interface MyFactory extends AutoBeanFactory {
+ * // A factory method for a simple bean
+ * AutoBean<BeanInterface> beanInterface();
+ * // A factory method for a wrapper bean
+ * AutoBean<ArbitraryInterface> wrapper(ArbitraryInterface delegate);
+ * }
+ * </pre>
+ *
+ * @see <a
+ * href="http://code.google.com/p/google-web-toolkit/wiki/AutoBean">AutoBean
+ * wiki page</a>
+ */
+public interface AutoBeanFactory {
+ /**
+ * Allows non-property methods on simple bean implementations when applied.
+ * For any given method, the specified classes will be searched for a public,
+ * static method whose method signature is exactly equal to the declared
+ * method's signature, save for the addition of a new initial paramater that
+ * must accept <code>AutoBean<T></code>.
+ *
+ * <pre>
+ * interface HasMethod {
+ * void doSomething(int a, double b);
+ * }
+ * </pre>
+ *
+ * would be paired with a category implemenation such as
+ *
+ * <pre>
+ * class HasMethodCategory {
+ * public static void doSomething(AutoBean<HasMethod> bean, int a, double b) {
+ * }
+ * }
+ * </pre>
+ *
+ * and registered with
+ *
+ * <pre>
+ * {@literal @}Category(HasMethodCategory.class)
+ * interface MyBeanFactory extends AutoBeanFactory {
+ * AutoBean<HasMethod> hasMethod();
+ * }
+ * </pre>
+ */
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.TYPE)
+ public @interface Category {
+ Class<?>[] value();
+ }
+
+ /**
+ * The types specified by this annotation will not be wrapped by an AutoBean
+ * when returned from an AutoBean-controlled method.
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.TYPE)
+ public @interface NoWrap {
+ /**
+ * The interface types that should not be wrapped.
+ */
+ Class<?>[] value();
+ }
+
+ /**
+ * Allows dynamic creation of AutoBean instances based on declared
+ * parameterizations.
+ *
+ * @param <T> the parameterization of the created {@link AutoBean}
+ * @param clazz the Class of type T of the new instance
+ * @return an {@link AutoBean} of type T or {@code null} if the interface type
+ * is unknown to the factory
+ */
+ <T> AutoBean<T> create(Class<T> clazz);
+
+ /**
+ * Allows dynamic creation of wrapped AutoBean instances based on declared
+ * parameterizations.
+ *
+ * @param <T> the parameterization of the created {@link AutoBean}
+ * @param <U> the delegate's type, a subtype of T
+ * @param clazz the Class of type T of the new instance
+ * @param delegate a delegate that extends type T
+ * @return an {@link AutoBean} of type T or {@code null} if the interface type
+ * is unknown to the factory
+ */
+ <T, U extends T> AutoBean<T> create(Class<T> clazz, U delegate);
+}
diff --git a/user/src/com/google/web/bindery/autobean/shared/AutoBeanUtils.java b/user/src/com/google/web/bindery/autobean/shared/AutoBeanUtils.java
new file mode 100644
index 0000000..e97c438
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/shared/AutoBeanUtils.java
@@ -0,0 +1,460 @@
+/*
+ * 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.web.bindery.autobean.shared;
+
+import com.google.gwt.core.client.impl.WeakMapping;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility methods for working with AutoBeans.
+ */
+public final class AutoBeanUtils {
+ /*
+ * TODO(bobv): Make Comparison a real type that holds a map contain the diff
+ * between the two objects. Then export a Map of PendingComparison to
+ * Comparisons as a public API to make it easy for developers to perform deep
+ * diffs across a graph structure.
+ *
+ * Three-way merge...
+ */
+
+ private enum Comparison {
+ TRUE, FALSE, PENDING;
+ }
+
+ /**
+ * A Pair where order does not matter and the objects are compared by
+ * identity.
+ */
+ private static class PendingComparison {
+ private final AutoBean<?> a;
+ private final AutoBean<?> b;
+ private final int hashCode;
+
+ public PendingComparison(AutoBean<?> a, AutoBean<?> b) {
+ this.a = a;
+ this.b = b;
+ // Don't make relatively prime since order does not matter
+ hashCode = System.identityHashCode(a) + System.identityHashCode(b);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof PendingComparison)) {
+ return false;
+ }
+ PendingComparison other = (PendingComparison) o;
+ return a == other.a && b == other.b || // Direct match
+ a == other.b && b == other.a; // Swapped
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+ }
+
+ /**
+ * Compare two graphs of AutoBeans based on values.
+ * <p>
+ * <ul>
+ * <li>AutoBeans are compared based on type and property values</li>
+ * <li>Lists are compared with element-order equality</li>
+ * <li>Sets and all other Collection types are compare with bag equality</li>
+ * <li>Maps are compared as a lists of keys-value pairs</li>
+ * <li>{@link Splittable Splittables} are compared by value</li>
+ * </ul>
+ * <p>
+ * This will work for both simple and wrapper AutoBeans.
+ * <p>
+ * This method may crawl the entire object graph reachable from the input
+ * parameters and may be arbitrarily expensive to compute.
+ *
+ * @param a an {@link AutoBean}
+ * @param b an {@link AutoBean}
+ * @return {@code false} if any values in the graph reachable through
+ * <code>a</code> are different from those reachable from
+ * <code>b</code>
+ */
+ public static boolean deepEquals(AutoBean<?> a, AutoBean<?> b) {
+ return sameOrEquals(a, b, new HashMap<PendingComparison, Comparison>());
+ }
+
+ /**
+ * Returns a map of properties that differ (via {@link Object#equals(Object)})
+ * between two AutoBeans. The keys are property names and the values are the
+ * value of the property in <code>b</code>. Properties present in
+ * <code>a</code> but missing in <code>b</code> will be represented by
+ * <code>null</code> values. This implementation will compare AutoBeans of
+ * different parameterizations, although the diff produced is likely
+ * meaningless.
+ * <p>
+ * This will work for both simple and wrapper AutoBeans.
+ *
+ * @param a an {@link AutoBean}
+ * @param b an {@link AutoBean}
+ * @return a {@link Map} of differing properties
+ */
+ public static Map<String, Object> diff(AutoBean<?> a, AutoBean<?> b) {
+ // Fast check for comparing an object to itself
+ if (a.equals(b)) {
+ return Collections.emptyMap();
+ }
+ final Map<String, Object> toReturn = getAllProperties(b);
+
+ // Remove the entries that are equal, adding nulls for missing properties
+ a.accept(new AutoBeanVisitor() {
+ @Override
+ public boolean visitReferenceProperty(String propertyName, AutoBean<?> previousValue,
+ PropertyContext ctx) {
+ if (toReturn.containsKey(propertyName)) {
+ if (equal(propertyName, previousValue)) {
+ // No change
+ toReturn.remove(propertyName);
+ }
+ } else {
+ // The predecessor has a value that this object doesn't.
+ toReturn.put(propertyName, null);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visitValueProperty(String propertyName, Object previousValue,
+ PropertyContext ctx) {
+ if (toReturn.containsKey(propertyName)) {
+ if (equal(propertyName, previousValue)) {
+ // No change
+ toReturn.remove(propertyName);
+ }
+ } else {
+ // The predecessor has a value that this object doesn't.
+ toReturn.put(propertyName, null);
+ }
+ return false;
+ }
+
+ private boolean equal(String propertyName, AutoBean<?> previousValue) {
+ return previousValue == null && toReturn.get(propertyName) == null || previousValue != null
+ && equal(propertyName, previousValue.as());
+ }
+
+ private boolean equal(String propertyName, Object previousValue) {
+ Object currentValue = toReturn.get(propertyName);
+ return previousValue == null && currentValue == null || previousValue != null
+ && previousValue.equals(currentValue);
+ }
+ });
+ return toReturn;
+ }
+
+ /**
+ * Returns a map that is a copy of the properties contained in an AutoBean.
+ * The returned map is mutable, but editing it will not have any effect on the
+ * bean that produced it.
+ *
+ * @param bean an {@link AutoBean}
+ * @return a {@link Map} of the bean's properties
+ */
+ public static Map<String, Object> getAllProperties(AutoBean<?> bean) {
+ final Map<String, Object> toReturn = new LinkedHashMap<String, Object>();
+
+ // Look at the previous value of all properties
+ bean.accept(new AutoBeanVisitor() {
+ @Override
+ public boolean visitReferenceProperty(String propertyName, AutoBean<?> value,
+ PropertyContext ctx) {
+ toReturn.put(propertyName, value == null ? null : value.as());
+ return false;
+ }
+
+ @Override
+ public boolean visitValueProperty(String propertyName, Object value, PropertyContext ctx) {
+ toReturn.put(propertyName, value);
+ return false;
+ }
+ });
+ return toReturn;
+ }
+
+ /**
+ * Return the single AutoBean wrapper that is observing the delegate object or
+ * {@code null} if the parameter is {@code null}or not wrapped by an AutoBean.
+ *
+ * @param delegate a delegate object, or {@code null}
+ * @return the {@link AutoBean} wrapper for the delegate, or {@code null}
+ */
+ @SuppressWarnings("unchecked")
+ public static <T, U extends T> AutoBean<T> getAutoBean(U delegate) {
+ return delegate == null ? null : (AutoBean<T>) WeakMapping.get(delegate, AutoBean.class
+ .getName());
+ }
+
+ /**
+ * Compare two AutoBeans, this method has the type fan-out.
+ */
+ static boolean sameOrEquals(Object value, Object otherValue,
+ Map<PendingComparison, Comparison> pending) {
+ if (value == otherValue) {
+ // Fast exit
+ return true;
+ }
+
+ if (value instanceof Collection<?> && otherValue instanceof Collection<?>) {
+ // Check collections
+ return sameOrEquals((Collection<?>) value, (Collection<?>) otherValue, pending, null);
+ }
+
+ if (value instanceof Map<?, ?> && otherValue instanceof Map<?, ?>) {
+ // Check maps
+ return sameOrEquals((Map<?, ?>) value, (Map<?, ?>) otherValue, pending);
+ }
+
+ if (value instanceof Splittable && otherValue instanceof Splittable) {
+ return sameOrEquals((Splittable) value, (Splittable) otherValue, pending);
+ }
+
+ // Possibly substitute the AutoBean for its shim
+ {
+ AutoBean<?> maybeValue = AutoBeanUtils.getAutoBean(value);
+ AutoBean<?> maybeOther = AutoBeanUtils.getAutoBean(otherValue);
+ if (maybeValue != null && maybeOther != null) {
+ value = maybeValue;
+ otherValue = maybeOther;
+ }
+ }
+
+ if (value instanceof AutoBean<?> && otherValue instanceof AutoBean<?>) {
+ // Check ValueProxies
+ return sameOrEquals((AutoBean<?>) value, (AutoBean<?>) otherValue, pending);
+ }
+
+ if (value == null ^ otherValue == null) {
+ // One is null, the other isn't
+ return false;
+ }
+
+ if (value != null && !value.equals(otherValue)) {
+ // Regular object equality
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * If a comparison between two AutoBeans is currently pending, this method
+ * will skip their comparison.
+ */
+ private static boolean sameOrEquals(AutoBean<?> value, AutoBean<?> otherValue,
+ Map<PendingComparison, Comparison> pending) {
+ if (value == otherValue) {
+ // Simple case
+ return true;
+ } else if (!value.getType().equals(otherValue.getType())) {
+ // Beans of different types
+ return false;
+ }
+
+ /*
+ * The PendingComparison key allows us to break reference cycles when
+ * crawling the graph. Since the entire operation is essentially a
+ * concatenated && operation, it's ok to speculatively return true for
+ * repeated a.equals(b) tests.
+ */
+ PendingComparison key = new PendingComparison(value, otherValue);
+ Comparison previous = pending.get(key);
+ if (previous == null) {
+ // Prevent the same comparison from being made
+ pending.put(key, Comparison.PENDING);
+
+ // Compare each property
+ Map<String, Object> beanProperties = AutoBeanUtils.getAllProperties(value);
+ Map<String, Object> otherProperties = AutoBeanUtils.getAllProperties(otherValue);
+ for (Map.Entry<String, Object> entry : beanProperties.entrySet()) {
+ Object property = entry.getValue();
+ Object otherProperty = otherProperties.get(entry.getKey());
+ if (!sameOrEquals(property, otherProperty, pending)) {
+ pending.put(key, Comparison.FALSE);
+ return false;
+ }
+ }
+ pending.put(key, Comparison.TRUE);
+ return true;
+ } else {
+ // Return true for TRUE or PENDING
+ return !Comparison.FALSE.equals(previous);
+ }
+ }
+
+ /**
+ * Compare two collections by size, then by contents. List comparisons will
+ * preserve order. All other collections will be treated with bag semantics.
+ */
+ private static boolean sameOrEquals(Collection<?> collection, Collection<?> otherCollection,
+ Map<PendingComparison, Comparison> pending, Map<Object, Object> pairs) {
+ if (collection.size() != otherCollection.size()) {
+ return false;
+ }
+
+ if (collection instanceof List<?>) {
+ // Lists we can simply iterate over
+ Iterator<?> it = collection.iterator();
+ Iterator<?> otherIt = otherCollection.iterator();
+ while (it.hasNext()) {
+ assert otherIt.hasNext();
+ Object element = it.next();
+ Object otherElement = otherIt.next();
+ if (!sameOrEquals(element, otherElement, pending)) {
+ return false;
+ }
+ if (pairs != null) {
+ pairs.put(element, otherElement);
+ }
+ }
+ } else {
+ // Do an n*m comparison on any other collection type
+ List<Object> values = new ArrayList<Object>(collection);
+ List<Object> otherValues = new ArrayList<Object>(otherCollection);
+ it : for (Iterator<Object> it = values.iterator(); it.hasNext();) {
+ Object value = it.next();
+ for (Iterator<Object> otherIt = otherValues.iterator(); otherIt.hasNext();) {
+ Object otherValue = otherIt.next();
+ if (sameOrEquals(value, otherValue, pending)) {
+ if (pairs != null) {
+ pairs.put(value, otherValue);
+ }
+ // If a match is found, remove both values from their lists
+ it.remove();
+ otherIt.remove();
+ continue it;
+ }
+ }
+ // A match for the value wasn't found
+ return false;
+ }
+ assert values.isEmpty() && otherValues.isEmpty();
+ }
+ return true;
+ }
+
+ /**
+ * Compare two Maps by size, and key-value pairs.
+ */
+ private static boolean sameOrEquals(Map<?, ?> map, Map<?, ?> otherMap,
+ Map<PendingComparison, Comparison> pending) {
+ if (map.size() != otherMap.size()) {
+ return false;
+ }
+ Map<Object, Object> pairs = new IdentityHashMap<Object, Object>();
+ if (!sameOrEquals(map.keySet(), otherMap.keySet(), pending, pairs)) {
+ return false;
+ }
+ for (Map.Entry<?, ?> entry : map.entrySet()) {
+ Object otherValue = otherMap.get(pairs.get(entry.getKey()));
+ if (!sameOrEquals(entry.getValue(), otherValue, pending)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Compare Splittables by kind and values.
+ */
+ private static boolean sameOrEquals(Splittable value, Splittable otherValue,
+ Map<PendingComparison, Comparison> pending) {
+ if (value == otherValue) {
+ return true;
+ }
+
+ // Strings
+ if (value.isString()) {
+ if (!otherValue.isString()) {
+ return false;
+ }
+ return value.asString().equals(otherValue.asString());
+ }
+
+ // Arrays
+ if (value.isIndexed()) {
+ if (!otherValue.isIndexed()) {
+ return false;
+ }
+
+ if (value.size() != otherValue.size()) {
+ return false;
+ }
+
+ for (int i = 0, j = value.size(); i < j; i++) {
+ if (!sameOrEquals(value.get(i), otherValue.get(i), pending)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Objects
+ if (value.isKeyed()) {
+ if (!otherValue.isKeyed()) {
+ return false;
+ }
+ /*
+ * We want to treat a missing property key as a null value, so we can't
+ * just compare the key lists.
+ */
+ List<String> keys = value.getPropertyKeys();
+ for (String key : keys) {
+ if (value.isNull(key)) {
+ // If value['foo'] is null, other['foo'] must also be null
+ if (!otherValue.isNull(key)) {
+ return false;
+ }
+ } else if (otherValue.isNull(key)
+ || !sameOrEquals(value.get(key), otherValue.get(key), pending)) {
+ return false;
+ }
+ }
+
+ // Look at keys only in otherValue, and ensure nullness
+ List<String> otherKeys = new ArrayList<String>(otherValue.getPropertyKeys());
+ otherKeys.removeAll(keys);
+ for (String key : otherKeys) {
+ if (!value.isNull(key)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Unexpected
+ throw new UnsupportedOperationException("Splittable of unknown type");
+ }
+
+ /**
+ * Utility class.
+ */
+ private AutoBeanUtils() {
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/shared/AutoBeanVisitor.java b/user/src/com/google/web/bindery/autobean/shared/AutoBeanVisitor.java
new file mode 100644
index 0000000..bff9d4a
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/shared/AutoBeanVisitor.java
@@ -0,0 +1,267 @@
+/*
+ * 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.web.bindery.autobean.shared;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Allows traversal of an AutoBean object graph.
+ */
+public class AutoBeanVisitor {
+ /**
+ * A PropertyContext that describes the parameterization of the Collection
+ * being visited.
+ */
+ public interface CollectionPropertyContext extends PropertyContext {
+ /**
+ * Returns the collection's element type.
+ *
+ * @return a Class object representing the element type
+ */
+ Class<?> getElementType();
+ }
+
+ /**
+ * Reserved for future expansion to avoid API breaks.
+ */
+ public interface Context {
+ }
+
+ /**
+ * A PropertyContext that describes the parameterization of the Map being
+ * visited.
+ */
+ public interface MapPropertyContext extends PropertyContext {
+ /**
+ * Returns the map's key type.
+ *
+ * @return a Class object representing the key type
+ */
+ Class<?> getKeyType();
+
+ /**
+ * Returns the map's value type.
+ *
+ * @return a Class object representing the value type
+ */
+ Class<?> getValueType();
+ }
+
+ /**
+ * The ParameterizationVisitor provides access to more complete type
+ * information than a simple class literal can provide.
+ * <p>
+ * The order of traversal reflects the declared parameterization of the
+ * property. For example, a {@code Map<String, List<Foo>>} would be traversed
+ * via the following sequence:
+ *
+ * <pre>
+ * visitType(Map.class);
+ * visitParameter();
+ * visitType(String.class);
+ * endVisitType(String.class);
+ * endVisitParameter();
+ * visitParameter();
+ * visitType(List.class);
+ * visitParameter();
+ * visitType(Foo.class);
+ * endVisitType(Foo.class);
+ * endParameter();
+ * endVisitType(List.class);
+ * endVisitParameter();
+ * endVisitType(Map.class);
+ * </pre>
+ */
+ public static class ParameterizationVisitor {
+ /**
+ * Called when finished with a type parameter.
+ */
+ public void endVisitParameter() {
+ }
+
+ /**
+ * Called when finished with a type.
+ *
+ * @param type a Class object
+ */
+ public void endVisitType(Class<?> type) {
+ }
+
+ /**
+ * Called when visiting a type parameter.
+ *
+ * @return {@code true} if the type parameter should be visited
+ */
+ public boolean visitParameter() {
+ return true;
+ }
+
+ /**
+ * Called when visiting a possibly parameterized type.
+ *
+ * @param type a Class object
+ * @return {@code true} if the type should be visited
+ */
+ public boolean visitType(Class<?> type) {
+ return true;
+ }
+ }
+
+ /**
+ * Allows properties to be reset.
+ */
+ public interface PropertyContext {
+ /**
+ * Allows deeper inspection of the declared parameterization of the
+ * property.
+ */
+ void accept(ParameterizationVisitor visitor);
+
+ /**
+ * Indicates if the {@link #set} method will succeed.
+ *
+ * @return {@code true} if the property can be set
+ */
+ boolean canSet();
+
+ /**
+ * Returns the expected type of the property.
+ *
+ * @return a Class object representing the property type
+ */
+ Class<?> getType();
+
+ /**
+ * Sets a property value.
+ *
+ * @param value the new value
+ */
+ void set(Object value);
+ }
+
+ /**
+ * Called after visiting an {@link AutoBean}.
+ *
+ * @param bean an {@link AutoBean}
+ * @param ctx a Context
+ */
+ public void endVisit(AutoBean<?> bean, Context ctx) {
+ }
+
+ /**
+ * Called after visiting a reference property.
+ *
+ * @param propertyName the property name, as a String
+ * @param value the property value
+ * @param ctx a PropertyContext
+ */
+ public void endVisitCollectionProperty(String propertyName, AutoBean<Collection<?>> value,
+ CollectionPropertyContext ctx) {
+ endVisitReferenceProperty(propertyName, value, ctx);
+ }
+
+ /**
+ * Called after visiting a reference property.
+ *
+ * @param propertyName the property name, as a String
+ * @param value the property value
+ * @param ctx a PropertyContext
+ */
+ public void endVisitMapProperty(String propertyName, AutoBean<Map<?, ?>> value,
+ MapPropertyContext ctx) {
+ endVisitReferenceProperty(propertyName, value, ctx);
+ }
+
+ /**
+ * Called after visiting a reference property.
+ *
+ * @param propertyName the property name, as a String
+ * @param value the property value
+ * @param ctx a PropertyContext
+ */
+ public void endVisitReferenceProperty(String propertyName, AutoBean<?> value, PropertyContext ctx) {
+ }
+
+ /**
+ * Called after visiting a value property.
+ *
+ * @param propertyName the property name, as a String
+ * @param value the property value
+ * @param ctx a PropertyContext
+ */
+ public void endVisitValueProperty(String propertyName, Object value, PropertyContext ctx) {
+ }
+
+ /**
+ * Called when visiting an {@link AutoBean}.
+ *
+ * @param bean an {@link AutoBean}
+ * @param ctx a Context
+ */
+ public boolean visit(AutoBean<?> bean, Context ctx) {
+ return true;
+ }
+
+ /**
+ * Called every time, but {@link #visit(AutoBean, Context)} will be called for
+ * the value only the first time it is encountered.
+ *
+ * @param propertyName the property name, as a String
+ * @param value the property value
+ * @param ctx a PropertyContext
+ */
+ public boolean visitCollectionProperty(String propertyName, AutoBean<Collection<?>> value,
+ CollectionPropertyContext ctx) {
+ return visitReferenceProperty(propertyName, value, ctx);
+ }
+
+ /**
+ * Called every time, but {@link #visit(AutoBean, Context)} will be called for
+ * the value only the first time it is encountered.
+ *
+ * @param propertyName the property name, as a String
+ * @param value the property value
+ * @param ctx a PropertyContext
+ */
+ public boolean visitMapProperty(String propertyName, AutoBean<Map<?, ?>> value,
+ MapPropertyContext ctx) {
+ return visitReferenceProperty(propertyName, value, ctx);
+ }
+
+ /**
+ * Called every time, but {@link #visit(AutoBean, Context)} will be called for
+ * the value only the first time it is encountered.
+ *
+ * @param propertyName the property name, as a String
+ * @param value the property value
+ * @param ctx a PropertyContext
+ */
+ public boolean visitReferenceProperty(String propertyName, AutoBean<?> value, PropertyContext ctx) {
+ return true;
+ }
+
+ /**
+ * TODO: document.
+ *
+ * @param propertyName the property name, as a String
+ * @param value the property value
+ * @param ctx a PropertyContext
+ */
+ public boolean visitValueProperty(String propertyName, Object value, PropertyContext ctx) {
+ return true;
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/shared/Splittable.java b/user/src/com/google/web/bindery/autobean/shared/Splittable.java
new file mode 100644
index 0000000..2698345
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/shared/Splittable.java
@@ -0,0 +1,152 @@
+/*
+ * 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.web.bindery.autobean.shared;
+
+import com.google.web.bindery.autobean.shared.impl.StringQuoter;
+
+import java.util.List;
+
+/**
+ * This interface provides an abstraction around the underlying data model
+ * (JavaScriptObject, {@code org.json}, or XML) used to encode an AutoBeanCodex
+ * payload.
+ */
+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);
+
+ /**
+ * Returns the named property.
+ */
+ Splittable get(String key);
+
+ /**
+ * Returns a wire-format representation of the data.
+ */
+ String getPayload();
+
+ /**
+ * Returns all keys available in the Splittable. This method may be expensive
+ * to compute.
+ */
+ 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.
+ */
+ boolean isIndexed();
+
+ /**
+ * Returns {@code} true if {@link #getPropertyKeys()} and {@link #get(String)}
+ * can be expected to return meaningful values.
+ */
+ boolean isKeyed();
+
+ /**
+ * Indicates if the nth element of a list is null or undefined.
+ */
+ boolean isNull(int index);
+
+ /**
+ * Indicates if the named property is null or undefined.
+ */
+ 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.
+ */
+ int size();
+}
diff --git a/user/src/com/google/web/bindery/autobean/shared/ValueCodex.java b/user/src/com/google/web/bindery/autobean/shared/ValueCodex.java
new file mode 100644
index 0000000..4bcb304
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/shared/ValueCodex.java
@@ -0,0 +1,368 @@
+/*
+ * 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.web.bindery.autobean.shared;
+
+import com.google.web.bindery.autobean.shared.impl.StringQuoter;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Provides unified encoding and decoding of value objects.
+ */
+public class ValueCodex {
+ enum Type {
+ BIG_DECIMAL(BigDecimal.class) {
+ @Override
+ public boolean canUpcast(Object value) {
+ return value instanceof BigDecimal;
+ }
+
+ @Override
+ public BigDecimal decode(Class<?> clazz, Splittable value) {
+ return new BigDecimal(value.asString());
+ }
+
+ @Override
+ public Splittable encode(Object value) {
+ return StringQuoter.create(((BigDecimal) value).toString());
+ }
+ },
+ BIG_INTEGER(BigInteger.class) {
+ @Override
+ public boolean canUpcast(Object value) {
+ return value instanceof BigInteger;
+ }
+
+ @Override
+ public BigInteger decode(Class<?> clazz, Splittable value) {
+ return new BigInteger(value.asString());
+ }
+
+ @Override
+ public Splittable encode(Object value) {
+ return StringQuoter.create(((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);
+ }
+ },
+ 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);
+ }
+ },
+ 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));
+ }
+ },
+ DATE(Date.class) {
+ @Override
+ public boolean canUpcast(Object value) {
+ return value instanceof Date;
+ }
+
+ @Override
+ public Date decode(Class<?> clazz, Splittable value) {
+ return StringQuoter.tryParseDate(value.asString());
+ }
+
+ @Override
+ public Splittable encode(Object value) {
+ return StringQuoter.create(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);
+ }
+ },
+ ENUM(Enum.class) {
+ @Override
+ public Enum<?> decode(Class<?> clazz, Splittable value) {
+ return (Enum<?>) clazz.getEnumConstants()[(int) value.asNumber()];
+ }
+
+ @Override
+ public Splittable encode(Object value) {
+ return StringQuoter.create(((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);
+ }
+ },
+ 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);
+ }
+ },
+ LONG(Long.class, long.class, 0L) {
+ @Override
+ public Long decode(Class<?> clazz, Splittable value) {
+ return Long.parseLong(value.asString());
+ }
+
+ @Override
+ public Splittable encode(Object value) {
+ return StringQuoter.create(String.valueOf((Long) value));
+ }
+ },
+ 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);
+ }
+ },
+ 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) {
+ return value;
+ }
+
+ @Override
+ public Splittable encode(Object value) {
+ return (Splittable) value;
+ }
+ },
+ VOID(Void.class, void.class, null) {
+ @Override
+ public Void decode(Class<?> clazz, Splittable value) {
+ return null;
+ }
+
+ @Override
+ public Splittable encode(Object value) {
+ return null;
+ }
+ };
+ private final Object defaultValue;
+ private final Class<?> type;
+ private final Class<?> primitiveType;
+
+ Type(Class<?> objectType) {
+ this(objectType, null, null);
+ }
+
+ Type(Class<?> objectType, Class<?> primitiveType, Object defaultValue) {
+ this.type = objectType;
+ this.primitiveType = primitiveType;
+ this.defaultValue = defaultValue;
+ }
+
+ /**
+ * Determines whether or not the Type can handle the given value via
+ * upcasting semantics.
+ *
+ * @param value a value Object
+ */
+ public boolean canUpcast(Object value) {
+ // Most value types are final, so this method is meaningless
+ return false;
+ }
+
+ public abstract Object decode(Class<?> clazz, Splittable value);
+
+ public abstract Splittable encode(Object value);
+
+ public Object getDefaultValue() {
+ return defaultValue;
+ }
+
+ public Class<?> getPrimitiveType() {
+ return primitiveType;
+ }
+
+ public Class<?> getType() {
+ return type;
+ }
+ }
+
+ private static final Set<Class<?>> ALL_VALUE_TYPES;
+ private static final Map<Class<?>, Type> TYPES_BY_CLASS;
+ static {
+ Map<Class<?>, Type> temp = new HashMap<Class<?>, Type>();
+ for (Type t : Type.values()) {
+ temp.put(t.getType(), t);
+ if (t.getPrimitiveType() != null) {
+ temp.put(t.getPrimitiveType(), t);
+ }
+ }
+ ALL_VALUE_TYPES = Collections.unmodifiableSet(temp.keySet());
+ TYPES_BY_CLASS = Collections.unmodifiableMap(temp);
+ }
+
+ /**
+ * Returns true if ValueCodex can operate on values of the given type.
+ *
+ * @param clazz a Class object
+ * @return {@code true} if the given object type can be decoded
+ */
+ public static boolean canDecode(Class<?> clazz) {
+ if (findType(clazz) != null) {
+ return true;
+ }
+ // Use other platform-specific tests
+ return ValueCodexHelper.canDecode(clazz);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> T decode(Class<T> clazz, Splittable split) {
+ if (split == null || split == Splittable.NULL) {
+ return null;
+ }
+ return (T) getTypeOrDie(clazz).decode(clazz, split);
+ }
+
+ /**
+ * No callers in GWT codebase.
+ *
+ * @deprecated use {@link #decode(Class, Splittable)} instead.
+ * @throws UnsupportedOperationException
+ */
+ @Deprecated
+ public static <T> T decode(Class<T> clazz, String string) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Encode a value object when the wire format type is known. This method
+ * should be preferred over {@link #encode(Object)} when possible.
+ */
+ public static Splittable encode(Class<?> clazz, Object obj) {
+ if (obj == null) {
+ return Splittable.NULL;
+ }
+ return getTypeOrDie(clazz).encode(obj);
+ }
+
+ public static Splittable encode(Object obj) {
+ if (obj == null) {
+ return Splittable.NULL;
+ }
+ Type t = findType(obj.getClass());
+ // Try upcasting
+ if (t == null) {
+ for (Type maybe : Type.values()) {
+ if (maybe.canUpcast(obj)) {
+ t = maybe;
+ break;
+ }
+ }
+ }
+ if (t == null) {
+ throw new UnsupportedOperationException(obj.getClass().getName());
+ }
+ return t.encode(obj);
+ }
+
+ /**
+ * Return all Value types that can be processed by the ValueCodex.
+ */
+ public static Set<Class<?>> getAllValueTypes() {
+ return ALL_VALUE_TYPES;
+ }
+
+ /**
+ * Returns the uninitialized field value for the given primitive type.
+ */
+ public static Object getUninitializedFieldValue(Class<?> clazz) {
+ Type type = getTypeOrDie(clazz);
+ if (clazz.equals(type.getPrimitiveType())) {
+ return type.getDefaultValue();
+ }
+ return null;
+ }
+
+ /**
+ * May return <code>null</code>.
+ */
+ private static <T> Type findType(Class<T> clazz) {
+ if (clazz.isEnum()) {
+ return Type.ENUM;
+ }
+ return TYPES_BY_CLASS.get(clazz);
+ }
+
+ private static <T> Type getTypeOrDie(Class<T> clazz) {
+ Type toReturn = findType(clazz);
+ if (toReturn == null) {
+ throw new UnsupportedOperationException(clazz.getName());
+ }
+ return toReturn;
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/shared/ValueCodexHelper.java b/user/src/com/google/web/bindery/autobean/shared/ValueCodexHelper.java
new file mode 100644
index 0000000..fb9fcc9
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/shared/ValueCodexHelper.java
@@ -0,0 +1,38 @@
+/*
+ * 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.web.bindery.autobean.shared;
+
+import com.google.gwt.core.client.GWT;
+
+/**
+ * Provides reflection-based operation for server (JVM) implementation. There is
+ * a no-op super-source version for client (dev- and web-mode) code.
+ */
+class ValueCodexHelper {
+ /**
+ * Returns {@code true} if {@code clazz} is assignable to any of the value
+ * types.
+ */
+ static boolean canDecode(Class<?> clazz) {
+ assert !GWT.isClient();
+ for (Class<?> valueType : ValueCodex.getAllValueTypes()) {
+ if (valueType.isAssignableFrom(clazz)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/shared/impl/AbstractAutoBean.java b/user/src/com/google/web/bindery/autobean/shared/impl/AbstractAutoBean.java
new file mode 100644
index 0000000..4e801d9
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/shared/impl/AbstractAutoBean.java
@@ -0,0 +1,291 @@
+/*
+ * 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.web.bindery.autobean.shared.impl;
+
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory;
+import com.google.web.bindery.autobean.shared.AutoBeanUtils;
+import com.google.web.bindery.autobean.shared.AutoBeanVisitor;
+import com.google.web.bindery.autobean.shared.AutoBeanVisitor.Context;
+import com.google.web.bindery.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl.Coder;
+import com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl.EncodeState;
+import com.google.gwt.core.client.impl.WeakMapping;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Basic implementation.
+ *
+ * @param <T> the wrapper type
+ */
+public abstract class AbstractAutoBean<T> implements AutoBean<T>, HasSplittable {
+ /**
+ * Used to avoid cycles when visiting.
+ */
+ public static class OneShotContext implements Context {
+ private final Set<AbstractAutoBean<?>> seen = new HashSet<AbstractAutoBean<?>>();
+
+ public boolean hasSeen(AbstractAutoBean<?> bean) {
+ return !seen.add(bean);
+ }
+ }
+
+ 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;
+ 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;
+
+ /**
+ * 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();
+ }
+
+ /**
+ * 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.
+ */
+ protected AbstractAutoBean(T wrapped, AutoBeanFactory factory) {
+ this.factory = factory;
+ usingSimplePeer = false;
+ data = null;
+ this.wrapped = wrapped;
+
+ // Used by AutoBeanUtils
+ WeakMapping.set(wrapped, AutoBean.class.getName(), this);
+ }
+
+ public void accept(AutoBeanVisitor visitor) {
+ traverse(visitor, new OneShotContext());
+ }
+
+ public abstract T as();
+
+ public AutoBean<T> clone(boolean deep) {
+ throw new UnsupportedOperationException();
+ }
+
+ 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;
+ }
+
+ public boolean isWrapper() {
+ 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;
+ }
+
+ public void setTag(String tagName, Object value) {
+ if (tags == null) {
+ tags = new HashMap<String, Object>();
+ }
+ tags.put(tagName, value);
+ }
+
+ public void traverse(AutoBeanVisitor visitor, OneShotContext ctx) {
+ // Avoid cycles
+ if (ctx.hasSeen(this)) {
+ return;
+ }
+ if (visitor.visit(this, ctx)) {
+ traverseProperties(visitor, ctx);
+ }
+ visitor.endVisit(this, ctx);
+ }
+
+ public T unwrap() {
+ if (usingSimplePeer) {
+ throw new IllegalStateException();
+ }
+ try {
+ WeakMapping.set(wrapped, AutoBean.class.getName(), null);
+ return wrapped;
+ } finally {
+ wrapped = null;
+ }
+ }
+
+ /**
+ * No-op. Used as a debugger hook point for generated code.
+ *
+ * @param method the method name
+ * @param returned the returned object
+ * @param parameters the parameter list
+ */
+ protected void call(String method, Object returned, Object... parameters) {
+ }
+
+ protected void checkFrozen() {
+ if (frozen) {
+ throw new IllegalStateException("The AutoBean has been frozen");
+ }
+ }
+
+ protected void checkWrapped() {
+ if (wrapped == null && !usingSimplePeer) {
+ throw new IllegalStateException("The AutoBean has been unwrapped");
+ }
+ }
+
+ protected T createSimplePeer() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * No-op. Used as a debugger hook point for generated code.
+ *
+ * @param method the method name
+ * @param toReturn the value to return
+ */
+ protected <V> V get(String method, V toReturn) {
+ return toReturn;
+ }
+
+ protected <W> W getFromWrapper(W obj) {
+ // Some versions of javac have problem inferring the generics here
+ 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();
+ return wrapped;
+ }
+
+ protected boolean isUsingSimplePeer() {
+ return usingSimplePeer;
+ }
+
+ protected boolean isWrapped(Object obj) {
+ return AutoBeanUtils.getAutoBean(obj) != null;
+ }
+
+ /**
+ * No-op. Used as a debugger hook point for generated code.
+ *
+ * @param method the method name
+ * @param value the Object value to be set
+ */
+ 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);
+}
diff --git a/user/src/com/google/web/bindery/autobean/shared/impl/AutoBeanCodexImpl.java b/user/src/com/google/web/bindery/autobean/shared/impl/AutoBeanCodexImpl.java
new file mode 100644
index 0000000..78b4f2e
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/shared/impl/AutoBeanCodexImpl.java
@@ -0,0 +1,613 @@
+/*
+ * 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.web.bindery.autobean.shared.impl;
+
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory;
+import com.google.web.bindery.autobean.shared.AutoBeanUtils;
+import com.google.web.bindery.autobean.shared.AutoBeanVisitor;
+import com.google.web.bindery.autobean.shared.AutoBeanVisitor.ParameterizationVisitor;
+import com.google.web.bindery.autobean.shared.Splittable;
+import com.google.web.bindery.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) {
+ /*
+ * If we decode the same Splittable twice, re-use the ProxyAutoBean to
+ * maintain referential integrity. If we didn't do this, either facade would
+ * update the same backing data, yet not be the same object via ==
+ * comparison.
+ */
+ @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/web/bindery/autobean/shared/impl/EnumMap.java b/user/src/com/google/web/bindery/autobean/shared/impl/EnumMap.java
new file mode 100644
index 0000000..8356831
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/shared/impl/EnumMap.java
@@ -0,0 +1,33 @@
+/*
+ * 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.web.bindery.autobean.shared.impl;
+
+/**
+ * This interface is implemented by our generated AutoBeanFactory types to
+ * convert enum values to strings.
+ */
+public interface EnumMap {
+ /**
+ * Extra enums that should be included in the AutoBeanFactory.
+ */
+ public @interface ExtraEnums {
+ Class<? extends Enum<?>>[] value();
+ }
+
+ <E extends Enum<?>> E getEnum(Class<E> clazz, String token);
+
+ String getToken(Enum<?> e);
+}
diff --git a/user/src/com/google/web/bindery/autobean/shared/impl/HasSplittable.java b/user/src/com/google/web/bindery/autobean/shared/impl/HasSplittable.java
new file mode 100644
index 0000000..41b39d2
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/shared/impl/HasSplittable.java
@@ -0,0 +1,26 @@
+/*
+ * 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.web.bindery.autobean.shared.impl;
+
+import com.google.web.bindery.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/web/bindery/autobean/shared/impl/SplittableComplexMap.java b/user/src/com/google/web/bindery/autobean/shared/impl/SplittableComplexMap.java
new file mode 100644
index 0000000..c118d29
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/shared/impl/SplittableComplexMap.java
@@ -0,0 +1,200 @@
+/*
+ * 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.web.bindery.autobean.shared.impl;
+
+import com.google.web.bindery.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl.Coder;
+import com.google.web.bindery.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/web/bindery/autobean/shared/impl/SplittableList.java b/user/src/com/google/web/bindery/autobean/shared/impl/SplittableList.java
new file mode 100644
index 0000000..48fe0f6
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/shared/impl/SplittableList.java
@@ -0,0 +1,114 @@
+/*
+ * 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.web.bindery.autobean.shared.impl;
+
+import com.google.web.bindery.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl.Coder;
+import com.google.web.bindery.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/web/bindery/autobean/shared/impl/SplittableSet.java b/user/src/com/google/web/bindery/autobean/shared/impl/SplittableSet.java
new file mode 100644
index 0000000..9b6d8ab
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/shared/impl/SplittableSet.java
@@ -0,0 +1,71 @@
+/*
+ * 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.web.bindery.autobean.shared.impl;
+
+import com.google.web.bindery.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl.Coder;
+import com.google.web.bindery.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/web/bindery/autobean/shared/impl/SplittableSimpleMap.java b/user/src/com/google/web/bindery/autobean/shared/impl/SplittableSimpleMap.java
new file mode 100644
index 0000000..fc4dc0f
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/shared/impl/SplittableSimpleMap.java
@@ -0,0 +1,255 @@
+/*
+ * 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.web.bindery.autobean.shared.impl;
+
+import com.google.web.bindery.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl.Coder;
+import com.google.web.bindery.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/web/bindery/autobean/shared/impl/StringQuoter.java b/user/src/com/google/web/bindery/autobean/shared/impl/StringQuoter.java
new file mode 100644
index 0000000..d30fb3e
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/shared/impl/StringQuoter.java
@@ -0,0 +1,98 @@
+/*
+ * 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.web.bindery.autobean.shared.impl;
+
+import com.google.web.bindery.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.vm.impl.JsonSplittable;
+
+import org.json.JSONObject;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * This class has a super-source version with a client-only implementation.
+ */
+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 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();
+ }
+
+ /**
+ * Create a quoted JSON string.
+ */
+ public static String quote(String raw) {
+ return JSONObject.quote(raw);
+ }
+
+ public static Splittable split(String payload) {
+ return JsonSplittable.create(payload);
+ }
+
+ /**
+ * Attempt to parse an ISO-8601 date format. May return {@code null} if the
+ * input cannot be parsed.
+ */
+ public static Date tryParseDate(String date) {
+ try {
+ return new Date(Long.parseLong(date));
+ } catch (NumberFormatException ignored) {
+ }
+ if (date.endsWith("Z")) {
+ date = date.substring(0, date.length() - 1) + "+0000";
+ }
+ try {
+ return ISO8601.parse(date);
+ } catch (ParseException ignored) {
+ }
+ try {
+ return RFC2822.parse(date);
+ } catch (ParseException ignored) {
+ }
+ return null;
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/shared/package-info.java b/user/src/com/google/web/bindery/autobean/shared/package-info.java
new file mode 100644
index 0000000..4dec6fa
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/shared/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+/**
+ * The AutoBean framework provides automatically-generated implementations of
+ * bean-like interfaces and a low-level serialization mechanism for those
+ * interfaces. AutoBeans can be used in both client and server code to improve
+ * code re-use.
+ *
+ * @see <a
+ * href="http://code.google.com/p/google-web-toolkit/wiki/AutoBean">AutoBean
+ * wiki page</a>
+ * @see com.google.web.bindery.autobean.shared.AutoBeanFactory
+ * @see com.google.web.bindery.autobean.vm.AutoBeanFactorySource
+ */
+@com.google.gwt.util.PreventSpuriousRebuilds
+package com.google.web.bindery.autobean.shared;
+
diff --git a/user/src/com/google/web/bindery/autobean/vm/AutoBeanFactorySource.java b/user/src/com/google/web/bindery/autobean/vm/AutoBeanFactorySource.java
new file mode 100644
index 0000000..eaba1d0
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/vm/AutoBeanFactorySource.java
@@ -0,0 +1,76 @@
+/*
+ * 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.web.bindery.autobean.vm;
+
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory.Category;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory.NoWrap;
+import com.google.web.bindery.autobean.shared.impl.EnumMap;
+import com.google.web.bindery.autobean.vm.impl.FactoryHandler;
+import com.google.web.bindery.autobean.vm.impl.ProxyAutoBean;
+
+/**
+ * Generates JVM-compatible implementations of AutoBeanFactory and AutoBean
+ * types.
+ * <p>
+ * This implementation is written assuming that the AutoBeanFactory and
+ * associated declarations will validate if compiled and used with the
+ * AutoBeanFactoyModel.
+ * <p>
+ * <span style='color: red'>This is experimental, unsupported code.</span>
+ */
+public class AutoBeanFactorySource {
+ /*
+ * NB: This implementation is excessively dynamic, however the inability to
+ * create a TypeOracle fram a ClassLoader prevents re-using the existing model
+ * code. If the model code could be reused, it would be straightforward to
+ * simply generate implementations of the various interfaces.
+ */
+ private static final AutoBeanFactory EMPTY = create(AutoBeanFactory.class);
+
+ /**
+ * Create an instance of an AutoBeanFactory.
+ *
+ * @param <F> the factory type
+ * @param clazz the Class representing the factory interface
+ * @return an instance of the AutoBeanFactory
+ */
+ public static <F extends AutoBeanFactory> F create(Class<F> clazz) {
+ Configuration.Builder builder = new Configuration.Builder();
+ Category cat = clazz.getAnnotation(Category.class);
+ if (cat != null) {
+ builder.setCategories(cat.value());
+ }
+ NoWrap noWrap = clazz.getAnnotation(NoWrap.class);
+ if (noWrap != null) {
+ builder.setNoWrap(noWrap.value());
+ }
+
+ return ProxyAutoBean.makeProxy(clazz, new FactoryHandler(builder.build()), EnumMap.class);
+ }
+
+ /**
+ * Create an instance of an AutoBean directly.
+ *
+ * @param <T> the interface type implemented by the AutoBean
+ * @param clazz the interface type implemented by the AutoBean
+ * @return an instance of an AutoBean
+ */
+ public static <T> AutoBean<T> createBean(Class<T> clazz, Configuration configuration) {
+ return new ProxyAutoBean<T>(EMPTY, clazz, configuration);
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/vm/Configuration.java b/user/src/com/google/web/bindery/autobean/vm/Configuration.java
new file mode 100644
index 0000000..593ad52
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/vm/Configuration.java
@@ -0,0 +1,94 @@
+/*
+ * 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.web.bindery.autobean.vm;
+
+import com.google.web.bindery.autobean.shared.AutoBean;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Used by {@link AutoBeanFactorySource#createBean(Class, Configuration)}. This
+ * type replicates the annotations that may be applied to an AutoBeanFactory
+ * declaration.
+ * <p>
+ * <span style='color: red'>This is experimental, unsupported code.</span>
+ */
+public class Configuration {
+ /**
+ * Builds {@link Configuration} objects.
+ */
+ public static class Builder {
+ private Configuration toReturn = new Configuration();
+
+ public Configuration build() {
+ toReturn.noWrap.add(AutoBean.class);
+ toReturn.noWrap = Collections.unmodifiableSet(toReturn.noWrap);
+ try {
+ return toReturn;
+ } finally {
+ toReturn = null;
+ }
+ }
+
+ /**
+ * Equivalent to applying a
+ * {@link com.google.web.bindery.autobean.shared.AutoBeanFactory.Category
+ * Category} annotation to an AutoBeanFactory declaration.
+ *
+ * @param categories the category types that should be searched for static
+ * implementations of non-property methods
+ * @return the Builder
+ */
+ public Builder setCategories(Class<?>... categories) {
+ toReturn.categories =
+ Collections.unmodifiableList(new ArrayList<Class<?>>(Arrays.asList(categories)));
+ return this;
+ }
+
+ /**
+ * Equivalent to applying a
+ * {@link com.google.web.bindery.autobean.shared.AutoBeanFactory.NoWrap
+ * NoWrap} annotation to an AutoBeanFactory declaration.
+ *
+ * @param noWrap the types that should be excluded from wrapping
+ * @return the Builder
+ */
+ public Builder setNoWrap(Class<?>... noWrap) {
+ toReturn.noWrap.addAll(Arrays.asList(noWrap));
+ return this;
+ }
+ }
+
+ private List<Class<?>> categories = Collections.emptyList();
+
+ private Set<Class<?>> noWrap = new HashSet<Class<?>>();
+
+ private Configuration() {
+ }
+
+ public List<Class<?>> getCategories() {
+ return categories;
+ }
+
+ public Set<Class<?>> getNoWrap() {
+ return noWrap;
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/vm/impl/BeanMethod.java b/user/src/com/google/web/bindery/autobean/vm/impl/BeanMethod.java
new file mode 100644
index 0000000..f165239
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/vm/impl/BeanMethod.java
@@ -0,0 +1,248 @@
+/*
+ * 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.web.bindery.autobean.vm.impl;
+
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBean.PropertyName;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * Breakout of method types that an AutoBean shim interface can implement. The
+ * order of the values of the enum is important.
+ *
+ * @see com.google.web.bindery.autobean.gwt.rebind.model.JBeanMethod
+ */
+public enum BeanMethod {
+ /**
+ * Methods defined in Object.
+ */
+ OBJECT {
+
+ @Override
+ public String inferName(Method method) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ Object invoke(SimpleBeanHandler<?> handler, Method method, Object[] args) throws Throwable {
+ if (CALL.matches(handler, method)) {
+ return CALL.invoke(handler, method, args);
+ }
+ return method.invoke(handler, args);
+ }
+
+ @Override
+ boolean matches(SimpleBeanHandler<?> handler, Method method) {
+ return method.getDeclaringClass().equals(Object.class);
+ }
+ },
+ /**
+ * Getters.
+ */
+ GET {
+ @Override
+ public String inferName(Method method) {
+ String name = method.getName();
+ if (name.startsWith(IS_PREFIX) && !method.isAnnotationPresent(PropertyName.class)) {
+ Class<?> returnType = method.getReturnType();
+ if (Boolean.TYPE.equals(returnType) || Boolean.class.equals(returnType)) {
+ return decapitalize(name.substring(2));
+ }
+ }
+ return super.inferName(method);
+ }
+
+ @Override
+ Object invoke(SimpleBeanHandler<?> handler, Method method, Object[] args) {
+ String propertyName = inferName(method);
+ Object toReturn = handler.getBean().getOrReify(propertyName);
+ if (toReturn == null && method.getReturnType().isPrimitive()) {
+ toReturn = TypeUtils.getDefaultPrimitiveValue(method.getReturnType());
+ }
+ return toReturn;
+ }
+
+ @Override
+ boolean matches(SimpleBeanHandler<?> handler, Method method) {
+ Class<?> returnType = method.getReturnType();
+ 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) {
+ return true;
+ }
+ }
+ return name.startsWith(GET_PREFIX) && name.length() > 3;
+ }
+ },
+ /**
+ * Setters.
+ */
+ SET {
+ @Override
+ Object invoke(SimpleBeanHandler<?> handler, Method method, Object[] args) {
+ handler.getBean().setProperty(inferName(method), args[0]);
+ return null;
+ }
+
+ @Override
+ 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);
+ }
+ },
+ /**
+ * A setter that returns a type assignable from the interface in which the
+ * method is declared to support chained, builder-pattern setters. For
+ * example, {@code foo.setBar(1).setBaz(42)}.
+ */
+ SET_BUILDER {
+ @Override
+ Object invoke(SimpleBeanHandler<?> handler, Method method, Object[] args) {
+ ProxyAutoBean<?> bean = handler.getBean();
+ bean.setProperty(inferName(method), args[0]);
+ return bean.as();
+ }
+
+ @Override
+ boolean matches(SimpleBeanHandler<?> handler, Method method) {
+ String name = method.getName();
+ return name.startsWith(SET_PREFIX) && name.length() > 3
+ && method.getParameterTypes().length == 1
+ && method.getReturnType().isAssignableFrom(method.getDeclaringClass());
+ }
+ },
+ /**
+ * Domain methods.
+ */
+ CALL {
+ @Override
+ public String inferName(Method method) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ Object invoke(SimpleBeanHandler<?> handler, Method method, Object[] args) throws Throwable {
+ if (args == null) {
+ args = EMPTY_OBJECT;
+ }
+
+ Method found = findMethod(handler, method);
+ if (found != null) {
+ Object[] realArgs = new Object[args.length + 1];
+ realArgs[0] = handler.getBean();
+ System.arraycopy(args, 0, realArgs, 1, args.length);
+ return found.invoke(null, realArgs);
+ }
+ throw new RuntimeException("Could not find category implementation of "
+ + method.toGenericString());
+ }
+
+ @Override
+ boolean matches(SimpleBeanHandler<?> handler, Method method) {
+ return handler.getBean().isWrapper()
+ || !handler.getBean().getConfiguration().getCategories().isEmpty()
+ && findMethod(handler, method) != null;
+ }
+ };
+
+ public static final String GET_PREFIX = "get";
+ public static final String HAS_PREFIX = "has";
+ public static final String IS_PREFIX = "is";
+ public static final String SET_PREFIX = "set";
+
+ private static final Object[] EMPTY_OBJECT = new Object[0];
+
+ static Method findMethod(SimpleBeanHandler<?> handler, Method method) {
+ Class<?>[] declaredParams = method.getParameterTypes();
+ Class<?>[] searchParams = new Class<?>[declaredParams.length + 1];
+ searchParams[0] = AutoBean.class;
+ System.arraycopy(declaredParams, 0, searchParams, 1, declaredParams.length);
+ Class<?> autoBeanType = handler.getBean().getType();
+
+ for (Class<?> clazz : handler.getBean().getConfiguration().getCategories()) {
+ try {
+ Method found = clazz.getMethod(method.getName(), searchParams);
+ if (!Modifier.isStatic(found.getModifiers())) {
+ continue;
+ }
+ // Check the AutoBean parameterization of the 0th argument
+ Class<?> foundAutoBean =
+ TypeUtils.ensureBaseType(TypeUtils.getSingleParameterization(AutoBean.class, found
+ .getGenericParameterTypes()[0]));
+ if (!foundAutoBean.isAssignableFrom(autoBeanType)) {
+ continue;
+ }
+ return found;
+ } catch (NoSuchMethodException expected) {
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 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) {
+ return null;
+ }
+ int length = name.length();
+ if (length == 0 || (length > 1 && Character.isUpperCase(name.charAt(1)))) {
+ return name;
+ }
+ StringBuilder sb = new StringBuilder(length);
+ sb.append(Character.toLowerCase(name.charAt(0)));
+ sb.append(name.substring(1));
+ return sb.toString();
+ }
+
+ public String inferName(Method method) {
+ PropertyName prop = method.getAnnotation(PropertyName.class);
+ if (prop != null) {
+ return prop.value();
+ }
+ return decapitalize(method.getName().substring(3));
+ }
+
+ /**
+ * Convenience method, not valid for {@link BeanMethod#CALL}.
+ */
+ public boolean matches(Method method) {
+ return matches(null, method);
+ }
+
+ /**
+ * Invoke the method.
+ */
+ 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);
+}
diff --git a/user/src/com/google/web/bindery/autobean/vm/impl/BeanPropertyContext.java b/user/src/com/google/web/bindery/autobean/vm/impl/BeanPropertyContext.java
new file mode 100644
index 0000000..a4fe31f
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/vm/impl/BeanPropertyContext.java
@@ -0,0 +1,46 @@
+/*
+ * 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.web.bindery.autobean.vm.impl;
+
+import java.lang.reflect.Method;
+
+/**
+ * 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;
+
+ public BeanPropertyContext(ProxyAutoBean<?> bean, Method getter) {
+ super(getter);
+ this.bean = bean;
+ propertyName = BeanMethod.GET.inferName(getter);
+ }
+
+ @Override
+ public boolean canSet() {
+ return true;
+ }
+
+ @Override
+ public void set(Object value) {
+ 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));
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/vm/impl/FactoryHandler.java b/user/src/com/google/web/bindery/autobean/vm/impl/FactoryHandler.java
new file mode 100644
index 0000000..8532f41
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/vm/impl/FactoryHandler.java
@@ -0,0 +1,129 @@
+/*
+ * 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.web.bindery.autobean.vm.impl;
+
+import com.google.web.bindery.autobean.shared.AutoBean.PropertyName;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory;
+import com.google.web.bindery.autobean.shared.AutoBeanUtils;
+import com.google.web.bindery.autobean.vm.Configuration;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+
+/**
+ * Handles dispatches on AutoBeanFactory interfaces.
+ */
+public class FactoryHandler implements InvocationHandler {
+ private final Configuration configuration;
+
+ /**
+ * Constructor.
+ *
+ * @param categories the classes specified by a Category annotation
+ */
+ public FactoryHandler(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ /**
+ * Handles both declared factory methods as well as the dynamic create
+ * methods.
+ */
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+
+ Class<?> beanType;
+ Object toWrap = null;
+ 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);
+ ParameterizedType returnType = (ParameterizedType) method.getGenericReturnType();
+ beanType = (Class<?>) returnType.getActualTypeArguments()[0];
+
+ if (args != null && args.length == 1) {
+ toWrap = args[0];
+ }
+ }
+
+ // Return any existing wrapper
+ ProxyAutoBean<Object> toReturn = (ProxyAutoBean<Object>) AutoBeanUtils.getAutoBean(toWrap);
+ if (toReturn == null) {
+ // Create the implementation bean
+ if (toWrap == null) {
+ toReturn = new ProxyAutoBean<Object>((AutoBeanFactory) proxy, beanType,
+ configuration);
+ } else {
+ 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();
+ }
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/vm/impl/GetterPropertyContext.java b/user/src/com/google/web/bindery/autobean/vm/impl/GetterPropertyContext.java
new file mode 100644
index 0000000..d00b8ed
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/vm/impl/GetterPropertyContext.java
@@ -0,0 +1,68 @@
+/*
+ * 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.web.bindery.autobean.vm.impl;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Used by {@link ProxyAutoBean#traverseProperties()}.
+ */
+class GetterPropertyContext extends MethodPropertyContext {
+ private final Method setter;
+ private final Object shim;
+
+ GetterPropertyContext(ProxyAutoBean<?> bean, Method getter) {
+ super(getter);
+ this.shim = bean.as();
+
+ // Look for the setter method.
+ Method found = null;
+ String name = BeanMethod.GET.inferName(getter);
+ for (Method m : getter.getDeclaringClass().getMethods()) {
+ if (BeanMethod.SET.matches(m) || BeanMethod.SET_BUILDER.matches(m)) {
+ if (BeanMethod.SET.inferName(m).equals(name)
+ && getter.getReturnType().isAssignableFrom(m.getParameterTypes()[0])) {
+ found = m;
+ break;
+ }
+ }
+ }
+ setter = found;
+ }
+
+ @Override
+ public boolean canSet() {
+ return setter != null;
+ }
+
+ @Override
+ public void set(Object value) {
+ if (!canSet()) {
+ throw new UnsupportedOperationException("No setter");
+ }
+ try {
+ setter.setAccessible(true);
+ setter.invoke(shim, value);
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e.getCause());
+ }
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/vm/impl/JsonSplittable.java b/user/src/com/google/web/bindery/autobean/vm/impl/JsonSplittable.java
new file mode 100644
index 0000000..8fdad79
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/vm/impl/JsonSplittable.java
@@ -0,0 +1,351 @@
+/*
+ * 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.web.bindery.autobean.vm.impl;
+
+import com.google.web.bindery.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.impl.HasSplittable;
+import com.google.web.bindery.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 static Splittable create(String payload) {
+ try {
+ switch (payload.charAt(0)) {
+ case '{':
+ return new JsonSplittable(new JSONObject(payload));
+ case '[':
+ return new JsonSplittable(new JSONArray(payload));
+ case '"':
+ return new JsonSplittable(new JSONArray("[" + payload + "]").getString(0));
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ 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;
+ default:
+ 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 static String[] getNames(JSONObject json) {
+ int length = json.length();
+ if (length == 0) {
+ return null;
+ }
+ String[] names = new String[length];
+ Iterator<?> i = json.keys();
+ int j = 0;
+ while (i.hasNext()) {
+ names[j++] = (String) i.next();
+ }
+ 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 JsonSplittable(JSONArray array) {
+ this.array = array;
+ }
+
+ private JsonSplittable(JSONObject obj) {
+ this.obj = obj;
+ }
+
+ private JsonSplittable(String string) {
+ this.array = null;
+ this.obj = null;
+ 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));
+ } catch (JSONException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public Splittable get(String key) {
+ try {
+ return makeSplittable(obj.get(key));
+ } catch (JSONException e) {
+ throw new RuntimeException(key, e);
+ }
+ }
+
+ public String getPayload() {
+ if (isNull) {
+ return "null";
+ }
+ if (obj != null) {
+ return obj.toString();
+ }
+ if (array != null) {
+ return array.toString();
+ }
+ 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");
+ }
+
+ public List<String> getPropertyKeys() {
+ String[] names = getNames(obj);
+ if (names == null) {
+ return Collections.emptyList();
+ } else {
+ return Collections.unmodifiableList(Arrays.asList(names));
+ }
+ }
+
+ 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;
+ }
+
+ public boolean isKeyed() {
+ return obj != null;
+ }
+
+ public boolean isNull(int index) {
+ return array.isNull(index);
+ }
+
+ public boolean isNull(String key) {
+ // Treat undefined and null as the same
+ 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();
+ }
+
+ /**
+ * For debugging use only.
+ */
+ @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 string;
+ }
+ if (number != null) {
+ return number;
+ }
+ if (bool != null) {
+ return bool;
+ }
+ throw new RuntimeException("No data");
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/vm/impl/MethodPropertyContext.java b/user/src/com/google/web/bindery/autobean/vm/impl/MethodPropertyContext.java
new file mode 100644
index 0000000..10ce579
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/vm/impl/MethodPropertyContext.java
@@ -0,0 +1,111 @@
+/*
+ * 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.web.bindery.autobean.vm.impl;
+
+import com.google.web.bindery.autobean.shared.AutoBeanVisitor.CollectionPropertyContext;
+import com.google.web.bindery.autobean.shared.AutoBeanVisitor.MapPropertyContext;
+import com.google.web.bindery.autobean.shared.AutoBeanVisitor.ParameterizationVisitor;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * A base type to handle analyzing the return value of a getter method. The
+ * accessor methods are implemented in subtypes.
+ */
+abstract class MethodPropertyContext implements CollectionPropertyContext,
+ MapPropertyContext {
+ private static class Data {
+ Class<?> elementType;
+ Type genericType;
+ Class<?> keyType;
+ Class<?> valueType;
+ Class<?> type;
+ }
+
+ /**
+ * Save prior instances in order to decrease the amount of data computed.
+ */
+ private static final Map<Method, Data> cache = new WeakHashMap<Method, Data>();
+ private final Data data;
+
+ public MethodPropertyContext(Method getter) {
+ synchronized (cache) {
+ Data previous = cache.get(getter);
+ if (previous != null) {
+ this.data = previous;
+ return;
+ }
+
+ this.data = new Data();
+ data.genericType = getter.getGenericReturnType();
+ data.type = getter.getReturnType();
+ // Compute collection element type
+ if (Collection.class.isAssignableFrom(getType())) {
+ data.elementType = TypeUtils.ensureBaseType(TypeUtils.getSingleParameterization(
+ Collection.class, getter.getGenericReturnType(),
+ getter.getReturnType()));
+ } else if (Map.class.isAssignableFrom(getType())) {
+ Type[] types = TypeUtils.getParameterization(Map.class,
+ getter.getGenericReturnType());
+ data.keyType = TypeUtils.ensureBaseType(types[0]);
+ data.valueType = TypeUtils.ensureBaseType(types[1]);
+ }
+ cache.put(getter, data);
+ }
+ }
+
+ public void accept(ParameterizationVisitor visitor) {
+ traverse(visitor, data.genericType);
+ }
+
+ public abstract boolean canSet();
+
+ public Class<?> getElementType() {
+ return data.elementType;
+ }
+
+ public Class<?> getKeyType() {
+ return data.keyType;
+ }
+
+ public Class<?> getType() {
+ return data.type;
+ }
+
+ public Class<?> getValueType() {
+ return data.valueType;
+ }
+
+ public abstract void set(Object value);
+
+ private void traverse(ParameterizationVisitor visitor, Type type) {
+ Class<?> base = TypeUtils.ensureBaseType(type);
+ if (visitor.visitType(base)) {
+ Type[] params = TypeUtils.getParameterization(base, type);
+ for (Type t : params) {
+ if (visitor.visitParameter()) {
+ traverse(visitor, t);
+ }
+ visitor.endVisitParameter();
+ }
+ }
+ visitor.endVisitType(base);
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/vm/impl/ProxyAutoBean.java b/user/src/com/google/web/bindery/autobean/vm/impl/ProxyAutoBean.java
new file mode 100644
index 0000000..cdf61dc
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/vm/impl/ProxyAutoBean.java
@@ -0,0 +1,316 @@
+/*
+ * 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.web.bindery.autobean.vm.impl;
+
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory;
+import com.google.web.bindery.autobean.shared.AutoBeanUtils;
+import com.google.web.bindery.autobean.shared.AutoBeanVisitor;
+import com.google.web.bindery.autobean.shared.impl.AbstractAutoBean;
+import com.google.web.bindery.autobean.vm.Configuration;
+import com.google.gwt.core.client.impl.WeakMapping;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * An implementation of an AutoBean that uses reflection.
+ *
+ * @param <T> the type of interface being wrapped
+ */
+public class ProxyAutoBean<T> extends AbstractAutoBean<T> {
+ private static class Data {
+ final List<Method> getters = new ArrayList<Method>();
+ final List<String> getterNames = new ArrayList<String>();
+ final List<PropertyType> propertyType = new ArrayList<PropertyType>();
+ }
+
+ private enum PropertyType {
+ VALUE, REFERENCE, COLLECTION, MAP;
+ }
+
+ private static final Map<Class<?>, Data> cache = new WeakHashMap<Class<?>, Data>();
+
+ /**
+ * Utility method to crete a new {@link Proxy} instance.
+ *
+ * @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
+ */
+ public 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(intf.getClassLoader(), intfs, handler));
+ }
+
+ private static Data calculateData(Class<?> beanType) {
+ Data toReturn;
+ synchronized (cache) {
+ toReturn = cache.get(beanType);
+ if (toReturn == null) {
+ toReturn = new Data();
+ for (Method method : beanType.getMethods()) {
+ if (BeanMethod.GET.matches(method)) {
+ toReturn.getters.add(method);
+
+ String name;
+ PropertyName annotation = method.getAnnotation(PropertyName.class);
+ if (annotation != null) {
+ name = annotation.value();
+ } else {
+ name = BeanMethod.GET.inferName(method);
+ }
+ toReturn.getterNames.add(name);
+
+ Class<?> returnType = method.getReturnType();
+ if (TypeUtils.isValueType(returnType)) {
+ toReturn.propertyType.add(PropertyType.VALUE);
+ } else if (Collection.class.isAssignableFrom(returnType)) {
+ toReturn.propertyType.add(PropertyType.COLLECTION);
+ } else if (Map.class.isAssignableFrom(returnType)) {
+ toReturn.propertyType.add(PropertyType.MAP);
+ } else {
+ toReturn.propertyType.add(PropertyType.REFERENCE);
+ }
+ }
+ }
+ cache.put(beanType, toReturn);
+ }
+ }
+ return toReturn;
+ }
+
+ 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) {
+ super(factory);
+ this.beanType = (Class<T>) beanType;
+ this.configuration = configuration;
+ this.data = calculateData(beanType);
+ this.shim = createShim();
+ }
+
+ @SuppressWarnings("unchecked")
+ public ProxyAutoBean(AutoBeanFactory factory, Class<?> beanType, Configuration configuration,
+ T toWrap) {
+ super(toWrap, factory);
+ this.beanType = (Class<T>) beanType;
+ this.configuration = configuration;
+ this.data = calculateData(beanType);
+ this.shim = createShim();
+ }
+
+ @Override
+ public T as() {
+ return shim;
+ }
+
+ public Configuration getConfiguration() {
+ return configuration;
+ }
+
+ public Class<T> getType() {
+ return beanType;
+ }
+
+ /**
+ * Allow access by {@link ShimHandler}.
+ */
+ @Override
+ protected void call(String method, Object returned, Object... parameters) {
+ super.call(method, returned, parameters);
+ }
+
+ /**
+ * Allow access by {@link ShimHandler}.
+ */
+ @Override
+ protected void checkFrozen() {
+ super.checkFrozen();
+ }
+
+ /**
+ * Allow access by {@link ShimHandler}.
+ */
+ @Override
+ protected void checkWrapped() {
+ super.checkWrapped();
+ }
+
+ /**
+ * Not used in this implementation. Instead, the simple implementation is
+ * created lazily in {@link #getWrapped()}.
+ */
+ @Override
+ protected T createSimplePeer() {
+ return null;
+ }
+
+ /**
+ * Allow access by {@link ShimHandler}.
+ */
+ @Override
+ protected <V> V get(String method, V toReturn) {
+ return super.get(method, toReturn);
+ }
+
+ /**
+ * Allow access by BeanMethod.
+ */
+ @Override
+ protected <V> V getOrReify(String propertyName) {
+ return super.<V> getOrReify(propertyName);
+ }
+
+ /**
+ * Allow access by {@link ShimHandler}.
+ */
+ @Override
+ protected T getWrapped() {
+ if (wrapped == null && isUsingSimplePeer()) {
+ wrapped = (T) ProxyAutoBean.makeProxy(beanType, new SimpleBeanHandler<T>(this));
+ }
+ return super.getWrapped();
+ }
+
+ /**
+ * Allow access by {@link ShimHandler}.
+ */
+ @Override
+ protected void set(String method, Object value) {
+ 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) {
+ assert data.getters.size() == data.getterNames.size()
+ && data.getters.size() == data.propertyType.size();
+ Iterator<Method> getterIt = data.getters.iterator();
+ Iterator<String> nameIt = data.getterNames.iterator();
+ Iterator<PropertyType> typeIt = data.propertyType.iterator();
+ while (getterIt.hasNext()) {
+ Method getter = getterIt.next();
+ String name = nameIt.next();
+ PropertyType propertyType = typeIt.next();
+
+ // Use the shim to handle automatic wrapping
+ Object value;
+ try {
+ getter.setAccessible(true);
+ value = getter.invoke(shim);
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e.getCause());
+ }
+
+ // Create the context used for the property visitation
+ MethodPropertyContext x =
+ isUsingSimplePeer() ? new BeanPropertyContext(this, getter) : new GetterPropertyContext(
+ this, getter);
+
+ switch (propertyType) {
+ case VALUE: {
+ if (visitor.visitValueProperty(name, value, x)) {
+ }
+ visitor.endVisitValueProperty(name, value, x);
+ break;
+ }
+ case COLLECTION: {
+ // Workaround for generics bug in mac javac 1.6.0_22
+ @SuppressWarnings("rawtypes")
+ AutoBean temp = AutoBeanUtils.getAutoBean((Collection) value);
+ @SuppressWarnings("unchecked")
+ AutoBean<Collection<?>> bean = (AutoBean<Collection<?>>) temp;
+ if (visitor.visitCollectionProperty(name, bean, x)) {
+ if (value != null) {
+ ((ProxyAutoBean<?>) bean).traverse(visitor, ctx);
+ }
+ }
+ visitor.endVisitCollectionProperty(name, bean, x);
+ break;
+ }
+ case MAP: {
+ // Workaround for generics bug in mac javac 1.6.0_22
+ @SuppressWarnings("rawtypes")
+ AutoBean temp = AutoBeanUtils.getAutoBean((Map) value);
+ @SuppressWarnings("unchecked")
+ AutoBean<Map<?, ?>> bean = (AutoBean<Map<?, ?>>) temp;
+ if (visitor.visitMapProperty(name, bean, x)) {
+ if (value != null) {
+ ((ProxyAutoBean<?>) bean).traverse(visitor, ctx);
+ }
+ }
+ visitor.endVisitMapProperty(name, bean, x);
+ break;
+ }
+ case REFERENCE: {
+ ProxyAutoBean<?> bean = (ProxyAutoBean<?>) AutoBeanUtils.getAutoBean(value);
+ if (visitor.visitReferenceProperty(name, bean, x)) {
+ if (value != null) {
+ bean.traverse(visitor, ctx);
+ }
+ }
+ visitor.endVisitReferenceProperty(name, bean, x);
+ break;
+ }
+ }
+ }
+ }
+
+ Class<?> getBeanType() {
+ return beanType;
+ }
+
+ private T createShim() {
+ 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/web/bindery/autobean/vm/impl/ShimHandler.java b/user/src/com/google/web/bindery/autobean/vm/impl/ShimHandler.java
new file mode 100644
index 0000000..95dabe8
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/vm/impl/ShimHandler.java
@@ -0,0 +1,131 @@
+/*
+ * 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.web.bindery.autobean.vm.impl;
+
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanUtils;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * Implements an AutoBean's shim interface that intercepts calls to the backing
+ * object.
+ *
+ * @param <T> the interface type of the AutoBean
+ */
+class ShimHandler<T> implements InvocationHandler {
+ private final ProxyAutoBean<T> bean;
+ private final Method interceptor;
+
+ public ShimHandler(ProxyAutoBean<T> bean, T toWrap) {
+ this.bean = bean;
+
+ Method maybe = null;
+ for (Class<?> clazz : bean.getConfiguration().getCategories()) {
+ try {
+ maybe = clazz.getMethod("__intercept", AutoBean.class, Object.class);
+ break;
+ } catch (SecurityException expected) {
+ } catch (NoSuchMethodException expected) {
+ }
+ }
+ interceptor = maybe;
+ }
+
+ @Override
+ public boolean equals(Object couldBeShim) {
+ if (couldBeShim == null) {
+ return false;
+ }
+ // Handles the foo.equals(foo) case
+ if (Proxy.isProxyClass(couldBeShim.getClass())
+ && this == Proxy.getInvocationHandler(couldBeShim)) {
+ return true;
+ }
+ return bean.getWrapped().equals(couldBeShim);
+ }
+
+ @Override
+ public int hashCode() {
+ return bean.getWrapped().hashCode();
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ method.setAccessible(true);
+ Object toReturn;
+ String name = method.getName();
+ 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();
+ }
+ return toReturn;
+ }
+
+ @Override
+ public String toString() {
+ return bean.getWrapped().toString();
+ }
+
+ private Object maybeWrap(Class<?> intf, Object toReturn) {
+ if (toReturn == null) {
+ return null;
+ }
+ AutoBean<?> returnBean = AutoBeanUtils.getAutoBean(toReturn);
+ if (returnBean != null) {
+ return returnBean.as();
+ }
+ if (TypeUtils.isValueType(intf) || TypeUtils.isValueType(toReturn.getClass())
+ || bean.getConfiguration().getNoWrap().contains(intf)) {
+ return toReturn;
+ }
+ if (toReturn.getClass().isArray()) {
+ /*
+ * We can't reliably wrap arrays, but the only time we typically see an
+ * array is with toArray() call on a collection, since arrays aren't
+ * supported property types.
+ */
+ return toReturn;
+ }
+ ProxyAutoBean<Object> newBean =
+ new ProxyAutoBean<Object>(bean.getFactory(), intf, bean.getConfiguration(), toReturn);
+ return newBean.as();
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/vm/impl/SimpleBeanHandler.java b/user/src/com/google/web/bindery/autobean/vm/impl/SimpleBeanHandler.java
new file mode 100644
index 0000000..a6624a6
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/vm/impl/SimpleBeanHandler.java
@@ -0,0 +1,57 @@
+/*
+ * 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.web.bindery.autobean.vm.impl;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+/**
+ * Dynamic implementation of an AutoBean's simple peer object.
+ *
+ * @param <T> the type of interface the shim allows access to
+ */
+class SimpleBeanHandler<T> implements InvocationHandler {
+ private final ProxyAutoBean<T> bean;
+
+ public SimpleBeanHandler(ProxyAutoBean<T> bean) {
+ this.bean = bean;
+ }
+
+ public ProxyAutoBean<T> getBean() {
+ return bean;
+ }
+
+ /**
+ * Delegates most work to {@link BeanMethod}.
+ */
+ 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);
+ return toReturn;
+ }
+ }
+ throw new RuntimeException("Unhandled invocation " + method.getName());
+ }
+
+ /**
+ * For debugging use only.
+ */
+ @Override
+ public String toString() {
+ return bean.getSplittable().getPayload();
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/vm/impl/TypeUtils.java b/user/src/com/google/web/bindery/autobean/vm/impl/TypeUtils.java
new file mode 100644
index 0000000..73b977e
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/vm/impl/TypeUtils.java
@@ -0,0 +1,175 @@
+/*
+ * 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.web.bindery.autobean.vm.impl;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Shared code for answering question about Class objects. This is a
+ * server-compatible analog to ModelUtils.
+ */
+public class TypeUtils {
+ static final Map<Class<?>, Class<?>> AUTOBOX_MAP;
+ static final Map<Class<?>, Object> DEFAULT_PRIMITIVE_VALUES;
+ @SuppressWarnings("unchecked")
+ static final Set<Class<?>> VALUE_TYPES = Collections.unmodifiableSet(new HashSet<Class<?>>(
+ Arrays.asList(Boolean.class, Character.class, Class.class, Date.class,
+ Enum.class, Number.class, String.class, Void.class)));
+
+ static {
+ Map<Class<?>, Object> temp = new HashMap<Class<?>, Object>();
+ temp.put(boolean.class, false);
+ temp.put(byte.class, (byte) 0);
+ temp.put(char.class, (char) 0);
+ temp.put(double.class, (double) 0);
+ temp.put(float.class, (float) 0);
+ temp.put(int.class, 0);
+ temp.put(long.class, (long) 0);
+ temp.put(short.class, (short) 0);
+ temp.put(void.class, null);
+
+ DEFAULT_PRIMITIVE_VALUES = Collections.unmodifiableMap(temp);
+ }
+
+ static {
+ Map<Class<?>, Class<?>> autoBoxMap = new HashMap<Class<?>, Class<?>>();
+ autoBoxMap.put(boolean.class, Boolean.class);
+ autoBoxMap.put(byte.class, Byte.class);
+ autoBoxMap.put(char.class, Character.class);
+ autoBoxMap.put(double.class, Double.class);
+ autoBoxMap.put(float.class, Float.class);
+ autoBoxMap.put(int.class, Integer.class);
+ autoBoxMap.put(long.class, Long.class);
+ autoBoxMap.put(short.class, Short.class);
+ autoBoxMap.put(void.class, Void.class);
+ AUTOBOX_MAP = Collections.unmodifiableMap(autoBoxMap);
+ }
+
+ /**
+ * Similar to ModelUtils#ensureBaseType(JType) but for the reflection API.
+ */
+ public static Class<?> ensureBaseType(Type type) {
+ if (type instanceof Class<?>) {
+ return (Class<?>) type;
+ }
+ if (type instanceof GenericArrayType) {
+ return Array.newInstance(
+ ensureBaseType(((GenericArrayType) type).getGenericComponentType()),
+ 0).getClass();
+ }
+ if (type instanceof ParameterizedType) {
+ return ensureBaseType(((ParameterizedType) type).getRawType());
+ }
+ if (type instanceof TypeVariable<?>) {
+ return ensureBaseType(((TypeVariable<?>) type).getBounds()[0]);
+ }
+ if (type instanceof WildcardType) {
+ WildcardType wild = (WildcardType) type;
+ return ensureBaseType(wild.getUpperBounds()[0]);
+ }
+ throw new RuntimeException("Cannot handle " + type.getClass().getName());
+ }
+
+ /**
+ * Given a primitive Class type, return a default value.
+ */
+ public static Object getDefaultPrimitiveValue(Class<?> clazz) {
+ assert clazz.isPrimitive() : "Expecting primitive type";
+ return DEFAULT_PRIMITIVE_VALUES.get(clazz);
+ }
+
+ public static Type[] getParameterization(Class<?> intf, Type... types) {
+ for (Type type : types) {
+ if (type == null) {
+ continue;
+ } else if (type instanceof ParameterizedType) {
+ ParameterizedType param = (ParameterizedType) type;
+ Type[] actualTypeArguments = param.getActualTypeArguments();
+ Class<?> base = ensureBaseType(param.getRawType());
+ Type[] typeParameters = base.getTypeParameters();
+
+ Map<Type, Type> map = new HashMap<Type, Type>();
+ for (int i = 0, j = typeParameters.length; i < j; i++) {
+ map.put(typeParameters[i], actualTypeArguments[i]);
+ }
+ Type[] lookFor = intf.equals(base) ? intf.getTypeParameters()
+ : getParameterization(intf, base.getGenericInterfaces());
+ List<Type> toReturn = new ArrayList<Type>();
+ for (int i = 0, j = lookFor.length; i < j; i++) {
+ Type found = map.get(lookFor[i]);
+ if (found != null) {
+ toReturn.add(found);
+ }
+ }
+ return toReturn.toArray(new Type[toReturn.size()]);
+ } else if (type instanceof Class<?>) {
+ Class<?> clazz = (Class<?>) type;
+ if (intf.equals(clazz)) {
+ return intf.getTypeParameters();
+ }
+ Type[] found = getParameterization(intf, clazz.getGenericSuperclass());
+ if (found != null) {
+ return found;
+ }
+ found = getParameterization(intf, clazz.getGenericInterfaces());
+ if (found != null) {
+ return found;
+ }
+ }
+ }
+ return null;
+ }
+
+ public static Type getSingleParameterization(Class<?> intf, Type... types) {
+ Type[] found = getParameterization(intf, types);
+ return found == null ? null : found[0];
+ }
+
+ public static boolean isValueType(Class<?> clazz) {
+ if (clazz.isPrimitive() || VALUE_TYPES.contains(clazz)) {
+ return true;
+ }
+ for (Class<?> c : VALUE_TYPES) {
+ if (c.isAssignableFrom(clazz)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static <V> Class<V> maybeAutobox(Class<V> domainType) {
+ @SuppressWarnings("unchecked")
+ Class<V> autoBoxType = (Class<V>) AUTOBOX_MAP.get(domainType);
+ return autoBoxType == null ? domainType : autoBoxType;
+ }
+
+ private TypeUtils() {
+ }
+}
diff --git a/user/src/com/google/web/bindery/autobean/vm/package-info.java b/user/src/com/google/web/bindery/autobean/vm/package-info.java
new file mode 100644
index 0000000..ef1a6ee
--- /dev/null
+++ b/user/src/com/google/web/bindery/autobean/vm/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+/**
+ * Contains JVM-compatible implementations of the AutoBean framework.
+ * <p>
+ * <span style='color: red'>This is experimental, unsupported code.</span>
+ *
+ * @see <a
+ * href="http://code.google.com/p/google-web-toolkit/wiki/AutoBean">AutoBean
+ * wiki page</a>
+ */
+@com.google.gwt.util.PreventSpuriousRebuilds
+package com.google.web.bindery.autobean.vm;
+
diff --git a/user/src/com/google/web/bindery/requestfactory/RequestFactory.gwt.xml b/user/src/com/google/web/bindery/requestfactory/RequestFactory.gwt.xml
index 279acc4..f55cc8d 100644
--- a/user/src/com/google/web/bindery/requestfactory/RequestFactory.gwt.xml
+++ b/user/src/com/google/web/bindery/requestfactory/RequestFactory.gwt.xml
@@ -17,7 +17,7 @@
-->
<module>
<inherits name='com.google.gwt.core.Core'/>
- <inherits name='com.google.gwt.autobean.AutoBean'/>
+ <inherits name='com.google.web.bindery.autobean.AutoBean'/>
<inherits name='com.google.gwt.editor.Editor'/>
<inherits name='com.google.gwt.http.HTTP'/>
<inherits name='com.google.gwt.logging.LoggingDisabled'/>
diff --git a/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/AbstractRequestFactoryEditorDriver.java b/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/AbstractRequestFactoryEditorDriver.java
index a4c10dd..df21135 100644
--- a/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/AbstractRequestFactoryEditorDriver.java
+++ b/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/AbstractRequestFactoryEditorDriver.java
@@ -15,8 +15,8 @@
*/
package com.google.web.bindery.requestfactory.gwt.client.impl;
-import com.google.gwt.autobean.shared.AutoBean;
-import com.google.gwt.autobean.shared.AutoBeanUtils;
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanUtils;
import com.google.gwt.editor.client.Editor;
import com.google.gwt.editor.client.EditorContext;
import com.google.gwt.editor.client.EditorVisitor;
diff --git a/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/PathCollector.java b/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/PathCollector.java
index e14d3b2..ab4f8aa 100644
--- a/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/PathCollector.java
+++ b/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/PathCollector.java
@@ -15,7 +15,7 @@
*/
package com.google.web.bindery.requestfactory.gwt.client.impl;
-import com.google.gwt.autobean.shared.ValueCodex;
+import com.google.web.bindery.autobean.shared.ValueCodex;
import com.google.gwt.editor.client.EditorContext;
import com.google.gwt.editor.client.EditorVisitor;
@@ -62,4 +62,4 @@
}
return true;
}
-}
\ No newline at end of file
+}
diff --git a/user/src/com/google/web/bindery/requestfactory/gwt/rebind/RequestFactoryGenerator.java b/user/src/com/google/web/bindery/requestfactory/gwt/rebind/RequestFactoryGenerator.java
index 834b5c0..e6d7f5c 100644
--- a/user/src/com/google/web/bindery/requestfactory/gwt/rebind/RequestFactoryGenerator.java
+++ b/user/src/com/google/web/bindery/requestfactory/gwt/rebind/RequestFactoryGenerator.java
@@ -15,13 +15,13 @@
*/
package com.google.web.bindery.requestfactory.gwt.rebind;
-import com.google.gwt.autobean.rebind.model.JBeanMethod;
-import com.google.gwt.autobean.shared.AutoBean;
-import com.google.gwt.autobean.shared.AutoBean.PropertyName;
-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.ExtraEnums;
+import com.google.web.bindery.autobean.gwt.rebind.model.JBeanMethod;
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBean.PropertyName;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory.Category;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory.NoWrap;
+import com.google.web.bindery.autobean.shared.impl.EnumMap.ExtraEnums;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
diff --git a/user/src/com/google/web/bindery/requestfactory/gwt/rebind/model/RequestFactoryModel.java b/user/src/com/google/web/bindery/requestfactory/gwt/rebind/model/RequestFactoryModel.java
index ef1d7e8..6af1dbb 100644
--- a/user/src/com/google/web/bindery/requestfactory/gwt/rebind/model/RequestFactoryModel.java
+++ b/user/src/com/google/web/bindery/requestfactory/gwt/rebind/model/RequestFactoryModel.java
@@ -15,8 +15,8 @@
*/
package com.google.web.bindery.requestfactory.gwt.rebind.model;
-import com.google.gwt.autobean.rebind.model.JBeanMethod;
-import com.google.gwt.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.gwt.rebind.model.JBeanMethod;
+import com.google.web.bindery.autobean.shared.Splittable;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
diff --git a/user/src/com/google/web/bindery/requestfactory/server/ReflectiveServiceLayer.java b/user/src/com/google/web/bindery/requestfactory/server/ReflectiveServiceLayer.java
index f25e0ae..3433518 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/ReflectiveServiceLayer.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/ReflectiveServiceLayer.java
@@ -15,9 +15,9 @@
*/
package com.google.web.bindery.requestfactory.server;
-import com.google.gwt.autobean.server.impl.BeanMethod;
-import com.google.gwt.autobean.server.impl.TypeUtils;
-import com.google.gwt.autobean.shared.ValueCodex;
+import com.google.web.bindery.autobean.shared.ValueCodex;
+import com.google.web.bindery.autobean.vm.impl.BeanMethod;
+import com.google.web.bindery.autobean.vm.impl.TypeUtils;
import com.google.web.bindery.requestfactory.shared.BaseProxy;
import com.google.web.bindery.requestfactory.shared.InstanceRequest;
import com.google.web.bindery.requestfactory.shared.Request;
diff --git a/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryInterfaceValidator.java b/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryInterfaceValidator.java
index dc69783..b6841ab 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryInterfaceValidator.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryInterfaceValidator.java
@@ -15,7 +15,7 @@
*/
package com.google.web.bindery.requestfactory.server;
-import com.google.gwt.autobean.shared.ValueCodex;
+import com.google.web.bindery.autobean.shared.ValueCodex;
import com.google.gwt.dev.asm.AnnotationVisitor;
import com.google.gwt.dev.asm.ClassReader;
import com.google.gwt.dev.asm.ClassVisitor;
diff --git a/user/src/com/google/web/bindery/requestfactory/server/RequestState.java b/user/src/com/google/web/bindery/requestfactory/server/RequestState.java
index 988250f..e167c41 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/RequestState.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/RequestState.java
@@ -15,12 +15,12 @@
*/
package com.google.web.bindery.requestfactory.server;
-import com.google.gwt.autobean.server.AutoBeanFactoryMagic;
-import com.google.gwt.autobean.shared.AutoBean;
-import com.google.gwt.autobean.shared.AutoBeanCodex;
-import com.google.gwt.autobean.shared.Splittable;
-import com.google.gwt.autobean.shared.ValueCodex;
-import com.google.gwt.autobean.shared.impl.StringQuoter;
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanCodex;
+import com.google.web.bindery.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.ValueCodex;
+import com.google.web.bindery.autobean.shared.impl.StringQuoter;
+import com.google.web.bindery.autobean.vm.AutoBeanFactorySource;
import com.google.web.bindery.requestfactory.server.SimpleRequestProcessor.IdToEntityMap;
import com.google.web.bindery.requestfactory.shared.BaseProxy;
import com.google.web.bindery.requestfactory.shared.EntityProxy;
@@ -210,7 +210,7 @@
*/
private <Q extends BaseProxy> AutoBean<Q> createProxyBean(
SimpleProxyId<Q> id, Object domainObject) {
- AutoBean<Q> toReturn = AutoBeanFactoryMagic.createBean(id.getProxyClass(),
+ AutoBean<Q> toReturn = AutoBeanFactorySource.createBean(id.getProxyClass(),
SimpleRequestProcessor.CONFIGURATION);
toReturn.setTag(Constants.STABLE_ID, id);
toReturn.setTag(Constants.DOMAIN_OBJECT, domainObject);
diff --git a/user/src/com/google/web/bindery/requestfactory/server/Resolver.java b/user/src/com/google/web/bindery/requestfactory/server/Resolver.java
index 086adbf..9f641eb 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/Resolver.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/Resolver.java
@@ -15,12 +15,12 @@
*/
package com.google.web.bindery.requestfactory.server;
-import com.google.gwt.autobean.server.impl.TypeUtils;
-import com.google.gwt.autobean.shared.AutoBean;
-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.ValueCodex;
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanUtils;
+import com.google.web.bindery.autobean.shared.AutoBeanVisitor;
+import com.google.web.bindery.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.ValueCodex;
+import com.google.web.bindery.autobean.vm.impl.TypeUtils;
import com.google.web.bindery.requestfactory.shared.BaseProxy;
import com.google.web.bindery.requestfactory.shared.EntityProxy;
import com.google.web.bindery.requestfactory.shared.EntityProxyId;
@@ -447,4 +447,4 @@
throw new ReportableException("Unsupported domain type "
+ returnClass.getCanonicalName());
}
-}
\ No newline at end of file
+}
diff --git a/user/src/com/google/web/bindery/requestfactory/server/ResolverServiceLayer.java b/user/src/com/google/web/bindery/requestfactory/server/ResolverServiceLayer.java
index e9f6914..f95e896 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/ResolverServiceLayer.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/ResolverServiceLayer.java
@@ -15,7 +15,7 @@
*/
package com.google.web.bindery.requestfactory.server;
-import com.google.gwt.autobean.server.impl.TypeUtils;
+import com.google.web.bindery.autobean.vm.impl.TypeUtils;
import com.google.web.bindery.requestfactory.shared.BaseProxy;
import com.google.web.bindery.requestfactory.shared.EntityProxy;
import com.google.web.bindery.requestfactory.shared.EntityProxyId;
diff --git a/user/src/com/google/web/bindery/requestfactory/server/SimpleRequestProcessor.java b/user/src/com/google/web/bindery/requestfactory/server/SimpleRequestProcessor.java
index c09a46a..a763dbb 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/SimpleRequestProcessor.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/SimpleRequestProcessor.java
@@ -15,16 +15,16 @@
*/
package com.google.web.bindery.requestfactory.server;
-import com.google.gwt.autobean.server.AutoBeanFactoryMagic;
-import com.google.gwt.autobean.server.Configuration;
-import com.google.gwt.autobean.server.impl.TypeUtils;
-import com.google.gwt.autobean.shared.AutoBean;
-import com.google.gwt.autobean.shared.AutoBeanCodex;
-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.ValueCodex;
import com.google.gwt.user.server.Base64Utils;
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanCodex;
+import com.google.web.bindery.autobean.shared.AutoBeanUtils;
+import com.google.web.bindery.autobean.shared.AutoBeanVisitor;
+import com.google.web.bindery.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.ValueCodex;
+import com.google.web.bindery.autobean.vm.AutoBeanFactorySource;
+import com.google.web.bindery.autobean.vm.Configuration;
+import com.google.web.bindery.autobean.vm.impl.TypeUtils;
import com.google.web.bindery.requestfactory.shared.BaseProxy;
import com.google.web.bindery.requestfactory.shared.EntityProxyId;
import com.google.web.bindery.requestfactory.shared.InstanceRequest;
@@ -37,6 +37,7 @@
import com.google.web.bindery.requestfactory.shared.impl.EntityProxyCategory;
import com.google.web.bindery.requestfactory.shared.impl.SimpleProxyId;
import com.google.web.bindery.requestfactory.shared.impl.ValueProxyCategory;
+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.MessageFactory;
import com.google.web.bindery.requestfactory.shared.messages.OperationMessage;
@@ -44,7 +45,6 @@
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.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
@@ -86,7 +86,7 @@
/**
* Vends message objects.
*/
- static final MessageFactory FACTORY = AutoBeanFactoryMagic.create(MessageFactory.class);
+ static final MessageFactory FACTORY = AutoBeanFactorySource.create(MessageFactory.class);
static String fromBase64(String encoded) {
try {
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/DefaultProxyStore.java b/user/src/com/google/web/bindery/requestfactory/shared/DefaultProxyStore.java
index f594bdc..44dcbce 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/DefaultProxyStore.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/DefaultProxyStore.java
@@ -15,9 +15,9 @@
*/
package com.google.web.bindery.requestfactory.shared;
-import com.google.gwt.autobean.shared.AutoBean;
-import com.google.gwt.autobean.shared.AutoBeanCodex;
-import com.google.gwt.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanCodex;
+import com.google.web.bindery.autobean.shared.Splittable;
import com.google.web.bindery.requestfactory.shared.impl.MessageFactoryHolder;
import com.google.web.bindery.requestfactory.shared.messages.OperationMessage;
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/ProxyStore.java b/user/src/com/google/web/bindery/requestfactory/shared/ProxyStore.java
index 4040217..ffb3f87 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/ProxyStore.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/ProxyStore.java
@@ -15,7 +15,7 @@
*/
package com.google.web.bindery.requestfactory.shared;
-import com.google.gwt.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.Splittable;
/**
* A ProxyStore provides a {@link ProxySerializer} with access to a low-level
@@ -50,4 +50,4 @@
* @see Splittable#getPayload()
*/
void put(String key, Splittable value);
-}
\ No newline at end of file
+}
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequest.java b/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequest.java
index fad1919..f7faed5 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequest.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequest.java
@@ -15,7 +15,7 @@
*/
package com.google.web.bindery.requestfactory.shared.impl;
-import com.google.gwt.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.Splittable;
import com.google.web.bindery.requestfactory.shared.BaseProxy;
import com.google.web.bindery.requestfactory.shared.InstanceRequest;
import com.google.web.bindery.requestfactory.shared.Receiver;
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..dd61fa2 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
@@ -19,16 +19,16 @@
import static com.google.web.bindery.requestfactory.shared.impl.Constants.REQUEST_CONTEXT;
import static com.google.web.bindery.requestfactory.shared.impl.Constants.STABLE_ID;
-import com.google.gwt.autobean.shared.AutoBean;
-import com.google.gwt.autobean.shared.AutoBeanCodex;
-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.ValueCodex;
-import com.google.gwt.autobean.shared.impl.AbstractAutoBean;
-import com.google.gwt.autobean.shared.impl.EnumMap;
-import com.google.gwt.autobean.shared.impl.StringQuoter;
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanCodex;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory;
+import com.google.web.bindery.autobean.shared.AutoBeanUtils;
+import com.google.web.bindery.autobean.shared.AutoBeanVisitor;
+import com.google.web.bindery.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.ValueCodex;
+import com.google.web.bindery.autobean.shared.impl.AbstractAutoBean;
+import com.google.web.bindery.autobean.shared.impl.EnumMap;
+import com.google.web.bindery.autobean.shared.impl.StringQuoter;
import com.google.gwt.event.shared.UmbrellaException;
import com.google.web.bindery.requestfactory.shared.BaseProxy;
import com.google.web.bindery.requestfactory.shared.EntityProxy;
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestFactory.java b/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestFactory.java
index 37a9c3d..a13e62d 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestFactory.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestFactory.java
@@ -15,7 +15,7 @@
*/
package com.google.web.bindery.requestfactory.shared.impl;
-import com.google.gwt.autobean.shared.AutoBeanFactory;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory;
import com.google.gwt.event.shared.EventBus;
import com.google.web.bindery.requestfactory.shared.EntityProxy;
import com.google.web.bindery.requestfactory.shared.EntityProxyId;
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/impl/BaseProxyCategory.java b/user/src/com/google/web/bindery/requestfactory/shared/impl/BaseProxyCategory.java
index 7dea94b..481f4c8 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/impl/BaseProxyCategory.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/impl/BaseProxyCategory.java
@@ -18,8 +18,8 @@
import static com.google.web.bindery.requestfactory.shared.impl.Constants.REQUEST_CONTEXT;
import static com.google.web.bindery.requestfactory.shared.impl.Constants.STABLE_ID;
-import com.google.gwt.autobean.shared.AutoBean;
-import com.google.gwt.autobean.shared.AutoBeanUtils;
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanUtils;
import com.google.web.bindery.requestfactory.shared.BaseProxy;
/**
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..f498ac3 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
@@ -15,11 +15,11 @@
*/
package com.google.web.bindery.requestfactory.shared.impl;
-import com.google.gwt.autobean.shared.AutoBean;
-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.StringQuoter;
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanUtils;
+import com.google.web.bindery.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.ValueCodex;
+import com.google.web.bindery.autobean.shared.impl.StringQuoter;
import com.google.web.bindery.requestfactory.shared.BaseProxy;
import com.google.web.bindery.requestfactory.shared.EntityProxyId;
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/impl/EntityProxyCategory.java b/user/src/com/google/web/bindery/requestfactory/shared/impl/EntityProxyCategory.java
index bc5e04a..4349d23 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/impl/EntityProxyCategory.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/impl/EntityProxyCategory.java
@@ -18,8 +18,8 @@
import static com.google.web.bindery.requestfactory.shared.impl.BaseProxyCategory.requestContext;
import static com.google.web.bindery.requestfactory.shared.impl.Constants.STABLE_ID;
-import com.google.gwt.autobean.shared.AutoBean;
-import com.google.gwt.autobean.shared.AutoBeanUtils;
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanUtils;
import com.google.web.bindery.requestfactory.shared.EntityProxy;
/**
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/impl/MessageFactoryHolder.java b/user/src/com/google/web/bindery/requestfactory/shared/impl/MessageFactoryHolder.java
index 160a2eb..9dc6b4a 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/impl/MessageFactoryHolder.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/impl/MessageFactoryHolder.java
@@ -15,12 +15,12 @@
*/
package com.google.web.bindery.requestfactory.shared.impl;
-import com.google.gwt.autobean.server.AutoBeanFactoryMagic;
+import com.google.web.bindery.autobean.vm.AutoBeanFactorySource;
import com.google.web.bindery.requestfactory.shared.messages.MessageFactory;
/**
* This class has a super-source version with a client-only implementation.
*/
public interface MessageFactoryHolder {
- MessageFactory FACTORY = AutoBeanFactoryMagic.create(MessageFactory.class);
+ MessageFactory FACTORY = AutoBeanFactorySource.create(MessageFactory.class);
}
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/impl/ProxySerializerImpl.java b/user/src/com/google/web/bindery/requestfactory/shared/impl/ProxySerializerImpl.java
index 9b3797c..f13942b 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/impl/ProxySerializerImpl.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/impl/ProxySerializerImpl.java
@@ -15,12 +15,12 @@
*/
package com.google.web.bindery.requestfactory.shared.impl;
-import com.google.gwt.autobean.shared.AutoBean;
-import com.google.gwt.autobean.shared.AutoBeanCodex;
-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.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanCodex;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory;
+import com.google.web.bindery.autobean.shared.AutoBeanUtils;
+import com.google.web.bindery.autobean.shared.AutoBeanVisitor;
+import com.google.web.bindery.autobean.shared.Splittable;
import com.google.web.bindery.requestfactory.shared.BaseProxy;
import com.google.web.bindery.requestfactory.shared.EntityProxy;
import com.google.web.bindery.requestfactory.shared.EntityProxyId;
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/impl/ValueProxyCategory.java b/user/src/com/google/web/bindery/requestfactory/shared/impl/ValueProxyCategory.java
index 775673a..95c5ca2 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/impl/ValueProxyCategory.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/impl/ValueProxyCategory.java
@@ -17,8 +17,8 @@
import static com.google.web.bindery.requestfactory.shared.impl.BaseProxyCategory.stableId;
-import com.google.gwt.autobean.shared.AutoBean;
-import com.google.gwt.autobean.shared.AutoBeanUtils;
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanUtils;
import com.google.web.bindery.requestfactory.shared.ValueProxy;
/**
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/messages/IdMessage.java b/user/src/com/google/web/bindery/requestfactory/shared/messages/IdMessage.java
index 48e42b2..6d1d6c1 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/messages/IdMessage.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/messages/IdMessage.java
@@ -15,7 +15,7 @@
*/
package com.google.web.bindery.requestfactory.shared.messages;
-import com.google.gwt.autobean.shared.AutoBean.PropertyName;
+import com.google.web.bindery.autobean.shared.AutoBean.PropertyName;
/**
* Used as a base type for messages that are about a particular id.
@@ -81,4 +81,4 @@
@PropertyName(TYPE_TOKEN)
void setTypeToken(String value);
-}
\ No newline at end of file
+}
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/messages/InvocationMessage.java b/user/src/com/google/web/bindery/requestfactory/shared/messages/InvocationMessage.java
index 38a124f..00fcac3 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/messages/InvocationMessage.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/messages/InvocationMessage.java
@@ -15,8 +15,8 @@
*/
package com.google.web.bindery.requestfactory.shared.messages;
-import com.google.gwt.autobean.shared.AutoBean.PropertyName;
-import com.google.gwt.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.AutoBean.PropertyName;
+import com.google.web.bindery.autobean.shared.Splittable;
import java.util.List;
import java.util.Set;
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/messages/JsonRpcRequest.java b/user/src/com/google/web/bindery/requestfactory/shared/messages/JsonRpcRequest.java
index 399a214..275c7d5 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/messages/JsonRpcRequest.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/messages/JsonRpcRequest.java
@@ -15,8 +15,8 @@
*/
package com.google.web.bindery.requestfactory.shared.messages;
-import com.google.gwt.autobean.shared.AutoBean.PropertyName;
-import com.google.gwt.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.AutoBean.PropertyName;
+import com.google.web.bindery.autobean.shared.Splittable;
import java.util.Map;
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/messages/MessageFactory.java b/user/src/com/google/web/bindery/requestfactory/shared/messages/MessageFactory.java
index 09fe6d6..e22048b 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/messages/MessageFactory.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/messages/MessageFactory.java
@@ -15,8 +15,8 @@
*/
package com.google.web.bindery.requestfactory.shared.messages;
-import com.google.gwt.autobean.shared.AutoBean;
-import com.google.gwt.autobean.shared.AutoBeanFactory;
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory;
/**
* The factory for creating RequestFactory wire messages.
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/messages/OperationMessage.java b/user/src/com/google/web/bindery/requestfactory/shared/messages/OperationMessage.java
index 0fff04f..9ed7e67 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/messages/OperationMessage.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/messages/OperationMessage.java
@@ -15,8 +15,8 @@
*/
package com.google.web.bindery.requestfactory.shared.messages;
-import com.google.gwt.autobean.shared.Splittable;
-import com.google.gwt.autobean.shared.AutoBean.PropertyName;
+import com.google.web.bindery.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.AutoBean.PropertyName;
import com.google.web.bindery.requestfactory.shared.WriteOperation;
import java.util.Map;
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/messages/RequestMessage.java b/user/src/com/google/web/bindery/requestfactory/shared/messages/RequestMessage.java
index d8dc38d..8f35588 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/messages/RequestMessage.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/messages/RequestMessage.java
@@ -15,7 +15,7 @@
*/
package com.google.web.bindery.requestfactory.shared.messages;
-import com.google.gwt.autobean.shared.AutoBean.PropertyName;
+import com.google.web.bindery.autobean.shared.AutoBean.PropertyName;
import java.util.List;
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/messages/ResponseMessage.java b/user/src/com/google/web/bindery/requestfactory/shared/messages/ResponseMessage.java
index 08d9a91..47efd4d 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/messages/ResponseMessage.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/messages/ResponseMessage.java
@@ -15,8 +15,8 @@
*/
package com.google.web.bindery.requestfactory.shared.messages;
-import com.google.gwt.autobean.shared.AutoBean.PropertyName;
-import com.google.gwt.autobean.shared.Splittable;
+import com.google.web.bindery.autobean.shared.AutoBean.PropertyName;
+import com.google.web.bindery.autobean.shared.Splittable;
import java.util.List;
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/messages/ServerFailureMessage.java b/user/src/com/google/web/bindery/requestfactory/shared/messages/ServerFailureMessage.java
index cda32c3..7e101e3 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/messages/ServerFailureMessage.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/messages/ServerFailureMessage.java
@@ -15,7 +15,7 @@
*/
package com.google.web.bindery.requestfactory.shared.messages;
-import com.google.gwt.autobean.shared.AutoBean.PropertyName;
+import com.google.web.bindery.autobean.shared.AutoBean.PropertyName;
/**
* Encapsulates a ServerFailure object.
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/messages/VersionedMessage.java b/user/src/com/google/web/bindery/requestfactory/shared/messages/VersionedMessage.java
index ea3d06b..51fe40e 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/messages/VersionedMessage.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/messages/VersionedMessage.java
@@ -15,7 +15,7 @@
*/
package com.google.web.bindery.requestfactory.shared.messages;
-import com.google.gwt.autobean.shared.AutoBean.PropertyName;
+import com.google.web.bindery.autobean.shared.AutoBean.PropertyName;
/**
* Describes a message that contains version information.
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/messages/ViolationMessage.java b/user/src/com/google/web/bindery/requestfactory/shared/messages/ViolationMessage.java
index e7b86d2..5b7725f 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/messages/ViolationMessage.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/messages/ViolationMessage.java
@@ -15,7 +15,7 @@
*/
package com.google.web.bindery.requestfactory.shared.messages;
-import com.google.gwt.autobean.shared.AutoBean.PropertyName;
+import com.google.web.bindery.autobean.shared.AutoBean.PropertyName;
/**
* Represents a ConstraintViolation.
diff --git a/user/src/com/google/web/bindery/requestfactory/vm/InProcessRequestContext.java b/user/src/com/google/web/bindery/requestfactory/vm/InProcessRequestContext.java
index cd63ab9..bcdb434 100644
--- a/user/src/com/google/web/bindery/requestfactory/vm/InProcessRequestContext.java
+++ b/user/src/com/google/web/bindery/requestfactory/vm/InProcessRequestContext.java
@@ -15,10 +15,10 @@
*/
package com.google.web.bindery.requestfactory.vm;
-import com.google.gwt.autobean.server.impl.BeanMethod;
-import com.google.gwt.autobean.server.impl.TypeUtils;
-import com.google.gwt.autobean.shared.AutoBean.PropertyName;
-import com.google.gwt.autobean.shared.AutoBeanFactory;
+import com.google.web.bindery.autobean.shared.AutoBean.PropertyName;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory;
+import com.google.web.bindery.autobean.vm.impl.BeanMethod;
+import com.google.web.bindery.autobean.vm.impl.TypeUtils;
import com.google.web.bindery.requestfactory.shared.InstanceRequest;
import com.google.web.bindery.requestfactory.shared.JsonRpcContent;
import com.google.web.bindery.requestfactory.shared.JsonRpcWireName;
diff --git a/user/src/com/google/web/bindery/requestfactory/vm/InProcessRequestFactory.java b/user/src/com/google/web/bindery/requestfactory/vm/InProcessRequestFactory.java
index 718c324..3d931ca 100644
--- a/user/src/com/google/web/bindery/requestfactory/vm/InProcessRequestFactory.java
+++ b/user/src/com/google/web/bindery/requestfactory/vm/InProcessRequestFactory.java
@@ -15,10 +15,10 @@
*/
package com.google.web.bindery.requestfactory.vm;
-import com.google.gwt.autobean.server.AutoBeanFactoryMagic;
-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.web.bindery.autobean.shared.AutoBeanFactory;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory.Category;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory.NoWrap;
+import com.google.web.bindery.autobean.vm.AutoBeanFactorySource;
import com.google.gwt.event.shared.EventBus;
import com.google.web.bindery.requestfactory.shared.BaseProxy;
import com.google.web.bindery.requestfactory.shared.EntityProxy;
@@ -92,7 +92,7 @@
@Override
protected AutoBeanFactory getAutoBeanFactory() {
- return AutoBeanFactoryMagic.create(Factory.class);
+ return AutoBeanFactorySource.create(Factory.class);
}
@Override