Revision: 8937
Author: b...@google.com
Date: Tue Oct  5 09:11:00 2010
Log: Fix ROO-1488 where forward references were not correctly resolved.
Patch by: cromwellian, bobv
Review by: bobv, cromwellian

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

http://code.google.com/p/google-web-toolkit/source/detail?r=8937

Modified:
/trunk/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequestContext.java
 /trunk/user/src/com/google/gwt/requestfactory/client/impl/EntityCodex.java
/trunk/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
 /trunk/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java
 /trunk/user/test/com/google/gwt/requestfactory/shared/SimpleFooRequest.java

=======================================
--- /trunk/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequestContext.java Sun Oct 3 14:17:43 2010 +++ /trunk/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequestContext.java Tue Oct 5 09:11:00 2010
@@ -26,15 +26,16 @@
 import com.google.gwt.requestfactory.shared.EntityProxyChange;
 import com.google.gwt.requestfactory.shared.Receiver;
 import com.google.gwt.requestfactory.shared.RequestContext;
+import com.google.gwt.requestfactory.shared.RequestTransport.TransportReceiver;
 import com.google.gwt.requestfactory.shared.ServerFailure;
 import com.google.gwt.requestfactory.shared.ValueCodex;
 import com.google.gwt.requestfactory.shared.Violation;
 import com.google.gwt.requestfactory.shared.WriteOperation;
-import com.google.gwt.requestfactory.shared.RequestTransport.TransportReceiver;
 import com.google.gwt.requestfactory.shared.impl.Constants;

 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
@@ -56,8 +57,13 @@
* Objects are placed into this map by being passed into {...@link #edit} or as
    * an invocation argument.
    */
- private final Map<SimpleEntityProxyId<?>, EntityProxy> seenProxies = new LinkedHashMap<SimpleEntityProxyId<?>, EntityProxy>(); + private final Map<SimpleEntityProxyId<?>, AutoBean<?>> editedProxies = new LinkedHashMap<SimpleEntityProxyId<?>, AutoBean<?>>();
   private Set<Violation> errors = new LinkedHashSet<Violation>();
+  /**
+ * A map that contains the canonical instance of an entity to return in the
+   * return graph, since this is built from scratch.
+   */
+ private final Map<SimpleEntityProxyId<?>, AutoBean<?>> returnedProxies = new HashMap<SimpleEntityProxyId<?>, AutoBean<?>>();

   protected AbstractRequestContext(AbstractRequestFactory factory) {
     this.requestFactory = factory;
@@ -71,7 +77,7 @@

     AutoBean<T> created = requestFactory.createEntityProxy(clazz,
         requestFactory.allocateId(clazz));
-    return makeEdited(created);
+    return takeOwnership(created);
   }

   public <T extends EntityProxy> T edit(T object) {
@@ -80,25 +86,21 @@
     checkLocked();

     @SuppressWarnings("unchecked")
-    T toReturn = (T) seenProxies.get(object.stableId());
-    if (toReturn != null) {
+ AutoBean<T> previouslySeen = (AutoBean<T>) editedProxies.get(object.stableId());
+    if (previouslySeen != null && !previouslySeen.isFrozen()) {
       /*
* If we've seen the object before, it might be because it was passed in * as a method argument. This does not guarantee its mutability, so check
        * that here before returning the cached object.
        */
-      AutoBean<T> previouslySeen = AutoBeanUtils.getAutoBean(toReturn);
-      if (!previouslySeen.isFrozen()) {
-        return toReturn;
-      }
+      return previouslySeen.as();
     }

     // Create editable copies
     AutoBean<T> parent = bean;
     bean = cloneBeanAndCollections(bean);
-    toReturn = makeEdited(bean);
     bean.setTag(PARENT_OBJECT, parent);
-    return toReturn;
+    return takeOwnership(bean);
   }

   /**
@@ -146,13 +148,12 @@
* simple flag-check because of the possibility of "unmaking" a change, per
      * the JavaDoc.
      */
-    for (EntityProxy edited : seenProxies.values()) {
-      AutoBean<EntityProxy> bean = AutoBeanUtils.getAutoBean(edited);
+    for (AutoBean<?> bean : editedProxies.values()) {
       AutoBean<?> previous = bean.getTag(PARENT_OBJECT);
       if (previous == null) {
         // Compare to empty object
-        previous = getRequestFactory().getAutoBeanFactory().create(
-            edited.stableId().getProxyClass());
+ Class<?> proxyClass = ((EntityProxy) bean.as()).stableId().getProxyClass(); + previous = getRequestFactory().getAutoBeanFactory().create(proxyClass);
       }
       if (!AutoBeanUtils.diff(previous, bean).isEmpty()) {
         return true;
@@ -182,7 +183,7 @@
    */
   protected void addInvocation(AbstractRequest<?> request) {
     if (invocations.size() > 0) {
-      // TODO(bobv): Upgrade wire protocal and server to handle chains
+      // TODO(bobv): Upgrade wire protocol and server to handle chains
       throw new IllegalStateException("Method chaining not implemented");
     }
     invocations.add(request);
@@ -192,38 +193,35 @@
   }

   /**
-   * Called by generated subclasses when decoding a request.
+ * Creates or retrieves a new canonical AutoBean to represent the given id in
+   * the returned payload.
    */
-  EntityProxy getSeenEntityProxy(SimpleEntityProxyId<?> id) {
-    return seenProxies.get(id);
+  <Q extends EntityProxy> AutoBean<Q> getProxyForReturnPayloadGraph(
+      SimpleEntityProxyId<Q> id) {
+    assert !id.isEphemeral();
+
+    @SuppressWarnings("unchecked")
+    AutoBean<Q> bean = (AutoBean<Q>) returnedProxies.get(id);
+    if (bean == null) {
+      Class<Q> proxyClass = id.getProxyClass();
+      bean = requestFactory.createEntityProxy(proxyClass, id);
+      returnedProxies.put(id, bean);
+    }
+
+    return bean;
   }

   /**
-   * Apply the deltas in a ReturnRecord to an EntityProxy.
+   * Create a new EntityProxy from a snapshot in the return payload.
    *
-   * @param id the EntityProxyId of the object being mutated
+   * @param id the EntityProxyId of the object
    * @param returnRecord the JSON map containing property/value pairs
* @param operations the WriteOperation eventns to broadcast over the EventBus
    */
   <Q extends EntityProxy> Q processReturnRecord(SimpleEntityProxyId<Q> id,
       final ReturnRecord returnRecord, WriteOperation... operations) {
-    @SuppressWarnings("unchecked")
-    Q proxy = (Q) seenProxies.get(id);
-    AutoBean<Q> toMutate;
-
-    if (proxy == null) {
-      // The server is sending us an object that hasn't been seen before
-      assert !id.isEphemeral();
-      Class<Q> proxyClass = id.getProxyClass();
-      toMutate = requestFactory.createEntityProxy(proxyClass, id);
-      makeEdited(toMutate);
-    } else {
-      // Create a new copy of the object
-      AutoBean<Q> original = AutoBeanUtils.getAutoBean(proxy);
-      toMutate = cloneBeanAndCollections(original);
-    }
-
-    proxy = toMutate.as();
+
+    AutoBean<Q> toMutate = getProxyForReturnPayloadGraph(id);

     // Apply updates
     toMutate.accept(new AutoBeanVisitor() {
@@ -266,6 +264,7 @@

     // Finished applying updates, freeze the bean
     makeImmutable(toMutate);
+    Q proxy = toMutate.as();

     /*
* Notify subscribers if the object differs from when it first came into the
@@ -394,8 +393,9 @@
             if (errors.isEmpty()) {
               receiver.onSuccess(null);
               // After success, shut down the context
-              seenProxies.clear();
+              editedProxies.clear();
               invocations.clear();
+              returnedProxies.clear();
             } else {
               receiver.onViolation(errors);
             }
@@ -411,22 +411,10 @@
    * Set the frozen status of all EntityProxies owned by this context.
    */
   private void freezeEntities(boolean frozen) {
-    for (EntityProxy proxy : seenProxies.values()) {
-      AutoBean<?> bean = AutoBeanUtils.getAutoBean(proxy);
+    for (AutoBean<?> bean : editedProxies.values()) {
       bean.setFrozen(frozen);
     }
   }
-
-  /**
-   * Make the EnityProxy bean edited and owned by this RequestContext.
-   */
-  private <T extends EntityProxy> T makeEdited(AutoBean<T> bean) {
-    T toReturn = bean.as();
-    seenProxies.put(EntityProxyCategory.stableId(bean), toReturn);
-    bean.setTag(EntityProxyCategory.REQUEST_CONTEXT,
-        AbstractRequestContext.this);
-    return toReturn;
-  }

   /**
    * Make an EntityProxy immutable.
@@ -456,17 +444,16 @@
     RequestContentData data = new RequestContentData();

     // Compute deltas for each entity seen by the context
-    for (EntityProxy proxy : seenProxies.values()) {
+    for (AutoBean<?> currentView : editedProxies.values()) {
       boolean isPersist = false;
       @SuppressWarnings("unchecked")
- SimpleEntityProxyId<EntityProxy> stableId = (SimpleEntityProxyId<EntityProxy>) proxy.stableId(); + SimpleEntityProxyId<EntityProxy> stableId = EntityProxyCategory.stableId((AutoBean<EntityProxy>) currentView);

       // Encoded string representations of the properties
       Map<String, String> encoded = new LinkedHashMap<String, String>();

       {
         // Find the object to compare against
-        AutoBean<?> currentView = AutoBeanUtils.getAutoBean(proxy);
         AutoBean<?> parent = currentView.getTag(PARENT_OBJECT);
         if (parent == null) {
           // Newly-created object, use a blank object to compare against
@@ -546,4 +533,14 @@
       edit((EntityProxy) arg);
     }
   }
-}
+
+  /**
+   * Make the EnityProxy bean edited and owned by this RequestContext.
+   */
+  private <T extends EntityProxy> T takeOwnership(AutoBean<T> bean) {
+    editedProxies.put(EntityProxyCategory.stableId(bean), bean);
+    bean.setTag(EntityProxyCategory.REQUEST_CONTEXT,
+        AbstractRequestContext.this);
+    return bean.as();
+  }
+}
=======================================
--- /trunk/user/src/com/google/gwt/requestfactory/client/impl/EntityCodex.java Fri Oct 1 18:15:55 2010 +++ /trunk/user/src/com/google/gwt/requestfactory/client/impl/EntityCodex.java Tue Oct 5 09:11:00 2010
@@ -79,7 +79,8 @@
     if (requestContext.getRequestFactory().getTypeToken(type) != null) {
       EntityProxyId<?> id = requestContext.getRequestFactory().getProxyId(
           (String) jso);
- return requestContext.getSeenEntityProxy((SimpleEntityProxyId<?>) id);
+      return requestContext.getProxyForReturnPayloadGraph(
+          (SimpleEntityProxyId<?>) id).as();
     }

     // Fall back to values
=======================================
--- /trunk/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java Mon Oct 4 15:35:00 2010 +++ /trunk/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java Tue Oct 5 09:11:00 2010
@@ -42,7 +42,6 @@
  * Tests for {...@link com.google.gwt.requestfactory.shared.RequestFactory}.
  */
 public class RequestFactoryTest extends RequestFactoryTestBase {
-  private static final int DELAY_TEST_FINISH = 5000;

   /*
    * DO NOT USE finishTest(). Instead, call finishTestAndReset();
@@ -167,6 +166,8 @@
       doTest();
     }
   }
+
+  private static final int DELAY_TEST_FINISH = 5000;

public <T extends EntityProxy> void assertContains(Collection<T> col, T value) {
     for (T x : col) {
@@ -174,8 +175,9 @@
         return;
       }
     }
-    assertTrue(("Value " + value + " not found in collection ")
-        + col.toString(), false);
+    assertTrue(
+        ("Value " + value + " not found in collection ") + col.toString(),
+        false);
   }

   public <T extends EntityProxy> void assertNotContains(Collection<T> col,
@@ -184,11 +186,131 @@
       assertNotSame(x.stableId(), value.stableId());
     }
   }
+
+  public void disabled_testEchoComplexFutures() {
+ // relate futures on the server. Check if the relationship is still present
+    // on the client.
+    delayTestFinish(DELAY_TEST_FINISH);
+ final SimpleFooEventHandler<SimpleFooProxy> handler = new SimpleFooEventHandler<SimpleFooProxy>();
+    EntityProxyChange.registerForProxyType(req.getEventBus(),
+        SimpleFooProxy.class, handler);
+    SimpleFooRequest context = req.simpleFooRequest();
+    final SimpleFooProxy simpleFoo = context.create(SimpleFooProxy.class);
+    final SimpleBarProxy simpleBar = context.create(SimpleBarProxy.class);
+    context.echoComplex(simpleFoo, simpleBar).fire(
+        new Receiver<SimpleFooProxy>() {
+          @Override
+          public void onSuccess(SimpleFooProxy response) {
+            assertEquals(0, handler.totalEventCount);
+            checkStableIdEquals(simpleFoo, response);
+            SimpleBarProxy responseBar = response.getBarField();
+            assertNotNull(responseBar);
+            checkStableIdEquals(simpleBar, responseBar);
+            finishTestAndReset();
+          }
+        });
+  }
+
+  public void disabled_testEchoSimpleFutures() {
+    // tests if futureIds can be echoed back.
+    delayTestFinish(DELAY_TEST_FINISH);
+ final SimpleFooEventHandler<SimpleFooProxy> handler = new SimpleFooEventHandler<SimpleFooProxy>();
+    EntityProxyChange.registerForProxyType(req.getEventBus(),
+        SimpleFooProxy.class, handler);
+    SimpleFooRequest context = req.simpleFooRequest();
+    final SimpleFooProxy simpleFoo = context.create(SimpleFooProxy.class);
+    context.echo(simpleFoo).fire(new Receiver<SimpleFooProxy>() {
+      @Override
+      public void onSuccess(SimpleFooProxy response) {
+        assertEquals(0, handler.totalEventCount);
+        checkStableIdEquals(simpleFoo, response);
+        finishTestAndReset();
+      }
+    });
+  }
+
+  /**
+ * Test that removing a parent entity and implicitly removing the child sends
+   * an event to the client that the child was removed.
+   *
+   * TODO(rjrjr): Should cascading deletes be detected?
+   */
+  public void disableTestMethodWithSideEffectDeleteChild() {
+    delayTestFinish(DELAY_TEST_FINISH);
+
+    // Persist bar.
+    SimpleBarRequest context = req.simpleBarRequest();
+    final SimpleBarProxy bar = context.create(SimpleBarProxy.class);
+    context.persistAndReturnSelf().using(bar).fire(
+        new Receiver<SimpleBarProxy>() {
+          @Override
+          public void onSuccess(SimpleBarProxy persistentBar) {
+            // Persist foo with bar as a child.
+            SimpleFooRequest context = req.simpleFooRequest();
+            SimpleFooProxy foo = context.create(SimpleFooProxy.class);
+ final Request<SimpleFooProxy> persistRequest = context.persistAndReturnSelf().using(
+                foo);
+            foo = context.edit(foo);
+            foo.setUserName("John");
+            foo.setBarField(bar);
+            persistRequest.fire(new Receiver<SimpleFooProxy>() {
+              @Override
+              public void onSuccess(SimpleFooProxy persistentFoo) {
+                // Handle changes to SimpleFooProxy.
+ final SimpleFooEventHandler<SimpleFooProxy> fooHandler = new SimpleFooEventHandler<SimpleFooProxy>();
+                EntityProxyChange.registerForProxyType(req.getEventBus(),
+                    SimpleFooProxy.class, fooHandler);
+
+                // Handle changes to SimpleBarProxy.
+ final SimpleFooEventHandler<SimpleBarProxy> barHandler = new SimpleFooEventHandler<SimpleBarProxy>();
+                EntityProxyChange.registerForProxyType(req.getEventBus(),
+                    SimpleBarProxy.class, barHandler);
+
+                // Delete bar.
+                SimpleFooRequest context = req.simpleFooRequest();
+ final Request<Void> deleteRequest = context.deleteBar().using(
+                    persistentFoo);
+                SimpleFooProxy editable = context.edit(persistentFoo);
+                editable.setBarField(bar);
+                deleteRequest.fire(new Receiver<Void>() {
+                  @Override
+                  public void onSuccess(Void response) {
+ assertEquals(1, fooHandler.updateEventCount); // set bar to
+                    // null
+                    assertEquals(1, fooHandler.totalEventCount);
+
+ assertEquals(1, barHandler.deleteEventCount); // deleted bar
+                    assertEquals(1, barHandler.totalEventCount);
+                    finishTestAndReset();
+                  }
+                });
+              }
+            });
+          }
+        });
+  }

   @Override
   public String getModuleName() {
     return "com.google.gwt.requestfactory.RequestFactorySuite";
   }
+
+  /**
+ * Test that the same object, referenced twice, points to the same instance.
+   */
+  public void testAntiAliasing() {
+    delayTestFinish(DELAY_TEST_FINISH);
+    simpleFooRequest().fetchDoubleReference().with("fooField",
+        "selfOneToManyField").fire(new Receiver<SimpleFooProxy>() {
+      @Override
+      public void onSuccess(SimpleFooProxy response) {
+        assertNotNull(response.getFooField());
+        assertSame(response.getFooField(),
+            response.getSelfOneToManyField().get(0));
+        finishTestAndReset();
+      }
+    });
+  }

   /**
    * Test that we can commit child objects.
@@ -221,11 +343,6 @@
       }
     });
   }
-
-  public void testChangedNothing() {
-    SimpleFooRequest context = simpleFooRequest();
-    assertFalse(context.isChanged());
-  }

   public void testChangedCreate() {
     SimpleFooRequest context = simpleFooRequest();
@@ -276,6 +393,11 @@
           }
         });
   }
+
+  public void testChangedNothing() {
+    SimpleFooRequest context = simpleFooRequest();
+    assertFalse(context.isChanged());
+  }

   public void testClassToken() {
     String token = req.getHistoryToken(SimpleFooProxy.class);
@@ -284,6 +406,21 @@
     SimpleFooProxy foo = simpleFooRequest().create(SimpleFooProxy.class);
     assertEquals(SimpleFooProxy.class, foo.stableId().getProxyClass());
   }
+
+  public void testCollectionSubProperties() {
+    delayTestFinish(DELAY_TEST_FINISH);
+    simpleFooRequest().getSimpleFooWithSubPropertyCollection().with(
+        "selfOneToManyField", "selfOneToManyField.fooField").fire(
+        new Receiver<SimpleFooProxy>() {
+          @Override
+          public void onSuccess(SimpleFooProxy response) {
+            assertEquals(
+                "I'm here",
+ response.getSelfOneToManyField().get(0).getFooField().getUserName());
+            finishTestAndReset();
+          }
+        });
+  }

   public void testDummyCreate() {
     delayTestFinish(DELAY_TEST_FINISH);
@@ -360,92 +497,6 @@
       }
     });
   }
-
-  public void testFindFindEdit() {
-    delayTestFinish(5000);
-
- final SimpleFooEventHandler<SimpleFooProxy> handler = new SimpleFooEventHandler<SimpleFooProxy>();
-    EntityProxyChange.registerForProxyType(req.getEventBus(),
-        SimpleFooProxy.class, handler);
-
-    req.simpleFooRequest().findSimpleFooById(999L).fire(
-        new Receiver<SimpleFooProxy>() {
-
-          @Override
-          public void onSuccess(SimpleFooProxy newFoo) {
-            assertEquals(1, handler.updateEventCount);
-            assertEquals(1, handler.totalEventCount);
-
-            req.simpleFooRequest().findSimpleFooById(999L).fire(
-                new Receiver<SimpleFooProxy>() {
-
-                  @Override
-                  public void onSuccess(SimpleFooProxy newFoo) {
-                    // no events are fired second time.
-                    assertEquals(1, handler.updateEventCount);
-                    assertEquals(1, handler.totalEventCount);
-                    SimpleFooRequest context = req.simpleFooRequest();
- final Request<Void> mutateRequest = context.persist().using(
-                        newFoo);
-                    newFoo = context.edit(newFoo);
-                    newFoo.setUserName("Ray");
-                    mutateRequest.fire(new Receiver<Void>() {
-                      @Override
-                      public void onSuccess(Void response) {
-                        // events fired on updates.
-                        assertEquals(2, handler.updateEventCount);
-                        assertEquals(2, handler.totalEventCount);
-
-                        finishTestAndReset();
-                      }
-                    });
-                  }
-                });
-          }
-        });
-  }
-
-  public void disabled_testEchoSimpleFutures() {
-    // tests if futureIds can be echoed back.
-    delayTestFinish(DELAY_TEST_FINISH);
- final SimpleFooEventHandler<SimpleFooProxy> handler = new SimpleFooEventHandler<SimpleFooProxy>();
-    EntityProxyChange.registerForProxyType(req.getEventBus(),
-        SimpleFooProxy.class, handler);
-    SimpleFooRequest context = req.simpleFooRequest();
-    final SimpleFooProxy simpleFoo = context.create(SimpleFooProxy.class);
-    context.echo(simpleFoo).fire(new Receiver<SimpleFooProxy>() {
-      @Override
-      public void onSuccess(SimpleFooProxy response) {
-        assertEquals(0, handler.totalEventCount);
-        checkStableIdEquals(simpleFoo, response);
-        finishTestAndReset();
-      }
-    });
-  }
-
-  public void disabled_testEchoComplexFutures() {
- // relate futures on the server. Check if the relationship is still present
-    // on the client.
-    delayTestFinish(DELAY_TEST_FINISH);
- final SimpleFooEventHandler<SimpleFooProxy> handler = new SimpleFooEventHandler<SimpleFooProxy>();
-    EntityProxyChange.registerForProxyType(req.getEventBus(),
-        SimpleFooProxy.class, handler);
-    SimpleFooRequest context = req.simpleFooRequest();
-    final SimpleFooProxy simpleFoo = context.create(SimpleFooProxy.class);
-    final SimpleBarProxy simpleBar = context.create(SimpleBarProxy.class);
-    context.echoComplex(simpleFoo, simpleBar).fire(
-        new Receiver<SimpleFooProxy>() {
-          @Override
-          public void onSuccess(SimpleFooProxy response) {
-            assertEquals(0, handler.totalEventCount);
-            checkStableIdEquals(simpleFoo, response);
-            SimpleBarProxy responseBar = response.getBarField();
-            assertNotNull(responseBar);
-            checkStableIdEquals(simpleBar, responseBar);
-            finishTestAndReset();
-          }
-        });
-  }

   /**
* Tests behaviors relating to editing an object with one context and then
@@ -546,6 +597,67 @@
       }
     });
   }
+
+  public void testFindFindEdit() {
+    delayTestFinish(5000);
+
+ final SimpleFooEventHandler<SimpleFooProxy> handler = new SimpleFooEventHandler<SimpleFooProxy>();
+    EntityProxyChange.registerForProxyType(req.getEventBus(),
+        SimpleFooProxy.class, handler);
+
+    req.simpleFooRequest().findSimpleFooById(999L).fire(
+        new Receiver<SimpleFooProxy>() {
+
+          @Override
+          public void onSuccess(SimpleFooProxy newFoo) {
+            assertEquals(1, handler.updateEventCount);
+            assertEquals(1, handler.totalEventCount);
+
+            req.simpleFooRequest().findSimpleFooById(999L).fire(
+                new Receiver<SimpleFooProxy>() {
+
+                  @Override
+                  public void onSuccess(SimpleFooProxy newFoo) {
+                    // no events are fired second time.
+                    assertEquals(1, handler.updateEventCount);
+                    assertEquals(1, handler.totalEventCount);
+                    SimpleFooRequest context = req.simpleFooRequest();
+ final Request<Void> mutateRequest = context.persist().using(
+                        newFoo);
+                    newFoo = context.edit(newFoo);
+                    newFoo.setUserName("Ray");
+                    mutateRequest.fire(new Receiver<Void>() {
+                      @Override
+                      public void onSuccess(Void response) {
+                        // events fired on updates.
+                        assertEquals(2, handler.updateEventCount);
+                        assertEquals(2, handler.totalEventCount);
+
+                        finishTestAndReset();
+                      }
+                    });
+                  }
+                });
+          }
+        });
+  }
+
+  public void testForwardReferenceDecode() {
+    delayTestFinish(DELAY_TEST_FINISH);
+    simpleFooRequest().getTripletReference().with(
+        "selfOneToManyField.selfOneToManyField.fooField").fire(
+        new Receiver<SimpleFooProxy>() {
+          public void onSuccess(SimpleFooProxy response) {
+            assertNotNull(response.getSelfOneToManyField().get(0));
+ assertNotNull(response.getSelfOneToManyField().get(0).getSelfOneToManyField()); + assertNotNull(response.getSelfOneToManyField().get(0).getSelfOneToManyField().get(
+                0));
+ assertNotNull(response.getSelfOneToManyField().get(0).getSelfOneToManyField().get(
+                0).getFooField());
+            finishTestAndReset();
+          }
+        });
+  }

   public void testGetEventBus() {
     assertEquals(eventBus, req.getEventBus());
@@ -625,6 +737,87 @@
       }
     });
   }
+
+  /*
+ * tests that (a) any method can have a side effect that is handled correctly.
+   * (b) instance methods are handled correctly and (c) a request cannot be
+   * reused after a successful response is received. (Yet?)
+   */
+  public void testMethodWithSideEffects() {
+    delayTestFinish(DELAY_TEST_FINISH);
+
+ final SimpleFooEventHandler<SimpleFooProxy> handler = new SimpleFooEventHandler<SimpleFooProxy>();
+    EntityProxyChange.registerForProxyType(req.getEventBus(),
+        SimpleFooProxy.class, handler);
+
+    simpleFooRequest().findSimpleFooById(999L).fire(
+        new Receiver<SimpleFooProxy>() {
+
+          @Override
+          public void onSuccess(SimpleFooProxy newFoo) {
+            assertEquals(1, handler.updateEventCount);
+            assertEquals(1, handler.totalEventCount);
+            SimpleFooRequest context = simpleFooRequest();
+ final Request<Long> mutateRequest = context.countSimpleFooWithUserNameSideEffect().using(
+                newFoo);
+            newFoo = context.edit(newFoo);
+            newFoo.setUserName("Ray");
+            mutateRequest.fire(new Receiver<Long>() {
+              @Override
+              public void onSuccess(Long response) {
+                assertCannotFire(mutateRequest);
+                assertEquals(new Long(2L), response);
+                assertEquals(2, handler.updateEventCount);
+                assertEquals(2, handler.totalEventCount);
+
+                // confirm that the instance method did have the desired
+                // sideEffect.
+                simpleFooRequest().findSimpleFooById(999L).fire(
+                    new Receiver<SimpleFooProxy>() {
+                      @Override
+                      public void onSuccess(SimpleFooProxy finalFoo) {
+                        assertEquals("Ray", finalFoo.getUserName());
+                        assertEquals(2, handler.updateEventCount);
+                        assertEquals(2, handler.totalEventCount);
+                        finishTestAndReset();
+                      }
+                    });
+              }
+
+            });
+
+            try {
+              newFoo.setUserName("Barney");
+              fail();
+            } catch (IllegalStateException e) {
+              /* pass, cannot change a request that is in flight */
+            }
+          }
+        });
+  }
+
+  public void testMultipleEdits() {
+    RequestContext c1 = req.simpleFooRequest();
+    SimpleFooProxy proxy = c1.create(SimpleFooProxy.class);
+    // Re-editing is idempotent
+    assertSame(proxy, c1.edit(c1.edit(proxy)));
+
+    // Should not allow "crossing the steams"
+    RequestContext c2 = req.simpleFooRequest();
+    try {
+      c2.edit(proxy);
+      fail();
+    } catch (IllegalArgumentException expected) {
+    }
+  }
+
+  /**
+   * Ensures that a service method can respond with a null value.
+   */
+  public void testNullEntityProxyResult() {
+    delayTestFinish(DELAY_TEST_FINISH);
+    simpleFooRequest().returnNullSimpleFoo().fire(new NullReceiver());
+  }

   /**
    * Test that a null value can be sent in a request.
@@ -639,6 +832,14 @@
       }
     });
   }
+
+  /**
+   * Ensures that a service method can respond with a null value.
+   */
+  public void testNullListResult() {
+    delayTestFinish(DELAY_TEST_FINISH);
+    simpleFooRequest().returnNullList().fire(new NullReceiver());
+  }

   /**
    * Test that a null value can be sent in a request.
@@ -691,6 +892,14 @@
       }
     });
   }
+
+  /**
+   * Ensures that a service method can respond with a null value.
+   */
+  public void testNullStringResult() {
+    delayTestFinish(DELAY_TEST_FINISH);
+    simpleFooRequest().returnNullString().fire(new NullReceiver());
+  }

   /**
    * Test that a null value can be sent within a list of entities.
@@ -723,7 +932,7 @@
    */
   public void testNullValueInIntegerListRequest() {
     delayTestFinish(DELAY_TEST_FINISH);
-    List<Integer> list = Arrays.asList(new Integer[] {1, 2, null});
+    List<Integer> list = Arrays.asList(new Integer[]{1, 2, null});
final Request<Void> fooReq = req.simpleFooRequest().receiveNullValueInIntegerList(
         list);
     fooReq.fire(new Receiver<Void>() {
@@ -739,7 +948,7 @@
    */
   public void testNullValueInStringListRequest() {
     delayTestFinish(DELAY_TEST_FINISH);
- List<String> list = Arrays.asList(new String[] {"nonnull", "null", null}); + List<String> list = Arrays.asList(new String[]{"nonnull", "null", null}); final Request<Void> fooReq = req.simpleFooRequest().receiveNullValueInStringList(
         list);
     fooReq.fire(new Receiver<Void>() {
@@ -749,164 +958,6 @@
       }
     });
   }
-
-  /**
-   * Ensures that a service method can respond with a null value.
-   */
-  public void testNullListResult() {
-    delayTestFinish(DELAY_TEST_FINISH);
-    simpleFooRequest().returnNullList().fire(new NullReceiver());
-  }
-
-  public void testMultipleEdits() {
-    RequestContext c1 = req.simpleFooRequest();
-    SimpleFooProxy proxy = c1.create(SimpleFooProxy.class);
-    // Re-editing is idempotent
-    assertSame(proxy, c1.edit(c1.edit(proxy)));
-
-    // Should not allow "crossing the steams"
-    RequestContext c2 = req.simpleFooRequest();
-    try {
-      c2.edit(proxy);
-      fail();
-    } catch (IllegalArgumentException expected) {
-    }
-  }
-
-  /**
-   * Ensures that a service method can respond with a null value.
-   */
-  public void testNullEntityProxyResult() {
-    delayTestFinish(DELAY_TEST_FINISH);
-    simpleFooRequest().returnNullSimpleFoo().fire(new NullReceiver());
-  }
-
-  /**
-   * Ensures that a service method can respond with a null value.
-   */
-  public void testNullStringResult() {
-    delayTestFinish(DELAY_TEST_FINISH);
-    simpleFooRequest().returnNullString().fire(new NullReceiver());
-  }
-
-  /*
- * tests that (a) any method can have a side effect that is handled correctly.
-   * (b) instance methods are handled correctly and (c) a request cannot be
-   * reused after a successful response is received. (Yet?)
-   */
-  public void testMethodWithSideEffects() {
-    delayTestFinish(DELAY_TEST_FINISH);
-
- final SimpleFooEventHandler<SimpleFooProxy> handler = new SimpleFooEventHandler<SimpleFooProxy>();
-    EntityProxyChange.registerForProxyType(req.getEventBus(),
-        SimpleFooProxy.class, handler);
-
-    simpleFooRequest().findSimpleFooById(999L).fire(
-        new Receiver<SimpleFooProxy>() {
-
-          @Override
-          public void onSuccess(SimpleFooProxy newFoo) {
-            assertEquals(1, handler.updateEventCount);
-            assertEquals(1, handler.totalEventCount);
-            SimpleFooRequest context = simpleFooRequest();
- final Request<Long> mutateRequest = context.countSimpleFooWithUserNameSideEffect().using(
-                newFoo);
-            newFoo = context.edit(newFoo);
-            newFoo.setUserName("Ray");
-            mutateRequest.fire(new Receiver<Long>() {
-              @Override
-              public void onSuccess(Long response) {
-                assertCannotFire(mutateRequest);
-                assertEquals(new Long(2L), response);
-                assertEquals(2, handler.updateEventCount);
-                assertEquals(2, handler.totalEventCount);
-
-                // confirm that the instance method did have the desired
-                // sideEffect.
-                simpleFooRequest().findSimpleFooById(999L).fire(
-                    new Receiver<SimpleFooProxy>() {
-                      @Override
-                      public void onSuccess(SimpleFooProxy finalFoo) {
-                        assertEquals("Ray", finalFoo.getUserName());
-                        assertEquals(2, handler.updateEventCount);
-                        assertEquals(2, handler.totalEventCount);
-                        finishTestAndReset();
-                      }
-                    });
-              }
-
-            });
-
-            try {
-              newFoo.setUserName("Barney");
-              fail();
-            } catch (IllegalStateException e) {
-              /* pass, cannot change a request that is in flight */
-            }
-          }
-        });
-  }
-
-  /**
- * Test that removing a parent entity and implicitly removing the child sends
-   * an event to the client that the child was removed.
-   *
-   * TODO(rjrjr): Should cascading deletes be detected?
-   */
-  public void disableTestMethodWithSideEffectDeleteChild() {
-    delayTestFinish(DELAY_TEST_FINISH);
-
-    // Persist bar.
-    SimpleBarRequest context = req.simpleBarRequest();
-    final SimpleBarProxy bar = context.create(SimpleBarProxy.class);
-    context.persistAndReturnSelf().using(bar).fire(
-        new Receiver<SimpleBarProxy>() {
-          @Override
-          public void onSuccess(SimpleBarProxy persistentBar) {
-            // Persist foo with bar as a child.
-            SimpleFooRequest context = req.simpleFooRequest();
-            SimpleFooProxy foo = context.create(SimpleFooProxy.class);
- final Request<SimpleFooProxy> persistRequest = context.persistAndReturnSelf().using(
-                foo);
-            foo = context.edit(foo);
-            foo.setUserName("John");
-            foo.setBarField(bar);
-            persistRequest.fire(new Receiver<SimpleFooProxy>() {
-              @Override
-              public void onSuccess(SimpleFooProxy persistentFoo) {
-                // Handle changes to SimpleFooProxy.
- final SimpleFooEventHandler<SimpleFooProxy> fooHandler = new SimpleFooEventHandler<SimpleFooProxy>();
-                EntityProxyChange.registerForProxyType(req.getEventBus(),
-                    SimpleFooProxy.class, fooHandler);
-
-                // Handle changes to SimpleBarProxy.
- final SimpleFooEventHandler<SimpleBarProxy> barHandler = new SimpleFooEventHandler<SimpleBarProxy>();
-                EntityProxyChange.registerForProxyType(req.getEventBus(),
-                    SimpleBarProxy.class, barHandler);
-
-                // Delete bar.
-                SimpleFooRequest context = req.simpleFooRequest();
- final Request<Void> deleteRequest = context.deleteBar().using(
-                    persistentFoo);
-                SimpleFooProxy editable = context.edit(persistentFoo);
-                editable.setBarField(bar);
-                deleteRequest.fire(new Receiver<Void>() {
-                  @Override
-                  public void onSuccess(Void response) {
- assertEquals(1, fooHandler.updateEventCount); // set bar to
-                    // null
-                    assertEquals(1, fooHandler.totalEventCount);
-
- assertEquals(1, barHandler.deleteEventCount); // deleted bar
-                    assertEquals(1, barHandler.totalEventCount);
-                    finishTestAndReset();
-                  }
-                });
-              }
-            });
-          }
-        });
-  }

   public void testPersistAllValueTypes() {
     delayTestFinish(DELAY_TEST_FINISH);
@@ -915,8 +966,8 @@
     SimpleFooProxy f = r.create(SimpleFooProxy.class);

     f.setUserName("user name");
-    f.setByteField((byte)100);
-    f.setShortField((short)12345);
+    f.setByteField((byte) 100);
+    f.setShortField((short) 12345);
     f.setFloatField(1234.56f);
     f.setDoubleField(1.2345);
     f.setLongField(1234L);
@@ -928,8 +979,8 @@
       @Override
       public void onSuccess(SimpleFooProxy f) {
         assertEquals("user name", f.getUserName());
-        assertEquals(Byte.valueOf((byte)100), f.getByteField());
-        assertEquals(Short.valueOf((short)12345), f.getShortField());
+        assertEquals(Byte.valueOf((byte) 100), f.getByteField());
+        assertEquals(Short.valueOf((short) 12345), f.getShortField());
         assertEquals(Float.valueOf(1234.56f), f.getFloatField());
         assertEquals(Double.valueOf(1.2345), f.getDoubleField());
         assertEquals(Long.valueOf(1234L), f.getLongField());
@@ -966,35 +1017,35 @@
                     fooProxy.setBarField(barProxy);
                     fooProxy.setUserName("Hello");
                     fooProxy.setByteField((byte) 55);
-                    context.persistAndReturnSelf().using(fooProxy).fire(
-                        new Receiver<SimpleFooProxy>() {
-                          @Override
-                          public void onSuccess(SimpleFooProxy received) {
-                            // Check that Foo points to Bar
-                            assertNotNull(received.getBarField());
-                            assertEquals(barProxy.stableId(),
-                                received.getBarField().stableId());
-                            assertEquals("Hello", received.getUserName());
-                            assertTrue(55 == received.getByteField());
-
-                            // Unset the association
-                            SimpleFooRequest context = simpleFooRequest();
-                            received = context.edit(received);
-                            received.setBarField(null);
-                            received.setUserName(null);
-                            received.setByteField(null);
- context.persistAndReturnSelf().using(received).fire(
-                                new Receiver<SimpleFooProxy>() {
-                                  @Override
- public void onSuccess(SimpleFooProxy response) {
-                                    assertNull(response.getBarField());
-                                    assertNull(response.getUserName());
-                                    assertNull(response.getByteField());
-                                    finishTestAndReset();
-                                  }
-                                });
-                          }
-                        });
+                    context.persistAndReturnSelf().using(fooProxy).with(
+                        "barField").fire(new Receiver<SimpleFooProxy>() {
+                      @Override
+                      public void onSuccess(SimpleFooProxy received) {
+                        // Check that Foo points to Bar
+                        assertNotNull(received.getBarField());
+                        assertEquals(barProxy.stableId(),
+                            received.getBarField().stableId());
+                        assertEquals("Hello", received.getUserName());
+                        assertTrue(55 == received.getByteField());
+
+                        // Unset the association
+                        SimpleFooRequest context = simpleFooRequest();
+                        received = context.edit(received);
+                        received.setBarField(null);
+                        received.setUserName(null);
+                        received.setByteField(null);
+ context.persistAndReturnSelf().using(received).fire(
+                            new Receiver<SimpleFooProxy>() {
+                              @Override
+ public void onSuccess(SimpleFooProxy response) {
+                                assertNull(response.getBarField());
+                                assertNull(response.getUserName());
+                                assertNull(response.getByteField());
+                                finishTestAndReset();
+                              }
+                            });
+                      }
+                    });
                   }
                 });
           }
@@ -1053,6 +1104,30 @@
       }
     });
   }
+
+  /**
+ * Ensure that a relationship can be set up between two newly-created objects.
+   */
+  public void testPersistFutureToFuture() {
+    delayTestFinish(DELAY_TEST_FINISH);
+    SimpleFooRequest context = simpleFooRequest();
+    SimpleFooProxy newFoo = context.create(SimpleFooProxy.class);
+    final SimpleBarProxy newBar = context.create(SimpleBarProxy.class);
+
+    Request<SimpleFooProxy> fooReq = context.persistAndReturnSelf().using(
+        newFoo).with("barField");
+    newFoo = context.edit(newFoo);
+    newFoo.setBarField(newBar);
+
+    fooReq.fire(new Receiver<SimpleFooProxy>() {
+      @Override
+      public void onSuccess(SimpleFooProxy response) {
+        assertNotNull(response.getBarField());
+        assertEquals(newBar.stableId(), response.getBarField().stableId());
+        finishTestAndReset();
+      }
+    });
+  }

   /*
* Find Entity2 Create Entity, Persist Entity Relate Entity2 to Entity Persist
@@ -1091,30 +1166,6 @@
           }
         });
   }
-
-  /**
- * Ensure that a relationship can be set up between two newly-created objects.
-   */
-  public void testPersistFutureToFuture() {
-    delayTestFinish(DELAY_TEST_FINISH);
-    SimpleFooRequest context = simpleFooRequest();
-    SimpleFooProxy newFoo = context.create(SimpleFooProxy.class);
-    final SimpleBarProxy newBar = context.create(SimpleBarProxy.class);
-
-    Request<SimpleFooProxy> fooReq = context.persistAndReturnSelf().using(
-        newFoo).with("barField");
-    newFoo = context.edit(newFoo);
-    newFoo.setBarField(newBar);
-
-    fooReq.fire(new Receiver<SimpleFooProxy>() {
-      @Override
-      public void onSuccess(SimpleFooProxy response) {
-        assertNotNull(response.getBarField());
-        assertEquals(newBar.stableId(), response.getBarField().stableId());
-        finishTestAndReset();
-      }
-    });
-  }

   /*
* Create Entity, Persist Entity Create Entity2, Perist Entity2 relate Entity2
@@ -1976,14 +2027,14 @@
       }
     });
   }
-
-  protected SimpleFooRequest simpleFooRequest() {
-    return req.simpleFooRequest();
-  }

   protected SimpleBarRequest simpleBarRequest() {
     return req.simpleBarRequest();
   }
+
+  protected SimpleFooRequest simpleFooRequest() {
+    return req.simpleFooRequest();
+  }

   private void assertCannotFire(final Request<Long> mutateRequest) {
     try {
=======================================
--- /trunk/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java Mon Oct 4 15:35:00 2010 +++ /trunk/user/test/com/google/gwt/requestfactory/server/SimpleFoo.java Tue Oct 5 09:11:00 2010
@@ -21,6 +21,7 @@
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -58,6 +59,16 @@
     simpleFoo.setBarField(simpleBar);
     return simpleFoo;
   }
+
+  public static SimpleFoo fetchDoubleReference() {
+    SimpleFoo foo = new SimpleFoo();
+    SimpleFoo foo2 = new SimpleFoo();
+    foo.setFooField(foo2);
+    foo.setSelfOneToManyField(Arrays.asList(foo2));
+    foo.persist();
+    foo2.persist();
+    return foo;
+  }

   public static List<SimpleFoo> findAll() {
     return new ArrayList<SimpleFoo>(get().values());
@@ -107,6 +118,36 @@
     list.add(3);
     return list;
   }
+
+  public static SimpleFoo getSimpleFooWithSubPropertyCollection() {
+    SimpleFoo foo = new SimpleFoo();
+    SimpleFoo subFoo = new SimpleFoo();
+    SimpleFoo subSubFoo = new SimpleFoo();
+    subFoo.setFooField(subSubFoo);
+    subSubFoo.setUserName("I'm here");
+    subSubFoo.persist();
+    subFoo.persist();
+    foo.persist();
+    foo.setSelfOneToManyField(Arrays.asList(subFoo));
+    return foo;
+  }
+
+  public static SimpleFoo getTripletReference() {
+    SimpleFoo foo1 = new SimpleFoo();
+    SimpleFoo foo2 = new SimpleFoo();
+    SimpleFoo foo3 = new SimpleFoo();
+    ArrayList<SimpleFoo> foos = new ArrayList<SimpleFoo>();
+    foos.add(foo2);
+    ArrayList<SimpleFoo> subFoos = new ArrayList<SimpleFoo>();
+    subFoos.add(foo3);
+    foo1.setSelfOneToManyField(foos);
+    foo2.setSelfOneToManyField(subFoos);
+    foo3.setFooField(foo2);
+    foo1.persist();
+    foo2.persist();
+    foo3.persist();
+    return foo1;
+  }

   public static Boolean processBooleanList(List<Boolean> values) {
     return values.get(0);
=======================================
--- /trunk/user/test/com/google/gwt/requestfactory/shared/SimpleFooRequest.java Fri Oct 1 18:15:55 2010 +++ /trunk/user/test/com/google/gwt/requestfactory/shared/SimpleFooRequest.java Tue Oct 5 09:11:00 2010
@@ -37,6 +37,8 @@
   Request<SimpleFooProxy> echoComplex(SimpleFooProxy fooProxy,
       SimpleBarProxy barProxy);

+  Request<SimpleFooProxy> fetchDoubleReference();
+
   Request<List<SimpleFooProxy>> findAll();

   Request<SimpleFooProxy> findSimpleFooById(Long id);
@@ -45,6 +47,10 @@

   Request<Set<Integer>> getNumberSet();

+  Request<SimpleFooProxy> getSimpleFooWithSubPropertyCollection();
+
+  Request<SimpleFooProxy> getTripletReference();
+
   InstanceRequest<SimpleFooProxy, String> hello(SimpleBarProxy proxy);

   InstanceRequest<SimpleFooProxy, Void> persist();
@@ -59,11 +65,11 @@

   Request<SimpleEnum> processEnumList(List<SimpleEnum> values);

-  Request<String> processString(String value);
-
   InstanceRequest<SimpleFooProxy, String> processList(
       List<SimpleFooProxy> values);

+  Request<String> processString(String value);
+
   InstanceRequest<SimpleFooProxy, Void> receiveNull(String value);

   Request<Void> receiveNullList(List<SimpleFooProxy> value);

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to