RequestFactory: Enabling support for interfaces in @ProxyFor.

Patch moved from rietveld (http://gwt-code-reviews.appspot.com/1764804/)

RequestFactory should allow that client-proxies point to interfaces in
server side instead of classes, facilitating different implementations.

This feature allows as well that we could reuse proxy interfaces in both
sides, what reduces the code.

@ProxyFor(value = A.class, locator = ALocator.class)
public interface A {
  String getSomething();
  void setSomething(String s);
}

public class ALocator extends Locator<A, String>  {
  @Override
  public A create(Class<? extends A> clazz) {
    return new AImpl();
  }
  ....
}

public class AImpl implements A {
  String getSomething(){return null}
  void setSomething(String s){}
}

Change-Id: I273ed2c403b275f4c9bec2b7e7ba72a3d05930b9
Bugs: issue 5762.
diff --git a/requestfactory/build.xml b/requestfactory/build.xml
index 586d155..53a6724 100755
--- a/requestfactory/build.xml
+++ b/requestfactory/build.xml
@@ -117,6 +117,7 @@
         <fileset dir="${gwt.build.lib}" includes="requestfactory-test.jar" />
       </classpath>
       <arg path="${gwt.build.lib}/requestfactory-test-validated.jar" />
+      <arg value="com.google.web.bindery.requestfactory.gwt.client.ProxyForInterfacesTest.Factory" />
       <arg value="com.google.web.bindery.requestfactory.gwt.client.RequestFactoryChainedContextTest.Factory" />
       <arg value="com.google.web.bindery.requestfactory.gwt.client.RequestFactoryPolymorphicTest.Factory" />
       <arg value="com.google.web.bindery.requestfactory.gwt.client.RequestFactoryGenericsTest.Factory" />
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 6bd744a..77daf2a 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/ResolverServiceLayer.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/ResolverServiceLayer.java
@@ -78,23 +78,12 @@
     if (TypeUtils.isValueType(domainClass)) {
       return domainClass.asSubclass(clientClass);
     }
-    Class<?> toSearch = domainClass;
-    while (toSearch != null) {
-      List<String> clientTypes = deobfuscator.getClientProxies(toSearch.getName());
-      if (clientTypes != null) {
-        for (String clientType : clientTypes) {
-          Class<?> proxy = forName(clientType);
-          if (clientClass.isAssignableFrom(proxy)) {
-            return proxy.asSubclass(clientClass);
-          }
-        }
-      }
-      toSearch = toSearch.getSuperclass();
-    }
-    if (required) {
+
+    Class<? extends T> ret = resolveClientType(domainClass, clientClass);
+    if (ret == null && required) {
       die(null, "The domain type %s cannot be sent to the client", domainClass.getCanonicalName());
     }
-    return null;
+    return ret;
   }
 
   @Override
@@ -263,4 +252,29 @@
     }
     return params.toArray(new Class<?>[params.size()]);
   }
+
+  private <T> Class<? extends T> resolveClientType(Class<?> domainClass, Class<T> clientClass) {
+    if (domainClass == null) {
+      return null;
+    }
+
+    List<String> clientTypes = deobfuscator.getClientProxies(domainClass.getName());
+    if (clientTypes != null) {
+      for (String clientType : clientTypes) {
+        Class<?> proxy = forName(clientType);
+        if (clientClass.isAssignableFrom(proxy)) {
+          return proxy.asSubclass(clientClass);
+        }
+      }
+    }
+
+    for (Class<?> toSearch : domainClass.getInterfaces()) {
+      Class<? extends T> ret = resolveClientType(toSearch, clientClass);
+      if (ret != null) {
+        return ret;
+      }
+    }
+
+    return resolveClientType(domainClass.getSuperclass(), clientClass);
+  }
 }
diff --git a/user/test/com/google/web/bindery/requestfactory/gwt/RequestFactorySuite.java b/user/test/com/google/web/bindery/requestfactory/gwt/RequestFactorySuite.java
index 09d259f..1a55e85 100644
--- a/user/test/com/google/web/bindery/requestfactory/gwt/RequestFactorySuite.java
+++ b/user/test/com/google/web/bindery/requestfactory/gwt/RequestFactorySuite.java
@@ -18,6 +18,7 @@
 import com.google.gwt.junit.tools.GWTTestSuite;
 import com.google.web.bindery.requestfactory.gwt.client.FindServiceTest;
 import com.google.web.bindery.requestfactory.gwt.client.JsonRpcRequestFactoryTest;
+import com.google.web.bindery.requestfactory.gwt.client.ProxyForInterfacesTest;
 import com.google.web.bindery.requestfactory.gwt.client.RequestBatcherTest;
 import com.google.web.bindery.requestfactory.gwt.client.RequestFactoryChainedContextTest;
 import com.google.web.bindery.requestfactory.gwt.client.RequestFactoryExceptionHandlerTest;
@@ -55,13 +56,14 @@
     suite.addTestSuite(LocatorTest.class);
     suite.addTestSuite(MethodProvidedByServiceLayerTest.class);
     suite.addTestSuite(MultipleFactoriesTest.class);
-    suite.addTestSuite(RequestFactoryTest.class);
+    suite.addTestSuite(ProxyForInterfacesTest.class);
     suite.addTestSuite(RequestFactoryChainedContextTest.class);
     suite.addTestSuite(RequestFactoryExceptionHandlerTest.class);
     suite.addTestSuite(RequestFactoryExceptionPropagationTest.class);
     suite.addTestSuite(RequestFactoryGenericsTest.class);
     suite.addTestSuite(RequestFactoryPolymorphicTest.class);
     suite.addTestSuite(RequestFactoryUnicodeEscapingTest.class);
+    suite.addTestSuite(RequestFactoryTest.class);
     suite.addTestSuite(RequestPayloadTest.class);
     suite.addTestSuite(ServiceInheritanceTest.class);
     return suite;
diff --git a/user/test/com/google/web/bindery/requestfactory/gwt/client/ProxyForInterfacesTest.java b/user/test/com/google/web/bindery/requestfactory/gwt/client/ProxyForInterfacesTest.java
new file mode 100644
index 0000000..2110133
--- /dev/null
+++ b/user/test/com/google/web/bindery/requestfactory/gwt/client/ProxyForInterfacesTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2012 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.requestfactory.gwt.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.web.bindery.event.shared.SimpleEventBus;
+import com.google.web.bindery.requestfactory.shared.ProxyFor;
+import com.google.web.bindery.requestfactory.shared.Receiver;
+import com.google.web.bindery.requestfactory.shared.Request;
+import com.google.web.bindery.requestfactory.shared.RequestContext;
+import com.google.web.bindery.requestfactory.shared.RequestFactory;
+import com.google.web.bindery.requestfactory.shared.Service;
+import com.google.web.bindery.requestfactory.shared.ValueProxy;
+
+/**
+ * Tests RequestFactory when using proxies for interfaces.
+ * @see http://code.google.com/p/google-web-toolkit/issues/detail?id=5762
+ */
+public class ProxyForInterfacesTest extends GWTTestCase {
+
+  /**
+   * The Factory.
+   */
+  protected interface Factory extends RequestFactory {
+    Context ctx();
+  }
+
+  static class C0 implements I1 {
+    public String getName() {
+      return "C0";
+    }
+  }
+
+  static class C1 implements I2 {
+    public String getName() {
+      return "C1";
+    }
+  }
+
+  static class C2 extends C1 {
+    public String getName() {
+      return "C2";
+    }
+  }
+
+  @Service(ContextImpl.class)
+  interface Context extends RequestContext {
+    Request<I1> getC0();
+
+    Request<I1> getC1_RetI1();
+
+    Request<I2> getC1_RetI2();
+
+    Request<I1> getC2_RetI1();
+
+    Request<I2> getC2_RetI2();
+  }
+
+  static class ContextImpl {
+    public static I1 getC0() {
+      return new C0();
+    }
+
+    public static I1 getC1_RetI1() {
+      return new C1();
+    }
+
+    public static I2 getC1_RetI2() {
+      return new C1();
+    }
+
+    public static I1 getC2_RetI1() {
+      return new C2();
+    }
+
+    public static I2 getC2_RetI2() {
+      return new C2();
+    }
+  }
+
+  @ProxyFor(I1.class)
+  interface I1 extends ValueProxy {
+    String getName();
+  }
+
+  @ProxyFor(I2.class)
+  interface I2 extends I1, ValueProxy {
+  }
+
+  class ReceiverAssert<T extends I1> extends Receiver<T> {
+    String name;
+
+    public ReceiverAssert(String name) {
+      this.name = name;
+    }
+
+    @Override
+    public void onSuccess(T response) {
+      assertEquals(name, response.getName());
+    }
+  }
+
+  private static final int TEST_DELAY = 5000;
+
+  protected Factory factory;
+
+  @Override
+  public String getModuleName() {
+    return "com.google.web.bindery.requestfactory.gwt.RequestFactorySuite";
+  }
+
+  public void testProxyForInterfaces() {
+    delayTestFinish(TEST_DELAY);
+    Context ctx = factory.ctx();
+    ctx.getC0().to(new ReceiverAssert<I1>("C0"));
+    ctx.getC1_RetI1().to(new ReceiverAssert<I1>("C1"));
+    ctx.getC2_RetI1().to(new ReceiverAssert<I1>("C2"));
+    ctx.getC1_RetI2().to(new ReceiverAssert<I2>("C1"));
+    ctx.getC2_RetI2().to(new ReceiverAssert<I2>("C2"));
+    ctx.fire(new Receiver<Void>() {
+      public void onSuccess(Void response) {
+        finishTest();
+      }
+    });
+  }
+
+  protected Factory createFactory() {
+    Factory f = GWT.create(Factory.class);
+    f.initialize(new SimpleEventBus());
+    return f;
+  }
+
+  @Override
+  protected void gwtSetUp() throws Exception {
+    factory = createFactory();
+  }
+}
diff --git a/user/test/com/google/web/bindery/requestfactory/server/ProxyForInterfacesJreTest.java b/user/test/com/google/web/bindery/requestfactory/server/ProxyForInterfacesJreTest.java
new file mode 100644
index 0000000..724a822
--- /dev/null
+++ b/user/test/com/google/web/bindery/requestfactory/server/ProxyForInterfacesJreTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2012 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.requestfactory.server;
+
+import com.google.web.bindery.requestfactory.gwt.client.ProxyForInterfacesTest;
+
+/**
+ * JRE version of {@link ProxyForInterfacesTest}.
+ */
+public class ProxyForInterfacesJreTest extends ProxyForInterfacesTest {
+
+  @Override
+  public String getModuleName() {
+    return null;
+  }
+
+  @Override
+  protected Factory createFactory() {
+    return RequestFactoryJreTest.createInProcess(Factory.class);
+  }
+
+}
diff --git a/user/test/com/google/web/bindery/requestfactory/vm/RequestFactoryJreSuite.java b/user/test/com/google/web/bindery/requestfactory/vm/RequestFactoryJreSuite.java
index 5fa499d..c2e20a1 100644
--- a/user/test/com/google/web/bindery/requestfactory/vm/RequestFactoryJreSuite.java
+++ b/user/test/com/google/web/bindery/requestfactory/vm/RequestFactoryJreSuite.java
@@ -23,6 +23,7 @@
 import com.google.web.bindery.requestfactory.server.LocatorJreTest;
 import com.google.web.bindery.requestfactory.server.MethodProvidedByServiceLayerJreTest;
 import com.google.web.bindery.requestfactory.server.MultipleFactoriesJreTest;
+import com.google.web.bindery.requestfactory.server.ProxyForInterfacesJreTest;
 import com.google.web.bindery.requestfactory.server.RequestFactoryChainedContextJreTest;
 import com.google.web.bindery.requestfactory.server.RequestFactoryExceptionPropagationJreTest;
 import com.google.web.bindery.requestfactory.server.RequestFactoryGenericsJreTest;
@@ -54,6 +55,7 @@
     suite.addTestSuite(LocatorJreTest.class);
     suite.addTestSuite(MethodProvidedByServiceLayerJreTest.class);
     suite.addTestSuite(MultipleFactoriesJreTest.class);
+    suite.addTestSuite(ProxyForInterfacesJreTest.class);
     suite.addTestSuite(RequestFactoryChainedContextJreTest.class);
     suite.addTestSuite(RequestFactoryExceptionPropagationJreTest.class);
     suite.addTestSuite(RequestFactoryGenericsJreTest.class);