Changes in com.google.gwt.app land to catch up to the RequestFactory
overhaul. In particular, nukes PropertyColumn, and reworks
AbstractProxyEdit now that create semantics have changed. Made APE a
bit less monolithic in the process.

Includes a change to (and test for) RequestFactoryEditorDriver to
allow it to be initialized without access to a factory, made
after discussion with Bob.

I will still be busting up com.google.gwt.app and banishing these
proxy specific places and activities to sample land, but this is a
necessary first step.

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

Review by: cromwellian@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8928 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/app/client/NullParser.java b/user/src/com/google/gwt/app/client/NullParser.java
deleted file mode 100644
index d8acee9..0000000
--- a/user/src/com/google/gwt/app/client/NullParser.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.gwt.app.client;
-
-import com.google.gwt.requestfactory.shared.EntityProxy;
-import com.google.gwt.text.shared.Parser;
-
-import java.text.ParseException;
-
-/**
- * <span style="color:red">Experimental API: This class is still under rapid
- * development, and is very likely to be deleted. Use it at your own risk.
- * </span>
- * <p>
- * A no-op renderer, always returns null
- * @param <T> a Record type.
- */
-public class NullParser<T extends EntityProxy> implements Parser<T> {
-
-  private static NullParser INSTANCE;
-
-  /**
-   * @return the instance of the null parser
-   */
-  public static <T extends EntityProxy> Parser<T> instance() {
-    if (INSTANCE == null) {
-      INSTANCE = new NullParser<T>();
-    }
-    return INSTANCE;
-  }
-
-  protected NullParser() {
-  }
-
-  public T parse(CharSequence object) throws ParseException {
-    return null;
-  }
-}
diff --git a/user/src/com/google/gwt/app/place/AbstractProxyEditActivity.java b/user/src/com/google/gwt/app/place/AbstractProxyEditActivity.java
index 6720b18..db9793c 100644
--- a/user/src/com/google/gwt/app/place/AbstractProxyEditActivity.java
+++ b/user/src/com/google/gwt/app/place/AbstractProxyEditActivity.java
@@ -21,9 +21,7 @@
 import com.google.gwt.requestfactory.shared.EntityProxy;
 import com.google.gwt.requestfactory.shared.EntityProxyId;
 import com.google.gwt.requestfactory.shared.Receiver;
-import com.google.gwt.requestfactory.shared.Request;
 import com.google.gwt.requestfactory.shared.RequestContext;
-import com.google.gwt.requestfactory.shared.RequestFactory;
 import com.google.gwt.requestfactory.shared.ServerFailure;
 import com.google.gwt.requestfactory.shared.Violation;
 import com.google.gwt.user.client.Window;
@@ -41,40 +39,18 @@
  * 
  * @param <P> the type of Proxy being edited
  */
-public abstract class AbstractProxyEditActivity<P extends EntityProxy>
-    implements Activity, ProxyEditView.Delegate {
+public abstract class AbstractProxyEditActivity<P extends EntityProxy> implements Activity,
+    ProxyEditView.Delegate {
 
-  private final boolean creating;
-  private final PlaceController placeController;
-  private RequestFactoryEditorDriver<P, ?> editorDriver;
   private final ProxyEditView<P, ?> view;
-  private final RequestFactory requests;
-  private AcceptsOneWidget display;
-  private EntityProxyId<P> proxyId;
-  private Class<P> proxyClass;
-  private boolean waiting = false;
+  private final PlaceController placeController;
 
-  /**
-   * Create an activity to edit or create proxy. Must provide either a proxyId
-   * (to be edited) or a proxyClass (to create a new proxy). If proxyId is
-   * provided, proxyClass will be ignored.
-   */
-  protected AbstractProxyEditActivity(EntityProxyId<P> proxyId,
-      Class<P> proxyClass, RequestFactory requests,
-      PlaceController placeController, ProxyEditView<P, ?> view) {
+  private RequestFactoryEditorDriver<P, ?> editorDriver;
+  private boolean waiting;
 
-    if (proxyId == null && proxyClass == null) {
-      throw new IllegalArgumentException(
-          "Must provide either proxyId or proxyClass");
-    }
-
-    this.creating = proxyId == null;
-    this.proxyClass = proxyClass;
-    this.proxyId = proxyId;
-    this.placeController = placeController;
-    this.requests = requests;
+  public AbstractProxyEditActivity(ProxyEditView<P, ?> view, PlaceController placeController) {
     this.view = view;
-    editorDriver = view.createEditorDriver(null, requests);
+    this.placeController = placeController;
   }
 
   public void cancelClicked() {
@@ -86,18 +62,6 @@
     }
   }
 
-  public EntityProxyId<P> getEntityProxyId() {
-    return proxyId;
-  }
-
-  public ProxyEditView<P, ?> getView() {
-    return view;
-  }
-
-  public boolean isCreating() {
-    return creating;
-  }
-
   public String mayStop() {
     if (isWaiting()
         || (editorDriver != null && editorDriver.flush().isChanged())) {
@@ -112,23 +76,19 @@
   }
 
   public void onStop() {
-    this.display = null;
+    editorDriver = null;
   }
 
   public void saveClicked() {
-    assert editorDriver != null;
-    RequestContext request = editorDriver.flush();
-    if (!request.isChanged()) {
-      return;
-    }
-
     setWaiting(true);
-    request.fire(new Receiver<Void>() {
-      // Do nothing if display is null, we were stopped in midflight
-
+    editorDriver.flush().fire(new Receiver<Void>() {
+      /*
+       * Callbacks do nothing if editorDriver is null, we were stopped in
+       * midflight
+       */
       @Override
       public void onFailure(ServerFailure error) {
-        if (display != null) {
+        if (editorDriver != null) {
           setWaiting(false);
           super.onFailure(error);
         }
@@ -136,13 +96,13 @@
 
       @Override
       public void onSuccess(Void ignore) {
-        if (display != null) {
+        if (editorDriver != null) {
           // We want no warnings from mayStop, so:
-          
-          // Defeats isChanged check
+
+          // Defeat isChanged check
           editorDriver = null;
-          
-          // Defeats call-in-flight check
+
+          // Defeat call-in-flight check
           setWaiting(false);
 
           exit(true);
@@ -151,7 +111,7 @@
 
       @Override
       public void onViolation(Set<Violation> errors) {
-        if (display != null) {
+        if (editorDriver != null) {
           setWaiting(false);
           editorDriver.setViolations(errors);
         }
@@ -159,71 +119,43 @@
     });
   }
 
-  public void start(AcceptsOneWidget startDisplay, EventBus eventBus) {
-
-    this.display = startDisplay;
-
+  public void start(AcceptsOneWidget display, EventBus eventBus) {
+    editorDriver = view.createEditorDriver();
     view.setDelegate(this);
-    view.setCreating(isCreating());
-    /*
-     * Lock ourselves until we actually have a proxy to edit
-     */
-    setWaiting(true);
-
-    final RequestContext context = getPersistRequestContext();
-    if (isCreating()) {
-      P newRecord = context.create(proxyClass);
-      proxyId = getProxyId(newRecord);
-      doStart(context, newRecord);
-    } else {
-      Request<P> findRequest = requests.find(getEntityProxyId());
-      findRequest.with(editorDriver.getPaths()).fire(new Receiver<P>() {
-        @Override
-        public void onSuccess(P proxy) {
-          if (display != null) {
-            doStart(context, proxy);
-          }
-        }
-      });
-    }
+    editorDriver.edit(getProxy(), createSaveRequest(getProxy()));
+    display.setWidget(view);
   }
 
   /**
+   * Called once to create the appropriate request to save
+   * changes.
+   * 
+   * @return the request context to fire when the save button is clicked
+   */
+  protected abstract RequestContext createSaveRequest(P proxy);
+
+  /**
    * Called when the user cancels or has successfully saved. This default
    * implementation tells the {@link PlaceController} to show the details of the
-   * edited record, or clears the display if a creation was canceled.
-   * <p>
-   * Call {@link getId()} for the id of the proxy that has been edited or
-   * created.
+   * edited record.
    * 
-   * @param saved true if changes were comitted
+   * @param saved true if changes were comitted, false if user canceled
    */
-  protected void exit(boolean saved) {
-    if (!saved && isCreating()) {
-      display.setWidget(null);
-    } else {
-      placeController.goTo(new ProxyPlace(proxyId, Operation.DETAILS));
-    }
+  protected void exit(@SuppressWarnings("unused") boolean saved) {
+    placeController.goTo(new ProxyPlace(getProxyId(), Operation.DETAILS));
   }
 
-  protected abstract RequestContext getPersistRequestContext();
-
-  private void doStart(RequestContext request, P proxy) {
-    setWaiting(false);
-
-    editorDriver.edit(proxy, request);
-
-    display.setWidget(view);
-  }
+  /**
+   * Get the proxy to be edited. Must be mutable, typically via a call to
+   * {@link RequestContext#edit(EntityProxy)}, or
+   * {@link RequestContext#create(Class)}.
+   */
+  protected abstract P getProxy();
 
   @SuppressWarnings("unchecked")
-  private EntityProxyId<P> getProxyId(P newRecord) {
-    /*
-     * We could make this cast go away if EntityProxy were typed to itself,
-     * EntityProxy<P extends EntityProxy<P>>, but the ripples this causes
-     * throughout the API are very, very unpleasant.
-     */
-    return (EntityProxyId<P>) newRecord.stableId();
+  // id type always matches proxy type
+  protected EntityProxyId<P> getProxyId() {
+    return (EntityProxyId<P>) getProxy().stableId();
   }
 
   /**
diff --git a/user/src/com/google/gwt/app/place/AbstractProxyListActivity.java b/user/src/com/google/gwt/app/place/AbstractProxyListActivity.java
index 33f5d86..deb1db8 100644
--- a/user/src/com/google/gwt/app/place/AbstractProxyListActivity.java
+++ b/user/src/com/google/gwt/app/place/AbstractProxyListActivity.java
@@ -23,7 +23,6 @@
 import com.google.gwt.requestfactory.shared.EntityProxyId;
 import com.google.gwt.requestfactory.shared.Receiver;
 import com.google.gwt.requestfactory.shared.Request;
-import com.google.gwt.requestfactory.shared.RequestFactory;
 import com.google.gwt.requestfactory.shared.WriteOperation;
 import com.google.gwt.user.cellview.client.AbstractHasData;
 import com.google.gwt.user.client.ui.AcceptsOneWidget;
@@ -69,21 +68,19 @@
   private final Map<EntityProxyId<P>, Integer> idToRow = new HashMap<EntityProxyId<P>, Integer>();
   private final Map<EntityProxyId<P>, P> idToProxy = new HashMap<EntityProxyId<P>, P>();
 
-  private final RequestFactory requests;
   private final PlaceController placeController;
   private final SingleSelectionModel<P> selectionModel;
-  private final Class<P> proxyType;
+  private final Class<P> proxyClass;
 
   private HandlerRegistration rangeChangeHandler;
   private ProxyListView<P> view;
   private AcceptsOneWidget display;
 
-  public AbstractProxyListActivity(RequestFactory requests,
-      PlaceController placeController, ProxyListView<P> view, Class<P> proxyType) {
+  public AbstractProxyListActivity(PlaceController placeController,
+      ProxyListView<P> view, Class<P> proxyType) {
     this.view = view;
-    this.requests = requests;
     this.placeController = placeController;
-    this.proxyType = proxyType;
+    this.proxyClass = proxyType;
     view.setDelegate(this);
 
     final HasData<P> hasData = view.asHasData();
@@ -109,7 +106,7 @@
   }
 
   public void createClicked() {
-    placeController.goTo(new ProxyPlace(null, Operation.CREATE));
+    placeController.goTo(new ProxyPlace(proxyClass));
   }
 
   public ProxyListView<P> getView() {
@@ -188,7 +185,7 @@
   }
 
   public void start(AcceptsOneWidget display, EventBus eventBus) {
-    EntityProxyChange.registerForProxyType(eventBus, proxyType,
+    EntityProxyChange.registerForProxyType(eventBus, proxyClass,
         new EntityProxyChange.Handler<P>() {
           public void onProxyChange(EntityProxyChange<P> event) {
             update(event.getWriteOperation(), event.getProxyId());
@@ -305,7 +302,7 @@
     if (newPlace instanceof ProxyPlace) {
       ProxyPlace proxyPlace = (ProxyPlace) newPlace;
       if (proxyPlace.getOperation() != Operation.CREATE
-          && proxyPlace.getProxyId().getProxyClass().equals(proxyType)) {
+          && proxyPlace.getProxyClass().equals(proxyClass)) {
         select(cast(proxyPlace));
         return;
       }
diff --git a/user/src/com/google/gwt/app/place/AbstractProxyListView.java b/user/src/com/google/gwt/app/place/AbstractProxyListView.java
index e8e2aa7..ea51991 100644
--- a/user/src/com/google/gwt/app/place/AbstractProxyListView.java
+++ b/user/src/com/google/gwt/app/place/AbstractProxyListView.java
@@ -18,26 +18,16 @@
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.requestfactory.shared.EntityProxy;
-import com.google.gwt.user.cellview.client.CellTable;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.Composite;
 import com.google.gwt.user.client.ui.Widget;
 import com.google.gwt.view.client.HasData;
 
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
 /**
  * <p>
  * <span style="color:red">Experimental API: This class is still under rapid
  * development, and is very likely to be deleted. Use it at your own risk.
  * </span>
- * </p>
- * Abstract implementation of ProxyListView. Subclasses must call {@link #init}
- * with the root widget, its {@link CellTable}, and a list of
- * {@link PropertyColumn}.
  * 
  * @param <P> the type of the proxy
  */
@@ -45,7 +35,6 @@
     Composite implements ProxyListView<P> {
 
   private HasData<P> display;
-  private Set<String> paths = new HashSet<String>();
   private Delegate<P> delegate;
 
   public HasData<P> asHasData() {
@@ -57,23 +46,14 @@
     return this;
   }
 
-  public String[] getPaths() {
-    return paths.toArray(new String[paths.size()]);
-  }
-
   public void setDelegate(final Delegate<P> delegate) {
     this.delegate = delegate;
   }
 
-  protected void init(Widget root, HasData<P> display, Button newButton,
-      Set<String> columns) {
+  protected void init(Widget root, HasData<P> display, Button newButton) {
     super.initWidget(root);
     this.display = display;
 
-    if (columns != null && columns.size() > 0) {
-      paths.addAll(columns);
-    }
-
     newButton.addClickHandler(new ClickHandler() {
       public void onClick(ClickEvent event) {
         delegate.createClicked();
@@ -81,25 +61,9 @@
     });
   }
 
-  /**
-   * @deprecated use {@link #init(Widget, HasData, Button, Set)} instead
-   */
-  @Deprecated
-  protected void init(Widget root, CellTable<P> table, Button newButton,
-      List<PropertyColumn<P, ?>> columns) {
-    Set<String> cols = new HashSet<String>(); 
-    for (PropertyColumn<P, ?> column : columns) {
-      table.addColumn(column, column.getDisplayName());
-      cols.addAll(Arrays.asList(column.getPaths()));
-    }
-
-    this.init(root, table, newButton, cols);
-  }
-
-  @Override
   protected void initWidget(Widget widget) {
     throw new UnsupportedOperationException(
         "AbstractRecordListView must be initialized via "
-            + "init(Widget, CellTable<R>, List<PropertyColumn<R, ?>> ) ");
+            + "init(Widget, HasData<P>, Button) ");
   }
 }
diff --git a/user/src/com/google/gwt/app/place/CreateAndEditProxy.java b/user/src/com/google/gwt/app/place/CreateAndEditProxy.java
new file mode 100644
index 0000000..e5d91c7
--- /dev/null
+++ b/user/src/com/google/gwt/app/place/CreateAndEditProxy.java
@@ -0,0 +1,64 @@
+/*
+ * 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.gwt.app.place;
+
+import com.google.gwt.event.shared.EventBus;
+import com.google.gwt.requestfactory.shared.EntityProxy;
+import com.google.gwt.requestfactory.shared.RequestContext;
+import com.google.gwt.user.client.ui.AcceptsOneWidget;
+
+/**
+ * Extends {@link AbstractProxyEditActivity} to first create an instance to edit
+ * 
+ * @param <P> the type of proxy to create and edit
+ */
+public abstract class CreateAndEditProxy<P extends EntityProxy> extends AbstractProxyEditActivity<P> {
+
+  private AcceptsOneWidget display;
+  private final P proxy;
+
+  public CreateAndEditProxy(Class<P> proxyClass, RequestContext request,
+      ProxyEditView<P, ?> view, PlaceController placeController) {
+    super(view, placeController);
+    this.proxy = request.create(proxyClass);
+  }
+
+  @Override
+  public void start(AcceptsOneWidget display, EventBus eventBus) {
+    this.display = display;
+    super.start(display, eventBus);
+  }
+
+  /**
+   * Called when the user cancels or has successfully saved. Refines the default
+   * implementation to clear the display given at {@link #start} on cancel.
+   * 
+   * @param saved true if changes were comitted, false if user canceled
+   */
+  @Override
+  protected void exit(boolean saved) {
+    if (!saved) {
+      display.setWidget(null);
+    }
+
+    super.exit(saved);
+  }
+
+  @Override
+  protected P getProxy() {
+    return proxy;
+  }  
+}
diff --git a/user/src/com/google/gwt/app/place/FindAndEditProxy.java b/user/src/com/google/gwt/app/place/FindAndEditProxy.java
new file mode 100644
index 0000000..e478456
--- /dev/null
+++ b/user/src/com/google/gwt/app/place/FindAndEditProxy.java
@@ -0,0 +1,59 @@
+/*
+ * 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.gwt.app.place;
+
+import com.google.gwt.event.shared.EventBus;
+import com.google.gwt.requestfactory.shared.EntityProxy;
+import com.google.gwt.requestfactory.shared.EntityProxyId;
+import com.google.gwt.requestfactory.shared.Receiver;
+import com.google.gwt.requestfactory.shared.RequestFactory;
+import com.google.gwt.user.client.ui.AcceptsOneWidget;
+
+/**
+ * Extends {@link AbstractProxyEditActivity} to work from a {@link EntityProxyId}
+ * 
+ * @param <P> the type of proxy to find and edit
+ */
+public abstract class FindAndEditProxy<P extends EntityProxy> extends
+    AbstractProxyEditActivity<P> {
+
+  private final RequestFactory factory;
+  private final EntityProxyId<P> proxyId;
+  private P proxy;
+
+  public FindAndEditProxy(EntityProxyId<P> proxyId, RequestFactory factory,
+      ProxyEditView<P, ?> view, PlaceController placeController) {
+    super(view, placeController);
+    this.proxyId = proxyId;
+    this.factory = factory;
+  }
+
+  @Override
+  public void start(final AcceptsOneWidget display, final EventBus eventBus) {
+    factory.find(proxyId).fire(new Receiver<P>() {
+      @Override
+      public void onSuccess(P response) {
+        proxy = response;
+        FindAndEditProxy.super.start(display, eventBus);
+      }
+    });
+  }
+
+  @Override
+  protected P getProxy() {
+    return proxy;
+  }
+}
diff --git a/user/src/com/google/gwt/app/place/PropertyColumn.java b/user/src/com/google/gwt/app/place/PropertyColumn.java
deleted file mode 100644
index 0f3cb39..0000000
--- a/user/src/com/google/gwt/app/place/PropertyColumn.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.gwt.app.place;
-
-import com.google.gwt.requestfactory.shared.EntityProxy;
-import com.google.gwt.text.shared.Renderer;
-import com.google.gwt.user.cellview.client.TextColumn;
-
-/**
- * <p>
- * <span style="color:red">Experimental API: This class is still under rapid
- * development, and is very likely to be deleted. Use it at your own risk.
- * </span>
- * </p>
- * A column that displays a record property as a string. NB: Property objects
- * will soon go away, and this column class will hopefully replaced by a (much
- * simpler to use) code generated system.
- * 
- * @param <R> the type of record in this table
- * @param <T> value type of the property
- */
-public abstract class PropertyColumn<R extends EntityProxy, T> extends
-    TextColumn<R> {
-  private String displayName;
-  private final String[] paths;
-
-  public PropertyColumn(String property, String displayName, Class<T> clazz,
-      ProxyRenderer<T> renderer) {
-    this.displayName = displayName;
-    this.paths = pathinate(property, renderer);
-  }
-
-  public PropertyColumn(String property, String displayName, Class<T> clazz,
-      Renderer<T> renderer) {
-    this.displayName = displayName;
-    this.paths = new String[] {property};
-  }
-
-  public String getDisplayName() {
-    return displayName;
-  }
-
-  public String[] getPaths() {
-    return paths;
-  }
-
-  private String[] pathinate(String property, ProxyRenderer<T> renderer) {
-    String[] rtn = new String[renderer.getPaths().length];
-    int i = 0;
-    for (String rendererPath : renderer.getPaths()) {
-      rtn[i++] = property + "." + rendererPath;
-    }
-
-    return rtn;
-  }
-}
diff --git a/user/src/com/google/gwt/app/place/PropertyView.java b/user/src/com/google/gwt/app/place/PropertyView.java
deleted file mode 100644
index e8842a7..0000000
--- a/user/src/com/google/gwt/app/place/PropertyView.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.gwt.app.place;
-
-import com.google.gwt.requestfactory.shared.EntityProxy;
-
-/**
- * <p>
- * <span style="color:red">Experimental API: This class is still under rapid
- * development, and is very likely to be deleted. Use it at your own risk.
- * </span>
- * </p>
- * A view that displays a set of property values for a type of {@link EntityProxy}.
- * 
- * @param <R> the type of the record
- */
-public interface PropertyView<R extends EntityProxy> {
-
-  /**
-   * @return the set of properties this view displays
-   */
-  String[] getPaths();
-}
diff --git a/user/src/com/google/gwt/app/place/ProxyEditView.java b/user/src/com/google/gwt/app/place/ProxyEditView.java
index be0ca38..987efa5 100644
--- a/user/src/com/google/gwt/app/place/ProxyEditView.java
+++ b/user/src/com/google/gwt/app/place/ProxyEditView.java
@@ -16,10 +16,8 @@
 package com.google.gwt.app.place;
 
 import com.google.gwt.editor.client.HasEditorErrors;
-import com.google.gwt.event.shared.EventBus;
 import com.google.gwt.requestfactory.client.RequestFactoryEditorDriver;
 import com.google.gwt.requestfactory.shared.EntityProxy;
-import com.google.gwt.requestfactory.shared.RequestFactory;
 import com.google.gwt.user.client.ui.IsWidget;
 
 /**
@@ -32,17 +30,16 @@
  * 
  * @param <P> the type of the proxy
  * @param <V> the type of this ProxyEditView, required to allow
- *          {@link #createEditorDriver(EventBus, RequestFactory)} to be
- *          correctly typed
+ *          {@link #createEditorDriver()} to be correctly typed
  */
 public interface ProxyEditView<P extends EntityProxy, V extends ProxyEditView<P, V>>
     extends IsWidget, HasEditorErrors<P> {
 
   /**
-   * @return a {@link RequestFactoryEditorDriver} initialized to run this editor
+   * @return a new {@link RequestFactoryEditorDriver} initialized to run this
+   *         editor
    */
-  RequestFactoryEditorDriver<P, V> createEditorDriver(EventBus eventBus,
-      RequestFactory requestFactory);
+  RequestFactoryEditorDriver<P, V> createEditorDriver();
 
   /**
    * Implemented by the owner of the view.
@@ -53,8 +50,6 @@
     void saveClicked();
   }
 
-  void setCreating(boolean b);
-
   void setDelegate(Delegate delegate);
 
   void setEnabled(boolean b);
diff --git a/user/src/com/google/gwt/app/place/ProxyListView.java b/user/src/com/google/gwt/app/place/ProxyListView.java
index 9e9ba0e..2e0387c 100644
--- a/user/src/com/google/gwt/app/place/ProxyListView.java
+++ b/user/src/com/google/gwt/app/place/ProxyListView.java
@@ -34,7 +34,7 @@
  * 
  * @param <P> the type of the records to display
  */
-public interface ProxyListView<P extends EntityProxy> extends IsWidget, PropertyView<P> {
+public interface ProxyListView<P extends EntityProxy> extends IsWidget {
   /**
    * Implemented by the owner of a RecordTableView.
    * 
@@ -46,6 +46,8 @@
 
   HasData<P> asHasData();
   
+  String[] getPaths();
+  
   /**
    * Sets the delegate.
    */
diff --git a/user/src/com/google/gwt/app/place/ProxyPlace.java b/user/src/com/google/gwt/app/place/ProxyPlace.java
index bdb2fc3..a66ae66 100644
--- a/user/src/com/google/gwt/app/place/ProxyPlace.java
+++ b/user/src/com/google/gwt/app/place/ProxyPlace.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.app.place;
 
+import com.google.gwt.requestfactory.shared.EntityProxy;
 import com.google.gwt.requestfactory.shared.EntityProxyId;
 import com.google.gwt.requestfactory.shared.RequestFactory;
 
@@ -35,6 +36,10 @@
    */
   @Prefix("r")
   public static class Tokenizer implements PlaceTokenizer<ProxyPlace> {
+    /**
+     * 
+     */
+    private static final String SEPARATOR = "!";
     private final RequestFactory requests;
 
     public Tokenizer(RequestFactory requests) {
@@ -42,20 +47,34 @@
     }
 
     public ProxyPlace getPlace(String token) {
-      String bits[] = token.split("!");
-      return new ProxyPlace(requests.getProxyId(bits[0]),
-          Operation.valueOf(bits[1]));
+      String bits[] = token.split(SEPARATOR);
+      Operation operation = Operation.valueOf(bits[1]);
+      if (Operation.CREATE == operation) {
+        return new ProxyPlace(requests.getProxyClass(bits[0]));
+      }
+      return new ProxyPlace(requests.getProxyId(bits[0]), operation);
     }
 
     public String getToken(ProxyPlace place) {
-      return requests.getHistoryToken(place.getProxyId()) + "!" + place.getOperation();
+      if (Operation.CREATE == place.getOperation()) {
+        return requests.getHistoryToken(place.getProxyClass()) + SEPARATOR
+            + place.getOperation();
+      }
+      return requests.getHistoryToken(place.getProxyId()) + SEPARATOR
+          + place.getOperation();
     }
   }
 
   private final EntityProxyId<?> proxyId;
-
+  private final Class<? extends EntityProxy> proxyClass;
   private final Operation operation;
 
+  public ProxyPlace(Class<? extends EntityProxy> proxyClass) {
+    this.operation = Operation.CREATE;
+    this.proxyId = null;
+    this.proxyClass = proxyClass;
+  }
+
   public ProxyPlace(EntityProxyId<?> record) {
     this(record, Operation.DETAILS);
   }
@@ -63,6 +82,8 @@
   public ProxyPlace(EntityProxyId<?> proxyId, Operation operation) {
     this.operation = operation;
     this.proxyId = proxyId;
+    this.proxyClass = null;
+    assert Operation.CREATE != operation;
   }
 
   @Override
@@ -77,10 +98,21 @@
       return false;
     }
     ProxyPlace other = (ProxyPlace) obj;
-    if (!operation.equals(other.operation)) {
+    if (operation != other.operation) {
       return false;
     }
-    if (!proxyId.equals(other.proxyId)) {
+    if (proxyClass == null) {
+      if (other.proxyClass != null) {
+        return false;
+      }
+    } else if (!proxyClass.equals(other.proxyClass)) {
+      return false;
+    }
+    if (proxyId == null) {
+      if (other.proxyId != null) {
+        return false;
+      }
+    } else if (!proxyId.equals(other.proxyId)) {
       return false;
     }
     return true;
@@ -90,6 +122,13 @@
     return operation;
   }
 
+  public Class<? extends EntityProxy> getProxyClass() {
+    return proxyId != null ? proxyId.getProxyClass() : proxyClass;
+  }
+
+  /**
+   * @return the proxyId, or null if the operation is {@link Operation#CREATE}
+   */
   public EntityProxyId<?> getProxyId() {
     return proxyId;
   }
@@ -98,13 +137,16 @@
   public int hashCode() {
     final int prime = 31;
     int result = 1;
-    result = prime * result + operation.hashCode();
-    result = prime * result + proxyId.hashCode();
+    result = prime * result + ((operation == null) ? 0 : operation.hashCode());
+    result = prime * result
+        + ((proxyClass == null) ? 0 : proxyClass.hashCode());
+    result = prime * result + ((proxyId == null) ? 0 : proxyId.hashCode());
     return result;
   }
 
   @Override
   public String toString() {
-    return "ProxyPlace [operation=" + operation + ", proxy=" + proxyId + "]";
+    return "ProxyPlace [operation=" + operation + ", proxy=" + proxyId
+        + ", proxyClass=" + proxyClass + "]";
   }
 }
diff --git a/user/src/com/google/gwt/app/place/ProxyPlaceToListPlace.java b/user/src/com/google/gwt/app/place/ProxyPlaceToListPlace.java
index b6f6dd6..038c321 100644
--- a/user/src/com/google/gwt/app/place/ProxyPlaceToListPlace.java
+++ b/user/src/com/google/gwt/app/place/ProxyPlaceToListPlace.java
@@ -43,6 +43,6 @@
     }
 
     ProxyPlace proxyPlace = (ProxyPlace) place;
-    return new ProxyListPlace(proxyPlace.getProxyId().getProxyClass());
+    return new ProxyListPlace(proxyPlace.getProxyClass());
   }
 }
diff --git a/user/src/com/google/gwt/requestfactory/client/RequestFactoryEditorDriver.java b/user/src/com/google/gwt/requestfactory/client/RequestFactoryEditorDriver.java
index e84772f..78341d8 100644
--- a/user/src/com/google/gwt/requestfactory/client/RequestFactoryEditorDriver.java
+++ b/user/src/com/google/gwt/requestfactory/client/RequestFactoryEditorDriver.java
@@ -25,11 +25,6 @@
 import java.util.List;
 
 /**
- * <p>
- * <span style="color:red">Experimental API: This class is still under rapid
- * development, and is very likely to be deleted. Use it at your own risk.
- * </span>
- * </p>
  * The interface that links RequestFactory and the Editor framework together.
  * Used for configuration and lifecycle control. Expected that this will be
  * created with
@@ -54,15 +49,16 @@
  */
 public interface RequestFactoryEditorDriver<P, E extends Editor<? super P>> {
   /**
-   * Initialize the Editor and its sub-editors with data for display-only mode.
+   * Start driving the Editor and its sub-editors with data for display-only
+   * mode.
    */
   void display(P proxy);
 
   /**
-   * Initialize the Editor and its sub-editors with data. A
-   * {@link RequestObject} is required to provide context for the changes to the
-   * proxy (see {@link RequestContext#edit()}. Note that this driver will not
-   * fire the request.
+   * Start driving the Editor and its sub-editors with data. A
+   * {@link RequestContext} is required to provide context for the changes to
+   * the proxy (see {@link RequestContext#edit()}. Note that this driver will
+   * not fire the request.
    * 
    * @param proxy the proxy to be edited
    * @param request the request context that will accumulate edits and is
@@ -98,19 +94,29 @@
 
   /**
    * Overload of {@link #initialize(RequestFactory, Editor)} to allow a modified
-   * {@link EventBus} to be used.
+   * {@link EventBus} to be monitored for subscription services.
    * 
+   * @see com.google.gwt.editor.client.EditorDelegate#subscribe
    * @see {@link com.google.gwt.event.shared.ResettableEventBus}
    */
   void initialize(EventBus eventBus, RequestFactory requestFactory, E editor);
 
   /**
-   * This or {@link #initialize(EventBus, RequestFactory, Editor)} must be
-   * called before {@link #edit(Object, RequestContext)}.
+   * Initializes a driver with the editor it will run, and a RequestFactory to
+   * use for subscription services.
+   * 
+   * @see com.google.gwt.editor.client.EditorDelegate#subscribe
    */
   void initialize(RequestFactory requestFactory, E editor);
 
   /**
+   * Initializes a driver that will not be able to support subscriptions. Calls
+   * to {@link com.google.gwt.editor.client.EditorDelegate#subscribe()} will do
+   * nothing.
+   */
+  void initialize(E editor);
+
+  /**
    * Show Violations returned from an attempt to submit a request. The
    * violations will be converted into {@link EditorError} objects whose
    * {@link EditorError#getUserData() getUserData()} method can be used to
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequestFactoryEditorDriver.java b/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequestFactoryEditorDriver.java
index bfc5adb..50fd171 100644
--- a/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequestFactoryEditorDriver.java
+++ b/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequestFactoryEditorDriver.java
@@ -90,13 +90,15 @@
     return !errors.isEmpty();
   }
 
+  public void initialize(E editor) {
+    doInitialize(null, null, editor);
+  }
+
   public void initialize(EventBus eventBus, RequestFactory requestFactory,
       E editor) {
-    this.eventBus = eventBus;
-    this.requestFactory = requestFactory;
-    this.editor = editor;
-
-    traverseEditors(paths);
+    assert eventBus != null : "eventBus must not be null";
+    assert requestFactory != null : "requestFactory must not be null";
+    doInitialize(eventBus, requestFactory, editor);
   }
 
   public void initialize(RequestFactory requestFactory, E editor) {
@@ -171,4 +173,13 @@
       throw new IllegalStateException("edit() was called with a null Request");
     }
   }
+
+  private void doInitialize(EventBus eventBus, RequestFactory requestFactory,
+      E editor) {
+    this.eventBus = eventBus;
+    this.requestFactory = requestFactory;
+    this.editor = editor;
+
+    traverseEditors(paths);
+  }
 }
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryEditorDelegate.java b/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryEditorDelegate.java
index 69f09d3..773851e 100644
--- a/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryEditorDelegate.java
+++ b/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryEditorDelegate.java
@@ -85,6 +85,14 @@
 
   @Override
   public HandlerRegistration subscribe() {
+    if (factory == null) {
+      /*
+       * They called the no-subscriptions version of
+       * RequestFactoryEditorDriver#initialize
+       */
+      return null;
+    }
+    
     if (!(getObject() instanceof EntityProxy)) {
       /*
        * This is kind of weird. The user is asking for notifications on a
diff --git a/user/src/com/google/gwt/requestfactory/client/testing/MockRequestFactoryEditorDriver.java b/user/src/com/google/gwt/requestfactory/client/testing/MockRequestFactoryEditorDriver.java
index 482357f..1f57378 100644
--- a/user/src/com/google/gwt/requestfactory/client/testing/MockRequestFactoryEditorDriver.java
+++ b/user/src/com/google/gwt/requestfactory/client/testing/MockRequestFactoryEditorDriver.java
@@ -122,6 +122,10 @@
     return false;
   }
 
+  public void initialize(E editor) {
+    initialize(null, editor);
+  }
+
   /**
    * Records its arguments.
    */
diff --git a/user/src/com/google/gwt/requestfactory/shared/UserInformationRequest.java b/user/src/com/google/gwt/requestfactory/shared/UserInformationRequest.java
index 6afc5c0..4e9e313 100644
--- a/user/src/com/google/gwt/requestfactory/shared/UserInformationRequest.java
+++ b/user/src/com/google/gwt/requestfactory/shared/UserInformationRequest.java
@@ -23,7 +23,7 @@
  * client access to the methods of {@link UserInformation}.
  */
 @Service(UserInformation.class)
-public interface UserInformationRequest {
+public interface UserInformationRequest extends RequestContext {
 
   Request<UserInformationProxy> getCurrentUserInformation(String redirectUrl);
 }
diff --git a/user/test/com/google/gwt/requestfactory/client/EditorTest.java b/user/test/com/google/gwt/requestfactory/client/EditorTest.java
index b438224..2db02d6 100644
--- a/user/test/com/google/gwt/requestfactory/client/EditorTest.java
+++ b/user/test/com/google/gwt/requestfactory/client/EditorTest.java
@@ -106,32 +106,46 @@
     driver.initialize(req, editor);
 
     req.simpleFooRequest().findSimpleFooById(1L).with(driver.getPaths()).fire(
-        new Receiver<SimpleFooProxy>() {
+    new Receiver<SimpleFooProxy>() {
+      @Override
+      public void onSuccess(SimpleFooProxy response) {
+    
+        SimpleFooRequest context = req.simpleFooRequest();
+        driver.edit(response, context);
+        context.persistAndReturnSelf().using(response).with(
+            driver.getPaths()).to(new Receiver<SimpleFooProxy>() {
           @Override
           public void onSuccess(SimpleFooProxy response) {
-
-            SimpleFooRequest context = req.simpleFooRequest();
-            driver.edit(response, context);
-            context.persistAndReturnSelf().using(response).with(
-                driver.getPaths()).to(new Receiver<SimpleFooProxy>() {
-              @Override
-              public void onSuccess(SimpleFooProxy response) {
-                assertEquals("EditorFooTest", response.getUserName());
-                assertEquals("EditorBarTest",
-                    response.getBarField().getUserName());
-                finishTestAndReset();
-              }
-            });
-            assertEquals("GWT", editor.userName.getValue());
-            assertEquals("FOO", editor.barEditor().userName.getValue());
-            assertEquals("FOO", editor.barName.getValue());
-            editor.userName.setValue("EditorFooTest");
-            // When there are duplicate paths, last declared editor wins
-            editor.barEditor().userName.setValue("EditorBarTest");
-            editor.barName.setValue("ignored");
-            driver.flush().fire();
+            assertEquals("EditorFooTest", response.getUserName());
+            assertEquals("EditorBarTest",
+                response.getBarField().getUserName());
+            finishTestAndReset();
           }
         });
+        assertEquals("GWT", editor.userName.getValue());
+        assertEquals("FOO", editor.barEditor().userName.getValue());
+        assertEquals("FOO", editor.barName.getValue());
+        editor.userName.setValue("EditorFooTest");
+        // When there are duplicate paths, last declared editor wins
+        editor.barEditor().userName.setValue("EditorBarTest");
+        editor.barName.setValue("ignored");
+        driver.flush().fire();
+      }
+    });
+  }
+
+  public void testNoSubscription() {
+    final SimpleFooEditorWithDelegate editor = new SimpleFooEditorWithDelegate();
+
+    final SimpleFooDriver driver = GWT.create(SimpleFooDriver.class);
+    driver.initialize(req, editor);
+    
+    /*
+     * Confirm that it's always safe to call subscribe. The editor's delegate
+     * isn't set until edit is called, so edit nothing. 
+     */
+    driver.edit(null, null);
+    assertNull(editor.delegate.subscribe());
   }
 
   public void testSubscription() {
@@ -186,7 +200,7 @@
           }
         });
   }
-
+  
   public void testViolations() {
     delayTestFinish(TEST_TIMEOUT);
     final SimpleFooEditor editor = new SimpleFooEditor();
diff --git a/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java b/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
index 1c9b072..d83dafa 100644
--- a/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
+++ b/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
@@ -42,6 +42,8 @@
  * 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();
    */
@@ -192,7 +194,7 @@
    * Test that we can commit child objects.
    */
   public void testCascadingCommit() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     SimpleFooRequest context = req.simpleFooRequest();
     final SimpleFooProxy foo = context.create(SimpleFooProxy.class);
     final SimpleBarProxy bar0 = context.create(SimpleBarProxy.class);
@@ -220,10 +222,13 @@
     });
   }
 
-  public void testChanged() {
-    // Nothing has happened
+  public void testChangedNothing() {
     SimpleFooRequest context = simpleFooRequest();
     assertFalse(context.isChanged());
+  }
+
+  public void testChangedCreate() {
+    SimpleFooRequest context = simpleFooRequest();
 
     // Creates don't cause a change
     SimpleFooProxy foo = context.create(SimpleFooProxy.class);
@@ -239,7 +244,7 @@
   }
 
   public void testChangedEdit() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     simpleFooRequest().findSimpleFooById(1L).fire(
         new Receiver<SimpleFooProxy>() {
 
@@ -281,7 +286,7 @@
   }
 
   public void testDummyCreate() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     final SimpleFooEventHandler<SimpleFooProxy> handler = new SimpleFooEventHandler<SimpleFooProxy>();
     EntityProxyChange.registerForProxyType(req.getEventBus(),
@@ -310,7 +315,7 @@
   }
 
   public void testDummyCreateBar() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     SimpleBarRequest context = simpleBarRequest();
     final SimpleBarProxy foo = context.create(SimpleBarProxy.class);
@@ -333,7 +338,7 @@
   }
 
   public void testDummyCreateList() {
-    delayTestFinish(500000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     SimpleBarRequest context = simpleBarRequest();
     final SimpleBarProxy bar = context.create(SimpleBarProxy.class);
@@ -358,7 +363,7 @@
 
   public void disabled_testEchoSimpleFutures() {
     // tests if futureIds can be echoed back.
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     final SimpleFooEventHandler<SimpleFooProxy> handler = new SimpleFooEventHandler<SimpleFooProxy>();
     EntityProxyChange.registerForProxyType(req.getEventBus(),
         SimpleFooProxy.class, handler);
@@ -377,7 +382,7 @@
   public void disabled_testEchoComplexFutures() {
     // relate futures on the server. Check if the relationship is still present
     // on the client.
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     final SimpleFooEventHandler<SimpleFooProxy> handler = new SimpleFooEventHandler<SimpleFooProxy>();
     EntityProxyChange.registerForProxyType(req.getEventBus(),
         SimpleFooProxy.class, handler);
@@ -426,7 +431,7 @@
     } catch (IllegalArgumentException expected) {
     }
 
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     contextA.findSimpleFooById(999L).fire(new Receiver<SimpleFooProxy>() {
       @Override
       public void onSuccess(SimpleFooProxy response) {
@@ -438,7 +443,7 @@
   }
 
   public void testFetchEntity() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     simpleFooRequest().findSimpleFooById(999L).fire(
         new Receiver<SimpleFooProxy>() {
           @Override
@@ -455,7 +460,7 @@
   }
 
   public void testFetchEntityWithRelation() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     simpleFooRequest().findSimpleFooById(999L).with("barField").fire(
         new Receiver<SimpleFooProxy>() {
           @Override
@@ -472,7 +477,7 @@
   }
 
   public void testFetchList() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     simpleFooRequest().findAll().fire(new Receiver<List<SimpleFooProxy>>() {
       @Override
       public void onSuccess(List<SimpleFooProxy> responseList) {
@@ -488,7 +493,7 @@
   }
 
   public void testFetchSet() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     simpleBarRequest().findAsSet().fire(new Receiver<Set<SimpleBarProxy>>() {
       @Override
       public void onSuccess(Set<SimpleBarProxy> response) {
@@ -503,7 +508,7 @@
   }
 
   public void testGetListLongId() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     // Long ids
     simpleFooRequest().findAll().with("barField.userName").fire(
@@ -521,7 +526,7 @@
   }
 
   public void testGetListStringId() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     // String ids
     simpleBarRequest().findAll().fire(new Receiver<List<SimpleBarProxy>>() {
@@ -538,7 +543,7 @@
   }
 
   public void testHistoryToken() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     SimpleBarRequest context = simpleBarRequest();
     final SimpleBarProxy foo = context.create(SimpleBarProxy.class);
     final EntityProxyId<SimpleBarProxy> futureId = foo.stableId();
@@ -581,7 +586,7 @@
    * Test that a null value can be sent in a request.
    */
   public void testNullListRequest() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     final Request<Void> fooReq = req.simpleFooRequest().receiveNullList(null);
     fooReq.fire(new Receiver<Void>() {
       @Override
@@ -595,7 +600,7 @@
    * Test that a null value can be sent in a request.
    */
   public void testNullSimpleFooRequest() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     final Request<Void> fooReq = req.simpleFooRequest().receiveNullSimpleFoo(
         null);
     fooReq.fire(new Receiver<Void>() {
@@ -610,7 +615,7 @@
    * Test that a null value can be sent to an instance method.
    */
   public void testNullStringInstanceRequest() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     // Get a valid proxy entity.
     req.simpleFooRequest().findSimpleFooById(999L).fire(
@@ -633,7 +638,7 @@
    * Test that a null value can be sent in a request.
    */
   public void testNullStringRequest() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     final Request<Void> fooReq = req.simpleFooRequest().receiveNullString(null);
     fooReq.fire(new Receiver<Void>() {
       @Override
@@ -647,7 +652,7 @@
    * Test that a null value can be sent within a list of entities.
    */
   public void testNullValueInEntityListRequest() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     // Get a valid proxy entity.
     req.simpleFooRequest().findSimpleFooById(999L).fire(
@@ -673,7 +678,7 @@
    * Test that a null value can be sent within a list of objects.
    */
   public void testNullValueInIntegerListRequest() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     List<Integer> list = Arrays.asList(new Integer[] {1, 2, null});
     final Request<Void> fooReq = req.simpleFooRequest().receiveNullValueInIntegerList(
         list);
@@ -689,7 +694,7 @@
    * Test that a null value can be sent within a list of strings.
    */
   public void testNullValueInStringListRequest() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     List<String> list = Arrays.asList(new String[] {"nonnull", "null", null});
     final Request<Void> fooReq = req.simpleFooRequest().receiveNullValueInStringList(
         list);
@@ -705,7 +710,7 @@
    * Ensures that a service method can respond with a null value.
    */
   public void testNullListResult() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     simpleFooRequest().returnNullList().fire(new NullReceiver());
   }
 
@@ -728,7 +733,7 @@
    * Ensures that a service method can respond with a null value.
    */
   public void testNullEntityProxyResult() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     simpleFooRequest().returnNullSimpleFoo().fire(new NullReceiver());
   }
 
@@ -736,7 +741,7 @@
    * Ensures that a service method can respond with a null value.
    */
   public void testNullStringResult() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     simpleFooRequest().returnNullString().fire(new NullReceiver());
   }
 
@@ -746,7 +751,7 @@
    * reused after a successful response is received. (Yet?)
    */
   public void testMethodWithSideEffects() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     final SimpleFooEventHandler<SimpleFooProxy> handler = new SimpleFooEventHandler<SimpleFooProxy>();
     EntityProxyChange.registerForProxyType(req.getEventBus(),
@@ -805,7 +810,7 @@
    * TODO(rjrjr): Should cascading deletes be detected?
    */
   public void disableTestMethodWithSideEffectDeleteChild() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     // Persist bar.
     SimpleBarRequest context = req.simpleBarRequest();
@@ -859,8 +864,47 @@
         });
   }
 
+  public void testPersistAllValueTypes() {
+    delayTestFinish(DELAY_TEST_FINISH);
+
+    SimpleFooRequest r = simpleFooRequest();
+    SimpleFooProxy f = r.create(SimpleFooProxy.class);
+
+    f.setUserName("user name");
+    f.setByteField((byte)100);
+    f.setShortField((short)12345);
+    f.setFloatField(1234.56f);
+    f.setDoubleField(1.2345);
+    f.setLongField(1234L);
+    f.setBoolField(false);
+    f.setOtherBoolField(true);
+    f.setCreated(new Date(397387389L));
+
+    r.persistAndReturnSelf().using(f).fire(new Receiver<SimpleFooProxy>() {
+      @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(Float.valueOf(1234.56f), f.getFloatField());
+        assertEquals(Double.valueOf(1.2345), f.getDoubleField());
+        assertEquals(Long.valueOf(1234L), f.getLongField());
+        assertFalse(f.getBoolField());
+        assertTrue(f.getOtherBoolField());
+        assertEquals(new Date(397387389L), f.getCreated());
+
+        finishTestAndReset();
+      }
+    });
+  }
+
+  /*
+   * TODO: all these tests should check the final values. It will be easy when
+   * we have better persistence than the singleton pattern.
+   */
+
   public void testPersistExistingEntityExistingRelation() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     // Retrieve a Bar
     simpleBarRequest().findSimpleBarById("999L").fire(
@@ -917,7 +961,7 @@
    * Find Entity Create Entity2 Relate Entity2 to Entity Persist Entity
    */
   public void testPersistExistingEntityNewRelation() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     // Make a new bar
     SimpleBarRequest context = simpleBarRequest();
     SimpleBarProxy makeABar = context.create(SimpleBarProxy.class);
@@ -971,7 +1015,7 @@
    * Entity
    */
   public void testPersistNewEntityExistingRelation() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     SimpleFooRequest context = simpleFooRequest();
     SimpleFooProxy newFoo = context.edit(context.create(SimpleFooProxy.class));
@@ -1008,7 +1052,7 @@
    * Ensure that a relationship can be set up between two newly-created objects.
    */
   public void testPersistFutureToFuture() {
-    delayTestFinish(500000);
+    delayTestFinish(DELAY_TEST_FINISH);
     SimpleFooRequest context = simpleFooRequest();
     SimpleFooProxy newFoo = context.create(SimpleFooProxy.class);
     final SimpleBarProxy newBar = context.create(SimpleBarProxy.class);
@@ -1033,7 +1077,7 @@
    * to Entity Persist
    */
   public void testPersistNewEntityNewRelation() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     SimpleFooRequest context = simpleFooRequest();
     SimpleFooProxy newFoo = context.create(SimpleFooProxy.class);
 
@@ -1085,7 +1129,7 @@
    * we have better persistence than the singleton pattern.
    */
   public void testPersistOneToManyExistingEntityExistingRelation() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     simpleBarRequest().findSimpleBarById("999L").fire(
         new Receiver<SimpleBarProxy>() {
@@ -1119,7 +1163,7 @@
   }
 
   public void testPersistRecursiveRelation() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     SimpleFooRequest context = simpleFooRequest();
     SimpleFooProxy rayFoo = context.create(SimpleFooProxy.class);
@@ -1142,7 +1186,7 @@
    */
 
   public void testPersistRelation() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     SimpleFooRequest context = simpleFooRequest();
     SimpleFooProxy rayFoo = context.create(SimpleFooProxy.class);
@@ -1190,7 +1234,7 @@
    */
 
   public void testPersistSelfOneToManyExistingEntityExistingRelation() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     simpleFooRequest().findSimpleFooById(999L).with("selfOneToManyField").fire(
         new Receiver<SimpleFooProxy>() {
@@ -1217,7 +1261,7 @@
   }
 
   public void testPersistValueList() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     simpleFooRequest().findSimpleFooById(999L).fire(
         new Receiver<SimpleFooProxy>() {
           @Override
@@ -1243,7 +1287,7 @@
    * we have better persistence than the singleton pattern.
    */
   public void testPersistValueListNull() {
-    delayTestFinish(500000);
+    delayTestFinish(DELAY_TEST_FINISH);
     simpleFooRequest().findSimpleFooById(999L).fire(
         new Receiver<SimpleFooProxy>() {
           @Override
@@ -1271,7 +1315,7 @@
    * we have better persistence than the singleton pattern.
    */
   public void testPersistValueListRemove() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     simpleFooRequest().findSimpleFooById(999L).fire(
         new Receiver<SimpleFooProxy>() {
           @Override
@@ -1297,7 +1341,7 @@
    * we have better persistence than the singleton pattern.
    */
   public void testPersistValueListReplace() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     simpleFooRequest().findSimpleFooById(999L).fire(
         new Receiver<SimpleFooProxy>() {
           @Override
@@ -1330,7 +1374,7 @@
    * we have better persistence than the singleton pattern.
    */
   public void testPersistValueListReverse() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     simpleFooRequest().findSimpleFooById(999L).fire(
         new Receiver<SimpleFooProxy>() {
           @Override
@@ -1360,7 +1404,7 @@
    * we have better persistence than the singleton pattern.
    */
   public void testPersistValueListSetIndex() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     simpleFooRequest().findSimpleFooById(999L).fire(
         new Receiver<SimpleFooProxy>() {
           @Override
@@ -1386,7 +1430,7 @@
    * we have better persistence than the singleton pattern.
    */
   public void testPersistValueSetAddNew() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     SimpleBarRequest context = simpleBarRequest();
     SimpleBarProxy newBar = context.create(SimpleBarProxy.class);
 
@@ -1427,7 +1471,7 @@
    * we have better persistence than the singleton pattern.
    */
   public void testPersistValueSetAlreadyExists() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     simpleBarRequest().findSimpleBarById("1L").fire(
         new Receiver<SimpleBarProxy>() {
@@ -1467,7 +1511,7 @@
    * we have better persistence than the singleton pattern.
    */
   public void testPersistValueSetRemove() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     simpleBarRequest().findSimpleBarById("1L").fire(
         new Receiver<SimpleBarProxy>() {
@@ -1504,7 +1548,7 @@
   }
 
   public void testPrimitiveList() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     final Request<List<Integer>> fooReq = simpleFooRequest().getNumberList();
     fooReq.fire(new Receiver<List<Integer>>() {
       @Override
@@ -1519,7 +1563,7 @@
   }
 
   public void testPrimitiveListAsParameter() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     final Request<SimpleFooProxy> fooReq = simpleFooRequest().findSimpleFooById(
         999L);
     fooReq.fire(new Receiver<SimpleFooProxy>() {
@@ -1539,7 +1583,7 @@
   }
 
   public void testPrimitiveListBooleanAsParameter() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     Request<Boolean> procReq = simpleFooRequest().processBooleanList(
         Arrays.asList(true, false));
@@ -1554,7 +1598,7 @@
   }
 
   public void testPrimitiveListDateAsParameter() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     @SuppressWarnings("deprecation")
     final Date date = new Date(90, 0, 1);
@@ -1570,7 +1614,7 @@
   }
 
   public void testPrimitiveListEnumAsParameter() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     Request<SimpleEnum> procReq = simpleFooRequest().processEnumList(
         Arrays.asList(SimpleEnum.BAR));
@@ -1585,7 +1629,7 @@
   }
 
   public void testPrimitiveParameter() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     simpleFooRequest().add(3, 5).fire(new Receiver<Integer>() {
       @Override
       public void onSuccess(Integer response) {
@@ -1596,7 +1640,7 @@
   }
 
   public void testPrimitiveSet() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     final Request<Set<Integer>> fooReq = simpleFooRequest().getNumberSet();
     fooReq.fire(new Receiver<Set<Integer>>() {
       @Override
@@ -1611,7 +1655,7 @@
   }
 
   public void testPrimitiveString() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     final String testString = "test\"string\'with\nstring\u2060characters\t";
     final Request<String> fooReq = simpleFooRequest().processString(testString);
     fooReq.fire(new Receiver<String>() {
@@ -1624,7 +1668,7 @@
   }
 
   public void testProxyList() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     final Request<SimpleFooProxy> fooReq = simpleFooRequest().findSimpleFooById(
         999L).with("oneToManyField");
     fooReq.fire(new Receiver<SimpleFooProxy>() {
@@ -1637,7 +1681,7 @@
   }
 
   public void testProxyListAsParameter() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     final Request<SimpleFooProxy> fooReq = simpleFooRequest().findSimpleFooById(
         999L).with("selfOneToManyField");
     fooReq.fire(new Receiver<SimpleFooProxy>() {
@@ -1657,7 +1701,7 @@
   }
 
   public void testProxysAsInstanceMethodParams() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     simpleFooRequest().findSimpleFooById(999L).fire(
         new Receiver<SimpleFooProxy>() {
           @Override
@@ -1690,7 +1734,7 @@
   }
 
   public void testServerFailureRuntimeException() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
     SimpleFooRequest context = simpleFooRequest();
     SimpleFooProxy newFoo = context.create(SimpleFooProxy.class);
     final Request<SimpleFooProxy> persistRequest = context.persistAndReturnSelf().using(
@@ -1778,7 +1822,7 @@
   }
 
   public void testStableId() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     SimpleFooRequest context = simpleFooRequest();
     final SimpleFooProxy foo = context.create(SimpleFooProxy.class);
@@ -1826,7 +1870,7 @@
   }
 
   public void testViolationAbsent() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     SimpleFooRequest context = simpleFooRequest();
     SimpleFooProxy newFoo = context.create(SimpleFooProxy.class);
@@ -1844,7 +1888,7 @@
   }
 
   public void testViolationsOnCreate() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     SimpleFooRequest context = simpleFooRequest();
     SimpleFooProxy newFoo = context.create(SimpleFooProxy.class);
@@ -1854,7 +1898,7 @@
   }
 
   public void testViolationsOnCreateVoidReturn() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     SimpleFooRequest context = simpleFooRequest();
     SimpleFooProxy newFoo = context.create(SimpleFooProxy.class);
@@ -1863,7 +1907,7 @@
   }
 
   public void testViolationsOnEdit() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     fooCreationRequest().fire(new Receiver<SimpleFooProxy>() {
       @Override
@@ -1877,7 +1921,7 @@
   }
 
   public void testViolationsOnEditVoidReturn() {
-    delayTestFinish(5000);
+    delayTestFinish(DELAY_TEST_FINISH);
 
     fooCreationRequest().fire(new Receiver<SimpleFooProxy>() {
       @Override