Cherry-pick r10472 into GWT 2.4 branch.


git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/2.4@10474 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/editor/client/impl/AbstractEditorDelegate.java b/user/src/com/google/gwt/editor/client/impl/AbstractEditorDelegate.java
index ed1e52b..583711e 100644
--- a/user/src/com/google/gwt/editor/client/impl/AbstractEditorDelegate.java
+++ b/user/src/com/google/gwt/editor/client/impl/AbstractEditorDelegate.java
@@ -80,7 +80,7 @@
         subDelegate.path = subPath;
       }
       subDelegate.setObject(ensureMutable(object));
-      traverse(new Initializer(), subDelegate);
+      traverse(createInitializerVisitor(), subDelegate);
     }
 
     public void detach(S subEditor) {
@@ -177,6 +177,10 @@
     throw new IllegalStateException();
   }
 
+  protected EditorVisitor createInitializerVisitor() {
+    return new Initializer();
+  }
+
   protected <Q> Q ensureMutable(Q object) {
     return object;
   }
diff --git a/user/src/com/google/gwt/editor/client/impl/BaseEditorDriver.java b/user/src/com/google/gwt/editor/client/impl/BaseEditorDriver.java
index dddb011..7c4dc5f 100644
--- a/user/src/com/google/gwt/editor/client/impl/BaseEditorDriver.java
+++ b/user/src/com/google/gwt/editor/client/impl/BaseEditorDriver.java
@@ -63,8 +63,7 @@
     return c.isDirty() || !leafValueMap.equals(c.getLeafValues());
   }
 
-  public boolean setConstraintViolations(
-      final Iterable<ConstraintViolation<?>> violations) {
+  public boolean setConstraintViolations(final Iterable<ConstraintViolation<?>> violations) {
     return doSetViolations(SimpleViolation.iterableFromConstrantViolations(violations));
   }
 
@@ -73,8 +72,7 @@
     if (GWT.isProdMode()) {
       return super.toString();
     } else {
-      return editor == null ? "Uninitialized"
-          : EditorHierarchyPrinter.toString(asEditorDriver());
+      return editor == null ? "Uninitialized" : EditorHierarchyPrinter.toString(asEditorDriver());
     }
   }
 
@@ -84,12 +82,16 @@
 
   protected abstract AbstractEditorDelegate<T, E> createDelegate();
 
+  protected EditorVisitor createInitializerVisitor() {
+    return new Initializer();
+  }
+
   protected void doEdit(T object) {
     checkEditor();
     object = delegate.ensureMutable(object);
     this.object = object;
     delegate.setObject(object);
-    accept(new Initializer());
+    accept(createInitializerVisitor());
     DirtCollector c = new DirtCollector();
     accept(c);
     leafValueMap = c.getLeafValues();
@@ -110,8 +112,7 @@
 
   protected boolean doSetViolations(Iterable<SimpleViolation> violations) {
     checkObject();
-    SimpleViolation.pushViolations(violations, asEditorDriver(),
-        getViolationKeyMethod());
+    SimpleViolation.pushViolations(violations, asEditorDriver(), getViolationKeyMethod());
 
     // Collect the errors, which will take care of co-editor chains.
     errors = new ArrayList<EditorError>();
diff --git a/user/src/com/google/gwt/editor/client/impl/Initializer.java b/user/src/com/google/gwt/editor/client/impl/Initializer.java
index 84168a5..3e9eb4c 100644
--- a/user/src/com/google/gwt/editor/client/impl/Initializer.java
+++ b/user/src/com/google/gwt/editor/client/impl/Initializer.java
@@ -24,7 +24,7 @@
  * Extends the logic in Refresher to provide the editor instance with references
  * to framework plumbing fixes.
  */
-class Initializer extends Refresher {
+public class Initializer extends Refresher {
 
   @Override
   public <Q> boolean visit(EditorContext<Q> ctx) {
diff --git a/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/AbstractRequestFactoryEditorDriver.java b/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/AbstractRequestFactoryEditorDriver.java
index 79a767f..fa7aa35 100644
--- a/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/AbstractRequestFactoryEditorDriver.java
+++ b/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/AbstractRequestFactoryEditorDriver.java
@@ -16,7 +16,6 @@
 package com.google.web.bindery.requestfactory.gwt.client.impl;
 
 import com.google.gwt.editor.client.Editor;
-import com.google.gwt.editor.client.EditorContext;
 import com.google.gwt.editor.client.EditorVisitor;
 import com.google.gwt.editor.client.impl.AbstractEditorDelegate;
 import com.google.gwt.editor.client.impl.BaseEditorDriver;
@@ -26,7 +25,6 @@
 import com.google.web.bindery.autobean.shared.AutoBean;
 import com.google.web.bindery.autobean.shared.AutoBeanUtils;
 import com.google.web.bindery.event.shared.EventBus;
-import com.google.web.bindery.requestfactory.gwt.client.HasRequestContext;
 import com.google.web.bindery.requestfactory.gwt.client.RequestFactoryEditorDriver;
 import com.google.web.bindery.requestfactory.shared.EntityProxy;
 import com.google.web.bindery.requestfactory.shared.RequestContext;
@@ -43,8 +41,8 @@
  * @param <R> the type being edited
  * @param <E> the type of Editor
  */
-public abstract class AbstractRequestFactoryEditorDriver<R, E extends Editor<R>>
-    extends BaseEditorDriver<R, E> implements RequestFactoryEditorDriver<R, E> {
+public abstract class AbstractRequestFactoryEditorDriver<R, E extends Editor<R>> extends
+    BaseEditorDriver<R, E> implements RequestFactoryEditorDriver<R, E> {
 
   /**
    * Adapts a RequestFactory Violation object to the SimpleViolation interface.
@@ -89,12 +87,14 @@
 
     private final Iterable<com.google.web.bindery.requestfactory.shared.Violation> violations;
 
-    public ViolationIterable(Iterable<com.google.web.bindery.requestfactory.shared.Violation> violations) {
+    public ViolationIterable(
+        Iterable<com.google.web.bindery.requestfactory.shared.Violation> violations) {
       this.violations = violations;
     }
 
     public Iterator<SimpleViolation> iterator() {
-      final Iterator<com.google.web.bindery.requestfactory.shared.Violation> source = violations.iterator();
+      final Iterator<com.google.web.bindery.requestfactory.shared.Violation> source =
+          violations.iterator();
       return new Iterator<SimpleViolation>() {
         public boolean hasNext() {
           return source.hasNext();
@@ -159,22 +159,13 @@
     edit(object, null);
   }
 
-  public void edit(R object, final RequestContext saveRequest) {
+  public void edit(R object, RequestContext saveRequest) {
     this.saveRequest = saveRequest;
-    // Provide the delegate and maybe the editor with the RequestContext
-    accept(new EditorVisitor() {
-      @Override
-      public <T> void endVisit(EditorContext<T> ctx) {
-        RequestFactoryEditorDelegate<?, ?> delegate = (RequestFactoryEditorDelegate<?, ?>) ctx.getEditorDelegate();
-        if (delegate != null) {
-          delegate.setRequestContext(saveRequest);
-        }
-        Editor<T> editor = ctx.getEditor();
-        if (editor instanceof HasRequestContext<?>) {
-          ((HasRequestContext<T>) editor).setRequestContext(saveRequest);
-        }
-      }
-    });
+    /*
+     * Provide the delegate with the RequestContext so ensureMutable works as
+     * expected Editor will be provided the delegate by the Initializer visitor.
+     */
+    ((RequestFactoryEditorDelegate<R, E>) getDelegate()).setRequestContext(saveRequest);
     doEdit(object);
   }
 
@@ -192,8 +183,7 @@
     doInitialize(null, null, editor);
   }
 
-  public void initialize(EventBus eventBus, RequestFactory requestFactory,
-      E editor) {
+  public void initialize(EventBus eventBus, RequestFactory requestFactory, E editor) {
     assert eventBus != null : "eventBus must not be null";
     assert requestFactory != null : "requestFactory must not be null";
     doInitialize(eventBus, requestFactory, editor);
@@ -204,7 +194,8 @@
   }
 
   @SuppressWarnings("deprecation")
-  public boolean setViolations(Iterable<com.google.web.bindery.requestfactory.shared.Violation> violations) {
+  public boolean setViolations(
+      Iterable<com.google.web.bindery.requestfactory.shared.Violation> violations) {
     return doSetViolations(new ViolationIterable(violations));
   }
 
@@ -216,12 +207,16 @@
 
   @Override
   protected void configureDelegate(AbstractEditorDelegate<R, E> rootDelegate) {
-    ((RequestFactoryEditorDelegate<R, E>) rootDelegate).initialize(eventBus,
-        factory, "", getEditor());
+    ((RequestFactoryEditorDelegate<R, E>) rootDelegate).initialize(eventBus, factory, "",
+        getEditor());
   }
 
-  protected void doInitialize(EventBus eventBus, RequestFactory requestFactory,
-      E editor) {
+  @Override
+  protected EditorVisitor createInitializerVisitor() {
+    return new Initializer(saveRequest);
+  }
+
+  protected void doInitialize(EventBus eventBus, RequestFactory requestFactory, E editor) {
     this.eventBus = eventBus;
     this.factory = requestFactory;
     super.doInitialize(editor);
diff --git a/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/Initializer.java b/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/Initializer.java
new file mode 100644
index 0000000..8e43e36
--- /dev/null
+++ b/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/Initializer.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2011 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.web.bindery.requestfactory.gwt.client.impl;
+
+import com.google.gwt.editor.client.Editor;
+import com.google.gwt.editor.client.EditorContext;
+import com.google.web.bindery.requestfactory.gwt.client.HasRequestContext;
+import com.google.web.bindery.requestfactory.shared.RequestContext;
+
+/**
+ * Extension of standard Editor Initializer that provides the current
+ * RequestContext.
+ */
+class Initializer extends com.google.gwt.editor.client.impl.Initializer {
+
+  private final RequestContext request;
+
+  public Initializer(RequestContext request) {
+    this.request = request;
+  }
+
+  @Override
+  public <T> boolean visit(EditorContext<T> ctx) {
+    RequestFactoryEditorDelegate<?, ?> delegate =
+        (RequestFactoryEditorDelegate<?, ?>) ctx.getEditorDelegate();
+    if (delegate != null) {
+      delegate.setRequestContext(request);
+    }
+    Editor<T> editor = ctx.getEditor();
+    if (editor instanceof HasRequestContext) {
+      ((HasRequestContext<T>) editor).setRequestContext(request);
+    }
+
+    return super.visit(ctx);
+  }
+}
diff --git a/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/RequestFactoryEditorDelegate.java b/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/RequestFactoryEditorDelegate.java
index 0cf247a..4cbcf92 100644
--- a/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/RequestFactoryEditorDelegate.java
+++ b/user/src/com/google/web/bindery/requestfactory/gwt/client/impl/RequestFactoryEditorDelegate.java
@@ -16,9 +16,9 @@
 package com.google.web.bindery.requestfactory.gwt.client.impl;
 
 import com.google.gwt.editor.client.Editor;
+import com.google.gwt.editor.client.EditorVisitor;
 import com.google.gwt.editor.client.impl.AbstractEditorDelegate;
 import com.google.gwt.editor.client.impl.Refresher;
-// This import is not an accident, details in subscribe() implementation
 import com.google.gwt.event.shared.HandlerRegistration;
 import com.google.web.bindery.event.shared.EventBus;
 import com.google.web.bindery.requestfactory.shared.BaseProxy;
@@ -127,6 +127,11 @@
   }
 
   @Override
+  protected EditorVisitor createInitializerVisitor() {
+    return new Initializer(request);
+  }
+
+  @Override
   protected <T> T ensureMutable(T object) {
     if (request == null) {
       // Read-only mode
diff --git a/user/test/com/google/web/bindery/requestfactory/gwt/client/ui/EditorTest.java b/user/test/com/google/web/bindery/requestfactory/gwt/client/ui/EditorTest.java
index 963b694..ec6987f 100644
--- a/user/test/com/google/web/bindery/requestfactory/gwt/client/ui/EditorTest.java
+++ b/user/test/com/google/web/bindery/requestfactory/gwt/client/ui/EditorTest.java
@@ -61,12 +61,18 @@
     }
   }
 
-  static class SimpleFooBarNameOnlyEditor implements Editor<SimpleFooProxy> {
+  static class SimpleFooBarNameOnlyEditor implements HasRequestContext<SimpleFooProxy> {
+    RequestContext ctx;
+
     /**
      * Test nested path access.
      */
     @Path("barField.userName")
     final SimpleEditor<String> barName = SimpleEditor.of();
+
+    public void setRequestContext(RequestContext ctx) {
+      this.ctx = ctx;
+    }
   }
 
   interface SimpleFooDriver extends RequestFactoryEditorDriver<SimpleFooProxy, SimpleFooEditor> {
@@ -84,14 +90,6 @@
     @Path("barField.userName")
     final SimpleEditor<String> barName = SimpleEditor.of();
 
-    final ListEditor<SimpleFooProxy, SimpleFooBarNameOnlyEditor> selfOneToManyField = ListEditor
-        .of(new EditorSource<SimpleFooBarNameOnlyEditor>() {
-          @Override
-          public SimpleFooBarNameOnlyEditor create(int index) {
-            return new SimpleFooBarNameOnlyEditor();
-          }
-        });
-
     private final SimpleBarEditor barEditor = new SimpleBarEditor();
 
     List<EditorError> errors;
@@ -118,6 +116,23 @@
     }
   }
 
+  static class SimpleFooEditorWithList implements Editor<SimpleFooProxy> {
+
+    final SimpleEditor<String> userName = SimpleEditor.of();
+
+    final ListEditor<SimpleFooProxy, SimpleFooBarNameOnlyEditor> selfOneToManyField = ListEditor
+        .of(new EditorSource<SimpleFooBarNameOnlyEditor>() {
+          @Override
+          public SimpleFooBarNameOnlyEditor create(int index) {
+            return new SimpleFooBarNameOnlyEditor();
+          }
+        });
+  }
+
+  interface SimpleFooEditorWithListDriver extends
+      RequestFactoryEditorDriver<SimpleFooProxy, SimpleFooEditorWithList> {
+  }
+
   private static final int TEST_TIMEOUT = 5000;
 
   @Override
@@ -132,8 +147,7 @@
     final SimpleFooDriver driver = GWT.create(SimpleFooDriver.class);
     driver.initialize(req, editor);
     final String[] paths = driver.getPaths();
-    assertEquals(Arrays.asList("barField", "selfOneToManyField", "selfOneToManyField.barField"),
-        Arrays.asList(paths));
+    assertEquals(Arrays.asList("barField"), Arrays.asList(paths));
 
     req.simpleFooRequest().findSimpleFooById(1L).with(paths).fire(new Receiver<SimpleFooProxy>() {
       @Override
@@ -157,8 +171,7 @@
         editor.userName.setValue("EditorFooTest");
         // When there are duplicate paths, last declared editor wins
         editor.barEditor().userName.setValue("EditorBarTest");
-        editor.barName.setValue("ignored 1");
-        editor.selfOneToManyField.getEditors().get(0).barName.setValue("ignored 2");
+        editor.barName.setValue("ignored");
         driver.flush().fire();
       }
     });
@@ -211,6 +224,53 @@
         });
   }
 
+  /**
+   * Tests issues with {@code CompositeEditor}s when subeditors are dynamically
+   * created, such as with a {@link ListEditor}.
+   * 
+   * @see http://code.google.com/p/google-web-toolkit/issues/detail?id=6081
+   */
+  public void testList() {
+    delayTestFinish(TEST_TIMEOUT);
+    final SimpleFooEditorWithList editor = new SimpleFooEditorWithList();
+
+    final SimpleFooEditorWithListDriver driver = GWT.create(SimpleFooEditorWithListDriver.class);
+    driver.initialize(req, editor);
+
+    final String[] paths = driver.getPaths();
+    assertEquals(Arrays.asList("selfOneToManyField", "selfOneToManyField.barField"), Arrays
+        .asList(paths));
+
+    req.simpleFooRequest().getSimpleFooWithSubPropertyCollection().with(paths).fire(
+        new Receiver<SimpleFooProxy>() {
+          @Override
+          public void onSuccess(SimpleFooProxy response) {
+
+            SimpleFooRequest context = req.simpleFooRequest();
+            driver.edit(response, context);
+
+            SimpleFooBarNameOnlyEditor subeditor = editor.selfOneToManyField.getEditors().get(0);
+            // test context is correctly set in CompositeEditor subeditors
+            assertSame(context, subeditor.ctx);
+
+            context.persistAndReturnSelf().using(response).with(paths).to(
+                new Receiver<SimpleFooProxy>() {
+                  @Override
+                  public void onSuccess(SimpleFooProxy response) {
+                    assertEquals("EditorBarTest", response.getSelfOneToManyField().get(0)
+                        .getBarField().getUserName());
+                    finishTestAndReset();
+                  }
+                });
+            assertEquals("FOO", subeditor.barName.getValue());
+
+            subeditor.barName.setValue("EditorBarTest");
+
+            driver.flush().fire();
+          }
+        });
+  }
+
   public void testNoSubscription() {
     final SimpleFooEditorWithDelegate editor = new SimpleFooEditorWithDelegate();
 
@@ -233,9 +293,9 @@
    */
   public void testReuse() {
     delayTestFinish(TEST_TIMEOUT);
-    final SimpleFooEditor editor = new SimpleFooEditor();
+    final SimpleFooEditorWithList editor = new SimpleFooEditorWithList();
 
-    final SimpleFooDriver driver = GWT.create(SimpleFooDriver.class);
+    final SimpleFooEditorWithListDriver driver = GWT.create(SimpleFooEditorWithListDriver.class);
     driver.initialize(req, editor);
 
     req.simpleFooRequest().findSimpleFooById(1L).with(driver.getPaths()).fire(
@@ -272,8 +332,7 @@
     driver.initialize(req, editor);
 
     String[] paths = driver.getPaths();
-    assertEquals(Arrays.asList("barField", "selfOneToManyField", "selfOneToManyField.barField"),
-        Arrays.asList(paths));
+    assertEquals(Arrays.asList("barField"), Arrays.asList(paths));
 
     req.simpleFooRequest().findSimpleFooById(1L).with(paths).fire(new Receiver<SimpleFooProxy>() {
       @Override
@@ -337,9 +396,11 @@
 
                   @SuppressWarnings("deprecation")
                   @Override
-                  public void onViolation(Set<com.google.web.bindery.requestfactory.shared.Violation> errors) {
+                  public void onViolation(
+                      Set<com.google.web.bindery.requestfactory.shared.Violation> errors) {
                     assertEquals(1, errors.size());
-                    com.google.web.bindery.requestfactory.shared.Violation v = errors.iterator().next();
+                    com.google.web.bindery.requestfactory.shared.Violation v =
+                        errors.iterator().next();
 
                     driver.setViolations(errors);
                     assertEquals(1, editor.errors.size());