Resolve GWT issue 5364.
Enable RequestFactoryPolymorphicTest.
Consolidate base-type code in ModelUtils.
Patch by: bobv
Review by: rjrjr

Review at http://gwt-code-reviews.appspot.com/992801


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9058 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/editor/rebind/AutoBeanFactoryGenerator.java b/user/src/com/google/gwt/editor/rebind/AutoBeanFactoryGenerator.java
index f4698bc..0c0dae5 100644
--- a/user/src/com/google/gwt/editor/rebind/AutoBeanFactoryGenerator.java
+++ b/user/src/com/google/gwt/editor/rebind/AutoBeanFactoryGenerator.java
@@ -31,13 +31,14 @@
 import com.google.gwt.editor.client.AutoBeanVisitor;
 import com.google.gwt.editor.client.AutoBeanVisitor.PropertyContext;
 import com.google.gwt.editor.client.impl.AbstractAutoBean;
-import com.google.gwt.editor.client.impl.AbstractAutoBeanFactory;
 import com.google.gwt.editor.client.impl.AbstractAutoBean.OneShotContext;
+import com.google.gwt.editor.client.impl.AbstractAutoBeanFactory;
 import com.google.gwt.editor.rebind.model.AutoBeanFactoryMethod;
 import com.google.gwt.editor.rebind.model.AutoBeanFactoryModel;
 import com.google.gwt.editor.rebind.model.AutoBeanMethod;
-import com.google.gwt.editor.rebind.model.AutoBeanType;
 import com.google.gwt.editor.rebind.model.AutoBeanMethod.Action;
+import com.google.gwt.editor.rebind.model.AutoBeanType;
+import com.google.gwt.editor.rebind.model.ModelUtils;
 import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
 import com.google.gwt.user.rebind.SourceWriter;
 
@@ -91,8 +92,8 @@
     StringBuilder parameters = new StringBuilder();
     for (JParameter param : jmethod.getParameters()) {
       parameters.append(",").append(
-          AutoBeanFactoryModel.ensureBaseType(param.getType()).getQualifiedSourceName()).append(
-          " ").append(param.getName());
+          ModelUtils.getQualifiedBaseName(param.getType())).append(" ").append(
+          param.getName());
     }
     if (parameters.length() > 0) {
       parameters = parameters.deleteCharAt(0);
@@ -102,13 +103,12 @@
     if (jmethod.getThrows().length > 0) {
       for (JType thrown : jmethod.getThrows()) {
         throwsDeclaration.append(". ").append(
-            AutoBeanFactoryModel.ensureBaseType(thrown).getQualifiedSourceName());
+            ModelUtils.getQualifiedBaseName(thrown));
       }
       throwsDeclaration.deleteCharAt(0);
       throwsDeclaration.insert(0, "throws ");
     }
-    String returnName = AutoBeanFactoryModel.ensureBaseType(
-        jmethod.getReturnType()).getQualifiedSourceName();
+    String returnName = ModelUtils.getQualifiedBaseName(jmethod.getReturnType());
     assert !returnName.contains("extends");
     return String.format("%s %s(%s) %s", returnName, jmethod.getName(),
         parameters, throwsDeclaration);
@@ -225,9 +225,8 @@
             sw.println("}");
           } else {
             // return (ReturnType) values.get(\"foo\");
-            sw.println(
-                "return (%s) values.get(\"%s\");",
-                AutoBeanFactoryModel.ensureBaseType(jmethod.getReturnType()).getQualifiedSourceName(),
+            sw.println("return (%s) values.get(\"%s\");",
+                ModelUtils.getQualifiedBaseName(jmethod.getReturnType()),
                 method.getPropertyName());
           }
         }
@@ -411,7 +410,7 @@
           // Foo toReturn=FooAutoBean.this.get("getFoo", getWrapped().getFoo());
           sw.println(
               "%s toReturn = %3$s.this.get(\"%2$s\", getWrapped().%2$s());",
-              AutoBeanFactoryModel.ensureBaseType(jmethod.getReturnType()).getQualifiedSourceName(),
+              ModelUtils.getQualifiedBaseName(jmethod.getReturnType()),
               methodName, type.getSimpleSourceName());
 
           // Non-value types might need to be wrapped
@@ -442,7 +441,7 @@
             // Type toReturn = getWrapped().doFoo(params);
             sw.println(
                 "%s toReturn = %s.this.getWrapped().%s(%s);",
-                AutoBeanFactoryModel.ensureBaseType(jmethod.getReturnType()).getQualifiedSourceName(),
+                ModelUtils.ensureBaseType(jmethod.getReturnType()).getQualifiedSourceName(),
                 type.getSimpleSourceName(), methodName, arguments);
             // Non-value types might need to be wrapped
             writeReturnWrapper(sw, type, method);
diff --git a/user/src/com/google/gwt/editor/rebind/model/AutoBeanFactoryModel.java b/user/src/com/google/gwt/editor/rebind/model/AutoBeanFactoryModel.java
index 8fac3b2..c8d6646 100644
--- a/user/src/com/google/gwt/editor/rebind/model/AutoBeanFactoryModel.java
+++ b/user/src/com/google/gwt/editor/rebind/model/AutoBeanFactoryModel.java
@@ -17,7 +17,6 @@
 
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.core.ext.typeinfo.JArrayType;
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JGenericType;
 import com.google.gwt.core.ext.typeinfo.JMethod;
@@ -47,28 +46,6 @@
 public class AutoBeanFactoryModel {
   private static final JType[] EMPTY_JTYPE = new JType[0];
 
-  @SuppressWarnings("unchecked")
-  public static <T extends JType> T ensureBaseType(T maybeParameterized) {
-    if (maybeParameterized.isArray() != null) {
-      JArrayType array = maybeParameterized.isArray();
-      return (T) array.getOracle().getArrayType(
-          ensureBaseType(array.getComponentType()));
-    }
-    if (maybeParameterized.isTypeParameter() != null) {
-      return (T) maybeParameterized.isTypeParameter().getBaseType();
-    }
-    if (maybeParameterized.isParameterized() != null) {
-      return (T) maybeParameterized.isParameterized().getBaseType();
-    }
-    if (maybeParameterized.isRawType() != null) {
-      return (T) maybeParameterized.isRawType().getBaseType();
-    }
-    if (maybeParameterized.isWildcard() != null) {
-      return (T) maybeParameterized.isWildcard().getBaseType();
-    }
-    return maybeParameterized;
-  }
-
   private final JGenericType autoBeanInterface;
   private final JClassType autoBeanFactoryInterface;
   private final List<JClassType> categoryTypes;
@@ -212,7 +189,7 @@
   }
 
   public AutoBeanType getPeer(JClassType beanType) {
-    beanType = ensureBaseType(beanType);
+    beanType = ModelUtils.ensureBaseType(beanType);
     return peers.get(beanType);
   }
 
@@ -377,7 +354,7 @@
   }
 
   private AutoBeanType getAutoBeanType(JClassType beanType) {
-    beanType = ensureBaseType(beanType);
+    beanType = ModelUtils.ensureBaseType(beanType);
     AutoBeanType toReturn = peers.get(beanType);
     if (toReturn == null) {
       AutoBeanType.Builder builder = new AutoBeanType.Builder();
@@ -413,7 +390,7 @@
 
     // Check using base types to account for erasure semantics
     JParameterizedType expectedFirst = oracle.getParameterizedType(
-        autoBeanInterface, new JClassType[] {ensureBaseType(beanType)});
+        autoBeanInterface, new JClassType[] {ModelUtils.ensureBaseType(beanType)});
     return expectedFirst.isAssignableTo(paramAsClass);
   }
 
diff --git a/user/src/com/google/gwt/editor/rebind/model/ModelUtils.java b/user/src/com/google/gwt/editor/rebind/model/ModelUtils.java
index d69f295..1a2c815 100644
--- a/user/src/com/google/gwt/editor/rebind/model/ModelUtils.java
+++ b/user/src/com/google/gwt/editor/rebind/model/ModelUtils.java
@@ -1,12 +1,12 @@
 /*
  * 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
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.editor.rebind.model;
 
+import com.google.gwt.core.ext.typeinfo.JArrayType;
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JParameterizedType;
 import com.google.gwt.core.ext.typeinfo.JType;
@@ -53,6 +54,28 @@
     AUTOBOX_MAP = Collections.unmodifiableMap(autoBoxMap);
   }
 
+  @SuppressWarnings("unchecked")
+  public static <T extends JType> T ensureBaseType(T maybeParameterized) {
+    if (maybeParameterized.isArray() != null) {
+      JArrayType array = maybeParameterized.isArray();
+      return (T) array.getOracle().getArrayType(
+          ensureBaseType(array.getComponentType()));
+    }
+    if (maybeParameterized.isTypeParameter() != null) {
+      return (T) maybeParameterized.isTypeParameter().getBaseType();
+    }
+    if (maybeParameterized.isParameterized() != null) {
+      return (T) maybeParameterized.isParameterized().getBaseType();
+    }
+    if (maybeParameterized.isRawType() != null) {
+      return (T) maybeParameterized.isRawType().getBaseType();
+    }
+    if (maybeParameterized.isWildcard() != null) {
+      return (T) maybeParameterized.isWildcard().getBaseType();
+    }
+    return maybeParameterized;
+  }
+
   public static JClassType[] findParameterizationOf(JClassType intfType,
       JClassType subType) {
     assert intfType.isAssignableFrom(subType) : subType.getParameterizedQualifiedSourceName()
@@ -71,6 +94,15 @@
     return null;
   }
 
+  /**
+   * Given a JType, return the source name of the class that is most proximately
+   * assignable to the type. This method will resolve type parameters as well as
+   * wildcard types.
+   */
+  public static String getQualifiedBaseName(JType type) {
+    return ensureBaseType(type).getErasedType().getQualifiedSourceName();
+  }
+
   public static boolean isValueType(TypeOracle oracle, JType type) {
     JClassType classType = type.isClassOrInterface();
     if (classType == null) {
diff --git a/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java b/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java
index bb26a22..53aaeed 100644
--- a/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java
+++ b/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java
@@ -23,11 +23,13 @@
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JMethod;
 import com.google.gwt.core.ext.typeinfo.JParameter;
+import com.google.gwt.core.ext.typeinfo.JTypeParameter;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.editor.client.AutoBean;
 import com.google.gwt.editor.client.AutoBeanFactory;
 import com.google.gwt.editor.client.AutoBeanFactory.Category;
 import com.google.gwt.editor.client.AutoBeanFactory.NoWrap;
+import com.google.gwt.editor.rebind.model.ModelUtils;
 import com.google.gwt.requestfactory.client.impl.AbstractRequest;
 import com.google.gwt.requestfactory.client.impl.AbstractRequestContext;
 import com.google.gwt.requestfactory.client.impl.AbstractRequestFactory;
@@ -158,11 +160,17 @@
         StringBuilder parameterArray = new StringBuilder();
         // final Foo foo, final Bar bar, final Baz baz
         StringBuilder parameterDeclaration = new StringBuilder();
+        // <P extends Blah>
+        StringBuilder typeParameterDeclaration = new StringBuilder();
 
         if (request.isInstance()) {
           // Leave a spot for the using() method to fill in later
           parameterArray.append(",null");
         }
+        for (JTypeParameter param : jmethod.getTypeParameters()) {
+          typeParameterDeclaration.append(",").append(
+              param.getQualifiedSourceName());
+        }
         for (JParameter param : jmethod.getParameters()) {
           parameterArray.append(",").append(param.getName());
           parameterDeclaration.append(",final ").append(
@@ -175,15 +183,17 @@
         if (parameterDeclaration.length() > 0) {
           parameterDeclaration.deleteCharAt(0);
         }
+        if (typeParameterDeclaration.length() > 0) {
+          typeParameterDeclaration.deleteCharAt(0).insert(0, "<").append(">");
+        }
 
         // public Request<Foo> doFoo(final Foo foo) {
-        sw.println("public %s %s(%s) {",
+        sw.println("public %s %s %s(%s) {", typeParameterDeclaration,
             jmethod.getReturnType().getParameterizedQualifiedSourceName(),
             jmethod.getName(), parameterDeclaration);
         sw.indent();
-        // Have to cover the old Request sub-interfaces
-        // TODO: ProxyListRequest et al. be removed?
-        // class X extends AbstractRequest<Return> implements ReturnType {
+        // The implements clause covers InstanceRequest
+        // class X extends AbstractRequest<Return> implements Request<Return> {
         sw.println("class X extends %s<%s> implements %s {",
             AbstractRequest.class.getCanonicalName(),
             request.getDataType().getParameterizedQualifiedSourceName(),
@@ -221,16 +231,19 @@
           // decodeReturnObjectList(FooEntityProxy.class,obj, (List)decoded);
           String decodeMethod = request.isValueType() ? "decodeReturnValueList"
               : "decodeReturnObjectList";
-          sw.println("%s(%s.class, obj, (%s)decoded);", decodeMethod,
-              request.getCollectionElementType().getQualifiedSourceName(),
+          sw.println(
+              "%s(%s.class, obj, (%s)decoded);",
+              decodeMethod,
+              ModelUtils.getQualifiedBaseName(request.getCollectionElementType()),
               collectionType.getCanonicalName());
         } else if (request.isValueType()) {
           // decoded = ValueCodex.cFString(Integer.class, String.valueOf(obj));
           sw.println(
               "decoded = %s.convertFromString(%s.class, String.valueOf(obj));",
               ValueCodex.class.getCanonicalName(),
-              request.getDataType().getQualifiedSourceName());
+              ModelUtils.getQualifiedBaseName(request.getDataType()));
         } else if (request.isEntityType()) {
+          // Implicitly erased
           sw.println("decoded = decodeReturnObject(%s.class, obj);",
               request.getEntityType().getQualifiedSourceName());
         } else {
diff --git a/user/src/com/google/gwt/requestfactory/rebind/model/EntityProxyModel.java b/user/src/com/google/gwt/requestfactory/rebind/model/EntityProxyModel.java
index 418448a..52989d1 100644
--- a/user/src/com/google/gwt/requestfactory/rebind/model/EntityProxyModel.java
+++ b/user/src/com/google/gwt/requestfactory/rebind/model/EntityProxyModel.java
@@ -49,6 +49,7 @@
     }
 
     public void setQualifiedSourceName(String name) {
+      assert !name.contains(" ");
       toReturn.qualifiedSourceName = name;
     }
 
diff --git a/user/src/com/google/gwt/requestfactory/rebind/model/RequestFactoryModel.java b/user/src/com/google/gwt/requestfactory/rebind/model/RequestFactoryModel.java
index 9165f71..6684536 100644
--- a/user/src/com/google/gwt/requestfactory/rebind/model/RequestFactoryModel.java
+++ b/user/src/com/google/gwt/requestfactory/rebind/model/RequestFactoryModel.java
@@ -1,12 +1,12 @@
 /*
  * 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
@@ -164,8 +164,8 @@
           contextType.getQualifiedSourceName(), Service.class.getSimpleName());
       return;
     }
-      Class<?> serviceClass = serviceAnnotation.value();
-      contextBuilder.setServiceClass(serviceClass);
+    Class<?> serviceClass = serviceAnnotation.value();
+    contextBuilder.setServiceClass(serviceClass);
 
     List<RequestMethod> requestMethods = new ArrayList<RequestMethod>();
     for (JMethod method : contextType.getInheritableMethods()) {
@@ -177,7 +177,8 @@
       RequestMethod.Builder methodBuilder = new RequestMethod.Builder();
       methodBuilder.setDeclarationMethod(method);
 
-      if (!validateContextMethodAndSetDataType(methodBuilder, method, serviceClass)) {
+      if (!validateContextMethodAndSetDataType(methodBuilder, method,
+          serviceClass)) {
         continue;
       }
 
@@ -208,6 +209,7 @@
 
   private EntityProxyModel getEntityProxyType(JClassType entityProxyType)
       throws UnableToCompleteException {
+    entityProxyType = ModelUtils.ensureBaseType(entityProxyType);
     EntityProxyModel toReturn = peers.get(entityProxyType);
     if (toReturn == null) {
       EntityProxyModel.Builder inProgress = peerBuilders.get(entityProxyType);
@@ -219,7 +221,7 @@
       EntityProxyModel.Builder builder = new EntityProxyModel.Builder();
       peerBuilders.put(entityProxyType, builder);
 
-      builder.setQualifiedSourceName(entityProxyType.getQualifiedSourceName());
+      builder.setQualifiedSourceName(ModelUtils.getQualifiedBaseName(entityProxyType));
 
       // Get the server domain object type
       ProxyFor proxyFor = entityProxyType.getAnnotation(ProxyFor.class);
@@ -301,7 +303,7 @@
    * Examine a RequestContext method to see if it returns a transportable type.
    */
   private boolean validateContextMethodAndSetDataType(
-          RequestMethod.Builder methodBuilder, JMethod method, Class<?> serviceClass)
+      RequestMethod.Builder methodBuilder, JMethod method, Class<?> serviceClass)
       throws UnableToCompleteException {
     JClassType requestReturnType = method.getReturnType().isInterface();
     JClassType invocationReturnType;
@@ -313,12 +315,11 @@
     }
 
     /*
-     * TODO: bad assumption
-     * Implicit assumption is that the Service and ProxyFor
+     * TODO: bad assumption Implicit assumption is that the Service and ProxyFor
      * classes are the same. This is because an instance method should
-     * technically be looked up on the class that is the instance parameter,
-     * and not on the serviceClass, which consists of static service methods.
-     * Can't be fixed until it is fixed in JsonRequestProcessor.
+     * technically be looked up on the class that is the instance parameter, and
+     * not on the serviceClass, which consists of static service methods. Can't
+     * be fixed until it is fixed in JsonRequestProcessor.
      */
     Method domainMethod = validateExistsAndNotOverriden(method, serviceClass,
         false);
@@ -330,8 +331,7 @@
       if (isStatic(domainMethod)) {
         poison("Method %s.%s is an instance method, "
             + "while the corresponding method on %s is static",
-            method.getEnclosingType().getName(),
-            method.getName(),
+            method.getEnclosingType().getName(), method.getName(),
             serviceClass.getName());
         return false;
       }
@@ -344,8 +344,7 @@
       if (!isStatic(domainMethod)) {
         poison("Method %s.%s is a static method, "
             + "while the corresponding method on %s is not",
-            method.getEnclosingType().getName(),
-            method.getName(),
+            method.getEnclosingType().getName(), method.getName(),
             serviceClass.getName());
         return false;
       }
@@ -367,8 +366,7 @@
     Class<?>[] domainParams = domainMethod.getParameterTypes();
     if (params.length != domainParams.length) {
       poison("Method %s.%s parameters do not match same method on %s",
-          method.getEnclosingType().getName(),
-          method.getName(),
+          method.getEnclosingType().getName(), method.getName(),
           serviceClass.getName());
     }
     for (int i = 0; i < params.length; ++i) {
@@ -377,16 +375,14 @@
       paramsOk = validateTransportableType(new RequestMethod.Builder(),
           param.getType(), false)
           && paramsOk;
-      paramsOk = validateProxyAndDomainTypeEquals(param.getType(), domainParam, i,
-          methodLocation(method), methodLocation(domainMethod))
-          && paramsOk;
+      paramsOk = validateProxyAndDomainTypeEquals(param.getType(), domainParam,
+          i, methodLocation(method), methodLocation(domainMethod)) && paramsOk;
     }
 
     return validateTransportableType(methodBuilder, invocationReturnType, true)
         && validateProxyAndDomainTypeEquals(invocationReturnType,
-        domainMethod.getReturnType(), -1, methodLocation(method),
-        methodLocation(domainMethod))
-        && paramsOk;
+            domainMethod.getReturnType(), -1, methodLocation(method),
+            methodLocation(domainMethod)) && paramsOk;
   }
 
   /**
@@ -397,8 +393,8 @@
     JMethod proxyMethod = requestMethod.getDeclarationMethod();
     // check if method exists on domain object
     Class<?> domainType = entityBuilder.peek().getProxyFor();
-    Method domainMethod = validateExistsAndNotOverriden(proxyMethod, domainType,
-        true);
+    Method domainMethod = validateExistsAndNotOverriden(proxyMethod,
+        domainType, true);
     if (domainMethod == null) {
       return false;
     }
@@ -408,27 +404,24 @@
       // compare return type of domain to proxy return type
       String returnTypeName = domainMethod.getReturnType().getName();
       // isEntityType() returns true for collections, but we want the Collection
-      String propertyTypeName =
-          requestMethod.isCollectionType() || requestMethod.isValueType() ?
-              requestMethod.getDataType().getQualifiedBinaryName() :
-              requestMethod.getEntityType().getProxyFor().getName();
+      String propertyTypeName = requestMethod.isCollectionType()
+          || requestMethod.isValueType()
+          ? requestMethod.getDataType().getQualifiedBinaryName()
+          : requestMethod.getEntityType().getProxyFor().getName();
       if (!returnTypeName.equals(propertyTypeName)) {
-                  poison("Method %s.%s return type %s does not match return type %s "
-              + " of method %s.%s", domainType.getName(),
-              domainMethod.getName(), returnTypeName,
-              propertyTypeName,
-              proxyMethod.getEnclosingType().getName(), proxyMethod.getName());
+        poison("Method %s.%s return type %s does not match return type %s "
+            + " of method %s.%s", domainType.getName(), domainMethod.getName(),
+            returnTypeName, propertyTypeName,
+            proxyMethod.getEnclosingType().getName(), proxyMethod.getName());
         return false;
       }
     }
     JParameter[] proxyParams = proxyMethod.getParameters();
     Class<?>[] domainParams = domainMethod.getParameterTypes();
     if (proxyParams.length != domainParams.length) {
-       poison("Method %s.%s parameter mismatch with %s.%s",
-           proxyMethod.getEnclosingType().getName(),
-           proxyMethod.getName(),
-           domainType.getName(),
-           domainMethod.getName());
+      poison("Method %s.%s parameter mismatch with %s.%s",
+          proxyMethod.getEnclosingType().getName(), proxyMethod.getName(),
+          domainType.getName(), domainMethod.getName());
       return false;
     }
     for (int i = 0; i < proxyParams.length; i++) {
@@ -436,11 +429,9 @@
       Class<?> domainParam = domainParams[i];
       if (!validateProxyAndDomainTypeEquals(proxyParam, domainParam, i,
           methodLocation(proxyMethod), methodLocation(domainMethod))) {
-        poison("Parameter %d of %s.%s doesn't match method %s.%s",
-            i, proxyMethod.getEnclosingType().getName(),
-            proxyMethod.getName(),
-            domainType.getName(),
-            domainMethod.getName());
+        poison("Parameter %d of %s.%s doesn't match method %s.%s", i,
+            proxyMethod.getEnclosingType().getName(), proxyMethod.getName(),
+            domainType.getName(), domainMethod.getName());
         return false;
       }
     }
@@ -452,16 +443,17 @@
    */
   private boolean validateDomainType(Class<?> domainType) {
     try {
-        domainType.getMethod("getId");
+      domainType.getMethod("getId");
     } catch (NoSuchMethodException e) {
-        poison("The class %s is missing method getId()", domainType.getName());
-        return false;
+      poison("The class %s is missing method getId()", domainType.getName());
+      return false;
     }
     try {
-        domainType.getMethod("getVersion");
+      domainType.getMethod("getVersion");
     } catch (NoSuchMethodException e) {
-        poison("The class %s is missing method getVersion()", domainType.getName());
-        return false;
+      poison("The class %s is missing method getVersion()",
+          domainType.getName());
+      return false;
     }
     return true;
   }
@@ -502,18 +494,18 @@
       // allow int to match int or Integer
       matchOk = proxyType.getQualifiedSourceName().equals(
           ModelUtils.maybeAutobox(domainType).getName())
-        || proxyType.getQualifiedSourceName().equals(domainType.getName());
+          || proxyType.getQualifiedSourceName().equals(domainType.getName());
     } else {
-      matchOk =  getEntityProxyType(
-          proxyType.isClassOrInterface()).getProxyFor().equals(domainType);
+      matchOk = getEntityProxyType(proxyType.isClassOrInterface()).getProxyFor().equals(
+          domainType);
     }
     if (!matchOk) {
       if (paramNumber < 0) {
-        poison("Return type of method %s does not match method %s", clientMethod,
-            serverMethod);
+        poison("Return type of method %s does not match method %s",
+            clientMethod, serverMethod);
       } else {
-         poison("Parameter %d of method %s does not match method %s",
-          paramNumber, clientMethod, serverMethod);
+        poison("Parameter %d of method %s does not match method %s",
+            paramNumber, clientMethod, serverMethod);
       }
     }
     return matchOk;
diff --git a/user/test/com/google/gwt/requestfactory/RequestFactorySuite.java b/user/test/com/google/gwt/requestfactory/RequestFactorySuite.java
index 44de728..d4ec7e4 100644
--- a/user/test/com/google/gwt/requestfactory/RequestFactorySuite.java
+++ b/user/test/com/google/gwt/requestfactory/RequestFactorySuite.java
@@ -18,6 +18,7 @@
 import com.google.gwt.junit.tools.GWTTestSuite;
 import com.google.gwt.requestfactory.client.FindServiceTest;
 import com.google.gwt.requestfactory.client.RequestFactoryExceptionHandlerTest;
+import com.google.gwt.requestfactory.client.RequestFactoryPolymorphicTest;
 import com.google.gwt.requestfactory.client.RequestFactoryStringTest;
 import com.google.gwt.requestfactory.client.RequestFactoryTest;
 import com.google.gwt.requestfactory.client.ui.EditorTest;
@@ -35,6 +36,7 @@
     suite.addTestSuite(RequestFactoryTest.class);
     suite.addTestSuite(RequestFactoryStringTest.class);
     suite.addTestSuite(RequestFactoryExceptionHandlerTest.class);
+    suite.addTestSuite(RequestFactoryPolymorphicTest.class);
     suite.addTestSuite(FindServiceTest.class);
     return suite;
   }