/* * 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.requestfactory.gwt.client; import com.google.gwt.core.client.GWT; import com.google.gwt.junit.client.GWTTestCase; import com.google.web.bindery.event.shared.EventBus; import com.google.web.bindery.event.shared.SimpleEventBus; import com.google.web.bindery.requestfactory.shared.EntityProxy; import com.google.web.bindery.requestfactory.shared.ExtraTypes; 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.SimpleFooProxy; import com.google.web.bindery.requestfactory.shared.TestRequestFactory; import java.util.Arrays; import java.util.List; /** * Tests behavior of RequestFactory when using polymorphic Request return types * and other non-trivial type hierarchies. */ public class RequestFactoryPolymorphicTest extends GWTTestCase { /** * Mandatory javadoc. */ public static class A { private static int idCount = 0; public static A findA(int id) { A toReturn = new A(); toReturn.id = id; return toReturn; } private String a = "a"; protected int id = idCount++; private A nextA; public String getA() { return a; } public int getId() { return id; } public A getNextA() { if (nextA == null) { nextA = new A(); } return nextA; } public int getVersion() { return 0; } public void setA(String value) { a = value; } } /** * Mandatory javadoc. */ @ProxyFor(A.class) public interface AProxy extends EntityProxy, HasA { AProxy getNextA(); // Mix in getA() from HasA void setA(String value); } /** * Mandatory javadoc. */ public static class ASub extends A { } /** * Mandatory javadoc. */ public static class B extends A { public static B findB(int id) { B toReturn = new B(); toReturn.id = id; return toReturn; } private String b = "b"; public String getB() { return b; } public void setB(String value) { b = value; } } /** * Mandatory javadoc. */ @ProxyFor(B.class) public interface B1Proxy extends AProxy { String getB(); void setB(String value); } /** * Mandatory javadoc. */ @ProxyFor(B.class) public interface B2Proxy extends AProxy { String getB(); void setB(String value); } /** * Mandatory javadoc. */ public static class BSub extends B { } /** * Mandatory javadoc. */ public static class C extends B { public static C findC(int id) { C toReturn = new C(); toReturn.id = id; return toReturn; } private String c = "c"; private C nextC; public String getC() { return c; } public C getNextC() { if (nextC == null) { nextC = new C(); } return nextC; } public void setC(String value) { c = value; } } /** * Mandatory javadoc. */ @ProxyFor(C.class) public interface C1Proxy extends B1Proxy { String getC(); C1Proxy getNextC(); void setC(String value); } /** * Mandatory javadoc. */ @ProxyFor(C.class) public interface C2Proxy extends B2Proxy { String getC(); void setC(String value); } /** * Mandatory javadoc. */ public static class CSub extends C { } /** * Mandatory javadoc. */ public static class D extends A { public static D findD(int id) { D toReturn = new D(); toReturn.id = id; return toReturn; } private String d = "d"; public String getD() { return d; } public void setD(String value) { d = value; } } /** * Mandatory javadoc. */ @ExtraTypes(D2Proxy.class) @ProxyFor(D.class) public interface D1Proxy extends AProxy { String getD(); void setD(String value); } /** * Mandatory javadoc. */ @ProxyFor(D.class) public interface D2Proxy extends EntityProxy { String getD(); void setD(String value); } /** * This class should not be referenced except as a superclass of * {@link MoreDerivedProxy}. */ @ProxyFor(D.class) public interface D3Proxy extends EntityProxy { String getD(); void setD(String value); } /** * Mandatory javadoc. */ public static class DSub extends D { } /** * Mandatory javadoc. */ public interface HasA { String getA(); } /** * Mandatory javadoc. */ public static class Impl { public static A AasA() { return new A(); } public static A BasA() { return new B(); } public static B BasB() { return new B(); } public static A CasA() { return new C(); } public static B CasB() { return new C(); } public static String checkA(Object obj) { return A.class.equals(obj.getClass()) && "A".equals(((A) obj).getA()) ? "" : "checkA"; } public static String checkB(Object obj) { return B.class.equals(obj.getClass()) && "B".equals(((B) obj).getB()) ? "" : "checkB"; } public static String checkC(Object obj) { return C.class.equals(obj.getClass()) && "C".equals(((C) obj).getC()) ? "" : "checkC"; } public static String checkD(Object obj) { return D.class.equals(obj.getClass()) && "D".equals(((D) obj).getD()) ? "" : "checkD"; } public static String checkList(List<Object> list) { if (list.size() != 4) { return "size"; } String temp; temp = checkA(list.get(0)); if (!temp.isEmpty()) { return temp; } temp = checkB(list.get(1)); if (!temp.isEmpty()) { return temp; } temp = checkC(list.get(2)); if (!temp.isEmpty()) { return temp; } temp = checkD(list.get(3)); if (!temp.isEmpty()) { return temp; } return ""; } public static String checkW(Object obj) { return W.class.equals(obj.getClass()) && "W".equals(((W) obj).getW()) ? "" : "checkW"; } public static String checkZ(Object obj) { return Z.class.equals(obj.getClass()) && "Z".equals(((Z) obj).getZ()) ? "" : "checkZ"; } public static A DasA() { return new D(); } public static D DasD() { return new D(); } public static List<A> testCollection() { return Arrays.asList(new A(), new B(), new C(), new D()); } public static List<A> testCollectionSub() { return Arrays.asList(new ASub(), new BSub(), new CSub(), new DSub()); } public static W W() { return new W(); } public static W W2() { return new W(); } public static Z Z() { return new Z(); } public static Z Z2() { return new Z(); } } /** * Check diamond inheritance. */ @ProxyFor(D.class) public interface MoreDerivedProxy extends AProxy, D1Proxy, D2Proxy, D3Proxy { } /** * The W and Z types are used with the WZProxy and ZWProxy to demonstrate * proxy interface inheritance even when their proxy-for types aren't related. */ public static class W { private static int idCount; public static W findW(int id) { W toReturn = new W(); toReturn.id = id; return toReturn; } private int id = idCount++; private String w = "w"; private String z = "z"; public int getId() { return id; } public int getVersion() { return 0; } public String getW() { return w; } public String getZ() { return z; } public void setW(String w) { this.w = w; } public void setZ(String z) { this.z = z; } } /** * Mandatory javadoc. */ @ProxyFor(W.class) public interface WProxy extends EntityProxy { String getW(); void setW(String w); } /** * Mandatory javadoc. */ @ProxyFor(W.class) public interface WZProxy extends ZProxy { String getW(); void setW(String w); } /** * @see W */ public static class Z extends A { private static int idCount; public static Z findZ(int id) { Z toReturn = new Z(); toReturn.id = id; return toReturn; } private int id = idCount++; private String w = "w"; private String z = "z"; public int getId() { return id; } public int getVersion() { return 0; } public String getW() { return w; } public String getZ() { return z; } public void setW(String w) { this.w = w; } public void setZ(String z) { this.z = z; } } /** * Mandatory javadoc. */ @ProxyFor(Z.class) public interface ZProxy extends EntityProxy { String getZ(); void setZ(String z); } /** * Mandatory javadoc. */ @ProxyFor(Z.class) public interface ZWProxy extends WProxy { String getZ(); void setZ(String z); } /** * Mandatory javadoc. */ @ExtraTypes(B2Proxy.class) protected interface Factory extends RequestFactory { Context ctx(); } /** * Verifies that the received proxy's proxy type is exactly {@code clazz} and * checks the values of the properties. */ static class CastAndCheckReceiver extends Receiver<EntityProxy> { public static CastAndCheckReceiver of(Class<?> clazz) { return new CastAndCheckReceiver(clazz); } private final Class<?> clazz; public CastAndCheckReceiver(Class<?> clazz) { this.clazz = clazz; } @Override public void onSuccess(EntityProxy response) { assertNotNull(response); assertEquals(clazz, response.stableId().getProxyClass()); if (response instanceof HasA) { assertEquals("a", ((HasA) response).getA()); } if (response instanceof B1Proxy) { assertEquals("b", ((B1Proxy) response).getB()); } if (response instanceof B2Proxy) { assertEquals("b", ((B2Proxy) response).getB()); } if (response instanceof C1Proxy) { assertEquals("c", ((C1Proxy) response).getC()); } if (response instanceof C2Proxy) { assertEquals("c", ((C2Proxy) response).getC()); } if (response instanceof D1Proxy) { assertEquals("d", ((D1Proxy) response).getD()); } if (response instanceof D2Proxy) { assertEquals("d", ((D2Proxy) response).getD()); } } } @ExtraTypes({C1Proxy.class, C2Proxy.class, MoreDerivedProxy.class}) @Service(Impl.class) interface Context extends RequestContext { Request<AProxy> AasA(); Request<AProxy> BasA(); Request<B1Proxy> BasB(); Request<AProxy> CasA(); Request<B1Proxy> CasB(); Request<String> checkA(EntityProxy proxy); Request<String> checkB(EntityProxy proxy); Request<String> checkC(EntityProxy proxy); Request<String> checkD(EntityProxy proxy); Request<String> checkList(List<EntityProxy> list); Request<String> checkW(EntityProxy proxy); Request<String> checkZ(EntityProxy proxy); Request<AProxy> DasA(); Request<D1Proxy> DasD(); Request<List<AProxy>> testCollection(); Request<List<AProxy>> testCollectionSub(); Request<WProxy> W(); Request<WZProxy> W2(); Request<ZProxy> Z(); Request<ZWProxy> Z2(); } /** * Checks that the incoming list is {@code [ A, B, C, D ]}. */ static class ListChecker extends Receiver<List<AProxy>> { @Override public void onSuccess(List<AProxy> response) { new CastAndCheckReceiver(AProxy.class).onSuccess(response.get(0)); new CastAndCheckReceiver(B1Proxy.class).onSuccess(response.get(1)); new CastAndCheckReceiver(C1Proxy.class).onSuccess(response.get(2)); new CastAndCheckReceiver(MoreDerivedProxy.class).onSuccess(response.get(3)); } } private final Receiver<String> checkReceiver = new Receiver<String>() { @Override public void onSuccess(String response) { assertEquals("", response); } }; private static final int TEST_DELAY = 5000; protected Factory factory; @Override public String getModuleName() { return "com.google.web.bindery.requestfactory.gwt.RequestFactorySuite"; } public void testChain() { delayTestFinish(TEST_DELAY); Context ctx = factory.ctx(); ctx.CasA().with("nextA.nextA").fire(new Receiver<AProxy>() { @Override public void onSuccess(AProxy response) { new CastAndCheckReceiver(AProxy.class).onSuccess(response.getNextA().getNextA()); assertNull(response.getNextA().getNextA().getNextA()); assertNull(((C1Proxy) response).getNextC()); finishTest(); } }); } public void testChainWithExtras() { delayTestFinish(TEST_DELAY); Context ctx = factory.ctx(); ctx.CasA().with("nextC.nextC").fire(new Receiver<AProxy>() { @Override public void onSuccess(AProxy response) { assertNull(response.getNextA()); C1Proxy cast = (C1Proxy) response; new CastAndCheckReceiver(C1Proxy.class).onSuccess(cast.getNextC().getNextC()); assertNull(cast.getNextC().getNextC().getNextC()); finishTest(); } }); } public void testChainWithWildcards() { delayTestFinish(TEST_DELAY); Context ctx = factory.ctx(); ctx.CasA().with("*.*").fire(new Receiver<AProxy>() { @Override public void onSuccess(AProxy response) { new CastAndCheckReceiver(AProxy.class).onSuccess(response.getNextA().getNextA()); C1Proxy cast = (C1Proxy) response; new CastAndCheckReceiver(C1Proxy.class).onSuccess(cast.getNextC().getNextC()); assertNull(cast.getNextC().getNextC().getNextC()); finishTest(); } }); } public void testCreation() { delayTestFinish(TEST_DELAY); Context ctx = factory.ctx(); checkA(ctx, AProxy.class); checkB(ctx, B1Proxy.class); checkB(ctx, B2Proxy.class); checkC(ctx, C1Proxy.class); checkC(ctx, C2Proxy.class); checkD(ctx, D1Proxy.class); checkD(ctx, D2Proxy.class); // D3Proxy is a proxy supertype, assignable to BaseProxy and has @ProxyFor checkD(ctx, D3Proxy.class); checkD(ctx, MoreDerivedProxy.class); checkW(ctx, WProxy.class); checkW(ctx, WZProxy.class); checkZ(ctx, ZProxy.class); checkZ(ctx, ZWProxy.class); ctx.fire(new Receiver<Void>() { @Override public void onSuccess(Void response) { finishTest(); } }); } /** * Ensure heterogeneous collections work. */ public void testCreationList() { Context ctx = factory.ctx(); ctx.checkList( Arrays.asList(create(ctx, AProxy.class), create(ctx, B2Proxy.class), create(ctx, C2Proxy.class), create(ctx, D2Proxy.class))).to(checkReceiver); ctx.fire(new Receiver<Void>() { @Override public void onSuccess(Void response) { finishTest(); } }); } public void testGenericRequest() { TestRequestFactory rf = GWT.create(TestRequestFactory.class); EventBus eventBus = new SimpleEventBus(); rf.initialize(eventBus); SimpleFooProxy simpleFoo = rf.testGenericRequest().create(SimpleFooProxy.class); assertNull(simpleFoo.getUserName()); } public void testRetrieval() { delayTestFinish(TEST_DELAY); Context ctx = factory.ctx(); ctx.AasA().to(CastAndCheckReceiver.of(AProxy.class)); ctx.BasA().to(CastAndCheckReceiver.of(B1Proxy.class)); ctx.BasB().to(CastAndCheckReceiver.of(B1Proxy.class)); ctx.CasA().to(CastAndCheckReceiver.of(C1Proxy.class)); ctx.CasB().to(CastAndCheckReceiver.of(C1Proxy.class)); ctx.DasA().to(CastAndCheckReceiver.of(MoreDerivedProxy.class)); ctx.DasD().to(CastAndCheckReceiver.of(MoreDerivedProxy.class)); ctx.W().to(CastAndCheckReceiver.of(WProxy.class)); ctx.W2().to(CastAndCheckReceiver.of(WZProxy.class)); ctx.Z().to(CastAndCheckReceiver.of(ZProxy.class)); ctx.Z2().to(CastAndCheckReceiver.of(ZWProxy.class)); ctx.fire(new Receiver<Void>() { @Override public void onSuccess(Void response) { finishTest(); } }); } public void testRetrievalCollection() { delayTestFinish(TEST_DELAY); Context ctx = factory.ctx(); ctx.testCollection().to(new ListChecker()); ctx.testCollectionSub().to(new ListChecker()); ctx.fire(new Receiver<Void>() { @Override 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(); } private void checkA(Context ctx, Class<? extends AProxy> clazz) { ctx.checkA(create(ctx, clazz)).to(checkReceiver); } private void checkB(Context ctx, Class<? extends EntityProxy> clazz) { ctx.checkB(create(ctx, clazz)).to(checkReceiver); } private void checkC(Context ctx, Class<? extends EntityProxy> clazz) { ctx.checkC(create(ctx, clazz)).to(checkReceiver); } private void checkD(Context ctx, Class<? extends EntityProxy> clazz) { ctx.checkD(create(ctx, clazz)).to(checkReceiver); } private void checkW(Context ctx, Class<? extends EntityProxy> clazz) { ctx.checkW(create(ctx, clazz)).to(checkReceiver); } private void checkZ(Context ctx, Class<? extends EntityProxy> clazz) { ctx.checkZ(create(ctx, clazz)).to(checkReceiver); } private <T extends EntityProxy> T create(Context ctx, Class<T> clazz) { T obj = ctx.create(clazz); if (obj instanceof AProxy) { ((AProxy) obj).setA("A"); } if (obj instanceof B1Proxy) { ((B1Proxy) obj).setB("B"); } if (obj instanceof B2Proxy) { ((B2Proxy) obj).setB("B"); } if (obj instanceof C1Proxy) { ((C1Proxy) obj).setC("C"); } if (obj instanceof C2Proxy) { ((C2Proxy) obj).setC("C"); } if (obj instanceof D1Proxy) { ((D1Proxy) obj).setD("D"); } if (obj instanceof D2Proxy) { ((D2Proxy) obj).setD("D"); } if (obj instanceof D3Proxy) { ((D3Proxy) obj).setD("D"); } if (obj instanceof WProxy) { ((WProxy) obj).setW("W"); } if (obj instanceof ZProxy) { ((ZProxy) obj).setZ("Z"); } if (obj instanceof WZProxy) { ((WZProxy) obj).setW("W"); } if (obj instanceof ZWProxy) { ((ZWProxy) obj).setZ("Z"); } return obj; } }