Revision: 9065
Author: [email protected]
Date: Thu Oct 14 12:29:49 2010
Log: Cleanups for the DynaTableRf sample.
Resolves GWT issue 5413.
Patch by: bobv
Review by: rjrjr

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

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

Added:
/trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/ErrorDialog.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/ErrorDialog.ui.xml /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/ZipPlusFourBox.java
Modified:
/trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/DynaTableRf.gwt.xml /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/DynaTableRf.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/FavoritesManager.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/PersonEditorWorkflow.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/events/MarkFavoriteEvent.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/AddressEditor.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/AddressEditor.ui.xml /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/DayFilterWidget.ui.xml /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/FavoritesWidget.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/MentorSelector.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/NameLabel.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/SummaryWidget.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Address.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Person.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Schedule.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/AddressFuzzer.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonSource.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/SchoolCalendarService.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/AddressProxy.java /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/DynaTableRequestFactory.java
 /trunk/user/src/com/google/gwt/editor/client/IsEditor.java
 /trunk/user/test/com/google/gwt/editor/client/SimpleBeanEditorTest.java

=======================================
--- /dev/null
+++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/ErrorDialog.java Thu Oct 14 12:29:49 2010
@@ -0,0 +1,84 @@
+/*
+ * 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.sample.dynatablerf.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.DialogBox;
+import com.google.gwt.user.client.ui.TextArea;
+
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+
+/**
+ * A simple glasspanel popup that terminates interaction with the application.
+ */
+class ErrorDialog {
+  interface Binder extends UiBinder<DialogBox, ErrorDialog> {
+  }
+
+  @UiField
+  DialogBox errorDialog;
+
+  @UiField
+  TextArea errorMessage;
+
+  public ErrorDialog() {
+    GWT.<Binder> create(Binder.class).createAndBindUi(this);
+  }
+
+  /**
+   * @return
+   */
+  public Handler getHandler() {
+    return new Handler() {
+      {
+        setLevel(Level.SEVERE);
+      }
+
+      @Override
+      public void close() {
+      }
+
+      @Override
+      public void flush() {
+      }
+
+      @Override
+      public void publish(LogRecord record) {
+        if (isLoggable(record)) {
+          errorMessage.setText(record.getMessage());
+          errorDialog.center();
+        }
+      }
+    };
+  }
+
+  @UiHandler("dismiss")
+  void onDismiss(ClickEvent event) {
+    errorDialog.hide();
+  }
+
+  @UiHandler("reload")
+  void onReload(ClickEvent event) {
+    Window.Location.reload();
+  }
+}
=======================================
--- /dev/null
+++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/ErrorDialog.ui.xml Thu Oct 14 12:29:49 2010
@@ -0,0 +1,39 @@
+<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui'>
+  <ui:style>
+    .dialog {
+       background: white;
+       border: thin solid black;
+       margin: 2px;
+       overflow: hidden;
+       padding: 5px;
+       -moz-border-radius: 5px;
+       -webkit-border-radius: 5px;
+    }
+
+    .glass {
+       filter: literal('alpha(opacity = 75)');
+       opacity: 0.75;
+       background-color: #000000;
+    }
+
+    .message {
+       height: 400px;
+       width: 400px;
+    }
+  </ui:style>
+  <g:DialogBox ui:field="errorDialog" glassEnabled="true"
+    stylePrimaryName="{style.dialog}" glassStyleName="{style.glass}">
+    <g:caption>
+      <b>An unexpected application error has occurred</b>
+      <br />
+      You may wish to reload the application
+    </g:caption>
+    <g:HTMLPanel>
+      <g:TextArea ui:field="errorMessage" readOnly="true"
+        stylePrimaryName="{style.message}"></g:TextArea>
+      <br />
+      <g:Button ui:field="dismiss">Continue</g:Button>
+      <g:Button ui:field="reload">Reload</g:Button>
+    </g:HTMLPanel>
+  </g:DialogBox>
+</ui:UiBinder>
=======================================
--- /dev/null
+++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/ZipPlusFourBox.java Thu Oct 14 08:15:26 2010
@@ -0,0 +1,71 @@
+/*
+ * 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.sample.dynatablerf.client.widgets;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.regexp.shared.RegExp;
+import com.google.gwt.text.shared.Parser;
+import com.google.gwt.text.shared.Renderer;
+import com.google.gwt.user.client.ui.ValueBox;
+
+import java.io.IOException;
+import java.text.ParseException;
+
+/**
+ * A simple implementation of a US zip code input field.
+ * <p>
+ * Accepted formats are <code>ddddd</code> or <code>ddddd-dddd</code>.
+ */
+public class ZipPlusFourBox extends ValueBox<String> {
+ private static final RegExp PATTERN = RegExp.compile("^\\d{5}(-\\d{4})?$");
+  private static final Renderer<String> RENDERER = new Renderer<String>() {
+    public String render(String object) {
+      if (object == null) {
+        return null;
+      }
+      StringBuilder sb = new StringBuilder(String.valueOf(object));
+      if (sb.length() == 9) {
+        sb.insert(5, '-');
+      }
+      return sb.toString();
+    }
+
+ public void render(String object, Appendable appendable) throws IOException {
+      appendable.append(render(object));
+    }
+  };
+  private static final Parser<String> PARSER = new Parser<String>() {
+    public String parse(CharSequence text) throws ParseException {
+      switch (text.length()) {
+        case 9:
+          text = text.subSequence(0, 5) + "-" + text.subSequence(5, 9);
+          // Fall-though intentional
+        case 5:
+        case 10:
+          if (PATTERN.test(text.toString())) {
+            return text.toString();
+          } else {
+            throw new ParseException("Illegal value", 0);
+          }
+      }
+      throw new ParseException("Illegal length", 0);
+    }
+  };
+
+  public ZipPlusFourBox() {
+    super(Document.get().createTextInputElement(), RENDERER, PARSER);
+  }
+}
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/DynaTableRf.gwt.xml Tue Oct 5 17:59:14 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/DynaTableRf.gwt.xml Thu Oct 14 08:15:26 2010
@@ -22,7 +22,7 @@

   <inherits name='com.google.gwt.logging.Logging'/>
   <set-property name="gwt.logging.enabled" value="TRUE"/>
-  <set-property name="gwt.logging.logLevel" value="INFO"/>
+  <set-property name="gwt.logging.logLevel" value="SEVERE"/>
   <set-property name="gwt.logging.consoleHandler" value="ENABLED" />
<set-property name="gwt.logging.developmentModeHandler" value="ENABLED" />
   <set-property name="gwt.logging.firebugHandler" value="ENABLED" />
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/DynaTableRf.java Fri Oct 1 18:15:55 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/DynaTableRf.java Thu Oct 14 08:15:26 2010
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * 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
@@ -28,7 +28,6 @@
 import com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory;
 import com.google.gwt.uibinder.client.UiBinder;
 import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.ui.RootLayoutPanel;
 import com.google.gwt.user.client.ui.Widget;

@@ -57,10 +56,12 @@
   @UiField(provided = true)
   DayFilterWidget filter;

+  /**
+   * This method sets up the top-level services used by the application.
+   */
   public void onModuleLoad() {
     GWT.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
       public void onUncaughtException(Throwable e) {
-        Window.alert("Error: " + e.getMessage());
         log.log(Level.SEVERE, e.getMessage(), e);
       }
     });
@@ -74,6 +75,7 @@
         return requests.loggingRequest();
       }
     };
+    Logger.getLogger("").addHandler(new ErrorDialog().getHandler());
     Logger.getLogger("").addHandler(
         new RequestFactoryLogHandler(provider, Level.WARNING,
             new ArrayList<String>()));
@@ -86,5 +88,13 @@

     RootLayoutPanel.get().add(
         GWT.<Binder> create(Binder.class).createAndBindUi(this));
+
+    // Fast test to see if the sample is not being run from devmode
+    if (GWT.getHostPageBaseURL().startsWith("file:")) {
+ log.log(Level.SEVERE, "The DynaTableRf sample cannot be run without its"
+          + " server component.  If you are running the sample from a"
+          + " GWT distribution, use the 'ant devmode' target to launch"
+          + " the DTRF server.");
+    }
   }
 }
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/FavoritesManager.java Wed Sep 22 07:33:27 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/FavoritesManager.java Thu Oct 14 08:15:26 2010
@@ -82,14 +82,13 @@
     return favoriteIds.contains(person.stableId());
   }

-  public void setFavorite(PersonProxy person, boolean isFavorite) {
+ public void setFavorite(EntityProxyId<PersonProxy> id, boolean isFavorite) {
     if (isFavorite) {
-      favoriteIds.add(person.stableId());
+      favoriteIds.add(id);
     } else {
-      favoriteIds.remove(person.stableId());
+      favoriteIds.remove(id);
     }

-    eventBus.fireEventFromSource(new MarkFavoriteEvent(person, isFavorite),
-        this);
+ eventBus.fireEventFromSource(new MarkFavoriteEvent(id, isFavorite), this);
   }
 }
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/PersonEditorWorkflow.java Tue Oct 5 11:03:13 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/PersonEditorWorkflow.java Thu Oct 14 08:15:26 2010
@@ -30,8 +30,8 @@
 import com.google.gwt.sample.dynatablerf.client.events.EditPersonEvent;
 import com.google.gwt.sample.dynatablerf.client.widgets.PersonEditor;
 import com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory;
-import com.google.gwt.sample.dynatablerf.shared.PersonProxy;
import com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory.PersonRequest;
+import com.google.gwt.sample.dynatablerf.shared.PersonProxy;
 import com.google.gwt.uibinder.client.UiBinder;
 import com.google.gwt.uibinder.client.UiField;
 import com.google.gwt.uibinder.client.UiHandler;
@@ -99,38 +99,60 @@
     }, KeyUpEvent.getType());
   }

+  /**
+ * Called by the cancel button when it is clicked. This method will just tear
+   * down the UI and clear the state of the workflow.
+   */
   @UiHandler("cancel")
-  void onCancel(@SuppressWarnings("unused") ClickEvent event) {
+  void onCancel(ClickEvent event) {
     dialog.hide();
   }

+  /**
+   * Called by the edit dialog's save button. This method will flush the
+ * contents of the UI into the PersonProxy that is being edited, check for
+   * errors, and send the request to the server.
+   */
   @UiHandler("save")
-  void onSave(@SuppressWarnings("unused") ClickEvent event) {
-    // MOVE TO ACTIVITY END
+  void onSave(ClickEvent event) {
+    // Flush the contents of the UI
     RequestContext context = editorDriver.flush();
+
+    // Check for errors
     if (editorDriver.hasErrors()) {
       dialog.setText("Errors detected locally");
       return;
     }
+
+    // Send the request
     context.fire(new Receiver<Void>() {
       @Override
       public void onSuccess(Void response) {
+        // If everything went as planned, just dismiss the dialog box
         dialog.hide();
       }

       @Override
       public void onViolation(Set<Violation> errors) {
+        // Otherwise, show ConstraintViolations in the UI
         dialog.setText("Errors detected on the server");
         editorDriver.setViolations(errors);
       }
     });
   }

+  /**
+   * Called by the favorite checkbox when its value has been toggled.
+   */
   @UiHandler("favorite")
- void onValueChanged(@SuppressWarnings("unused") ValueChangeEvent<Boolean> event) {
-    manager.setFavorite(person, favorite.getValue());
+  void onValueChanged(ValueChangeEvent<Boolean> event) {
+    manager.setFavorite(person.stableId(), favorite.getValue());
   }

+  /**
+   * Construct and display the UI that will be used to edit the current
+   * PersonProxy, using the given RequestContext to accumulate the edits.
+   */
   private void edit(RequestContext requestContext) {
     editorDriver = GWT.create(Driver.class);
     editorDriver.initialize(requestFactory, personEditor);
@@ -149,19 +171,23 @@
   private void fetchAndEdit() {
     // The request is configured arbitrarily
Request<PersonProxy> fetchRequest = requestFactory.find(person.stableId());
-    // Add the paths that the EditorDelegate computes are necessary
+
+    // Add the paths that the EditorDrives computes
     fetchRequest.with(editorDriver.getPaths());

     // We could do more with the request, but we just fire it
-    fetchRequest.fire(new Receiver<PersonProxy>() {
+    fetchRequest.to(new Receiver<PersonProxy>() {
       @Override
       public void onSuccess(PersonProxy person) {
         PersonEditorWorkflow.this.person = person;
         // Start the edit process
         PersonRequest context = requestFactory.personRequest();
-        context.persist().using(person);
+        // Display the UI
         edit(context);
-      }
-    });
+        // Configure the method invocation to be sent in the context
+        context.persist().using(person);
+        // The context will be fire()'ed from the onSave() method
+      }
+    }).fire();
   }
 }
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/events/MarkFavoriteEvent.java Fri Aug 27 09:23:17 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/events/MarkFavoriteEvent.java Thu Oct 14 08:15:26 2010
@@ -17,6 +17,7 @@

 import com.google.gwt.event.shared.EventHandler;
 import com.google.gwt.event.shared.GwtEvent;
+import com.google.gwt.requestfactory.shared.EntityProxyId;
 import com.google.gwt.sample.dynatablerf.shared.PersonProxy;

 /**
@@ -32,11 +33,11 @@

   public static final Type<Handler> TYPE = new Type<Handler>();

-  private final PersonProxy person;
+  private final EntityProxyId<PersonProxy> id;
   private final boolean isFavorite;

-  public MarkFavoriteEvent(PersonProxy person, boolean isFavorite) {
-    this.person = person;
+ public MarkFavoriteEvent(EntityProxyId<PersonProxy> id, boolean isFavorite) {
+    this.id = id;
     this.isFavorite = isFavorite;
   }

@@ -45,8 +46,8 @@
     return TYPE;
   }

-  public PersonProxy getPerson() {
-    return person;
+  public EntityProxyId<PersonProxy> getId() {
+    return id;
   }

   public boolean isFavorite() {
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/AddressEditor.java Tue Oct 5 17:59:14 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/AddressEditor.java Thu Oct 14 08:15:26 2010
@@ -38,7 +38,7 @@
   @UiField
   ValueBoxEditorDecorator<String> state;
   @UiField
-  ValueBoxEditorDecorator<Integer> zip;
+  ValueBoxEditorDecorator<String> zip;

   public AddressEditor() {
     initWidget(GWT.<Binder> create(Binder.class).createAndBindUi(this));
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/AddressEditor.ui.xml Tue Oct 5 17:59:14 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/AddressEditor.ui.xml Thu Oct 14 08:15:26 2010
@@ -1,4 +1,5 @@
 <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
+    xmlns:dt='urn:import:com.google.gwt.sample.dynatablerf.client.widgets'
     xmlns:g='urn:import:com.google.gwt.user.client.ui'
     xmlns:e='urn:import:com.google.gwt.editor.ui.client'>
   <ui:style src="../common.css">
@@ -34,7 +35,7 @@
       <e:ValueBoxEditorDecorator ui:field="zip"
         stylePrimaryName="{style.editField}">
         <e:valuebox>
-          <g:IntegerBox stylePrimaryName="{style.editField}" />
+          <dt:ZipPlusFourBox stylePrimaryName="{style.editField}" />
         </e:valuebox>
       </e:ValueBoxEditorDecorator>
       <br />
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/DayFilterWidget.ui.xml Fri Aug 27 09:23:17 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/DayFilterWidget.ui.xml Thu Oct 14 08:15:26 2010
@@ -1,5 +1,5 @@
-<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui'
-  xmlns:dt='urn:import:com.google.gwt.sample.dynatablerf.client.widgets'>
+<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
+ xmlns:g='urn:import:com.google.gwt.user.client.ui' xmlns:dt='urn:import:com.google.gwt.sample.dynatablerf.client.widgets'>
   <ui:style>
     .all {
        float: left;
@@ -14,23 +14,22 @@
     }
   </ui:style>
   <g:FlowPanel>
-    <dt:DayCheckBox ui:field="sunday" caption="Sunday" day="0"
-      styleName="{style.cb}" />
-    <dt:DayCheckBox ui:field="monday" caption="Monday" day="0"
-      styleName="{style.cb}" />
-    <dt:DayCheckBox ui:field="tuesday" caption="Tuesday" day="0"
-      styleName="{style.cb}" />
-    <dt:DayCheckBox ui:field="wednesday" caption="Wednesday" day="0"
-      styleName="{style.cb}" />
-    <dt:DayCheckBox ui:field="thursday" caption="Thursday" day="0"
-      styleName="{style.cb}" />
-    <dt:DayCheckBox ui:field="friday" caption="Friday" day="0"
-      styleName="{style.cb}" />
-    <dt:DayCheckBox ui:field="saturday" caption="Saturday" day="0"
-      styleName="{style.cb}" />
-
- <g:Button ui:field="all" enabled="false" styleName="{style.all}">All</g:Button> - <g:Button ui:field="none" enabled="false" styleName="{style.none}">None</g:Button>
-    <g:Label>Not implemented yet</g:Label>
+    <dt:DayCheckBox ui:field="sunday" caption="Sunday"
+      day="0" styleName="{style.cb}" />
+    <dt:DayCheckBox ui:field="monday" caption="Monday"
+      day="1" styleName="{style.cb}" />
+    <dt:DayCheckBox ui:field="tuesday" caption="Tuesday"
+      day="2" styleName="{style.cb}" />
+    <dt:DayCheckBox ui:field="wednesday" caption="Wednesday"
+      day="3" styleName="{style.cb}" />
+    <dt:DayCheckBox ui:field="thursday" caption="Thursday"
+      day="4" styleName="{style.cb}" />
+    <dt:DayCheckBox ui:field="friday" caption="Friday"
+      day="5" styleName="{style.cb}" />
+    <dt:DayCheckBox ui:field="saturday" caption="Saturday"
+      day="6" styleName="{style.cb}" />
+
+    <g:Button ui:field="all" styleName="{style.all}">All</g:Button>
+    <g:Button ui:field="none" styleName="{style.none}">None</g:Button>
   </g:FlowPanel>
 </ui:UiBinder>
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/FavoritesWidget.java Fri Sep 24 12:10:50 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/FavoritesWidget.java Thu Oct 14 08:15:26 2010
@@ -48,6 +48,10 @@
   interface Binder extends UiBinder<Widget, FavoritesWidget> {
   }

+  /**
+   * A driver that accepts a List of PersonProxy objects, controlled by a
+   * ListEditor of PersonProxy instances, displayed using NameLabels.
+   */
   interface Driver extends RequestFactoryEditorDriver<List<PersonProxy>, //
       ListEditor<PersonProxy, NameLabel>> {
   }
@@ -57,7 +61,7 @@
   }

   /**
-   * This is used by a ListEditor.
+   * This is used by the ListEditor to control the state of the UI.
    */
   private class NameLabelSource extends EditorSource<NameLabel> {
     @Override
@@ -86,10 +90,14 @@
   @UiField
   Style style;

+  /**
+ * This list is a facade provided by the ListEditor. Structural modifications + * to this list (e.g. add(), set(), remove()) will trigger UI update events.
+   */
   private final List<PersonProxy> displayedList;
   private final EventBus eventBus;
   private final RequestFactory factory;
-  private FavoritesManager manager;
+  private final FavoritesManager manager;
   private HandlerRegistration subscription;

   public FavoritesWidget(EventBus eventBus, RequestFactory factory,
@@ -111,9 +119,7 @@
     ListEditor<PersonProxy, NameLabel> listEditor = editor;
     driver.initialize(eventBus, factory, listEditor);

-    /*
-     * Notice the backing list is essentially anonymous.
-     */
+    // Notice the backing list is essentially anonymous.
     driver.display(new ArrayList<PersonProxy>());

     // Modifying this list triggers widget creation and destruction
@@ -122,19 +128,16 @@

   @Override
   protected void onLoad() {
+    // Subscribe to notifications from the FavoritesManager
subscription = manager.addMarkFavoriteHandler(new MarkFavoriteEvent.Handler() {
       public void onMarkFavorite(MarkFavoriteEvent event) {
         FavoritesWidget.this.onMarkFavorite(event);
       }
     });

+    // Initialize the UI with the existing list of favorites
     for (EntityProxyId<PersonProxy> id : manager.getFavoriteIds()) {
-      factory.find(id).fire(new Receiver<PersonProxy>() {
-        @Override
-        public void onSuccess(PersonProxy person) {
-          onMarkFavorite(new MarkFavoriteEvent(person, true));
-        }
-      });
+      onMarkFavorite(new MarkFavoriteEvent(id, true));
     }
   }

@@ -143,31 +146,36 @@
     subscription.removeHandler();
   }

-  void onMarkFavorite(MarkFavoriteEvent event) {
-    PersonProxy person = event.getPerson();
-    if (person == null) {
+  private void onMarkFavorite(MarkFavoriteEvent event) {
+    EntityProxyId<PersonProxy> id = event.getId();
+    if (id == null) {
       return;
     }

     // EntityProxies should be compared by stable id
-    EntityProxyId<PersonProxy> lookFor = person.stableId();
     PersonProxy found = null;
     for (PersonProxy displayed : displayedList) {
-      if (displayed.stableId().equals(lookFor)) {
+      if (displayed.stableId().equals(id)) {
         found = displayed;
         break;
       }
     }

     if (event.isFavorite() && found == null) {
-      displayedList.add(person);
+      factory.find(id).to(new Receiver<PersonProxy>() {
+        @Override
+        public void onSuccess(PersonProxy response) {
+          displayedList.add(response);
+          sortDisplayedList();
+        }
+      }).fire();
     } else if (!event.isFavorite() && found != null) {
-      displayedList.remove(person);
-    } else {
-      // No change
-      return;
-    }
-
+      displayedList.remove(found);
+      sortDisplayedList();
+    }
+  }
+
+  private void sortDisplayedList() {
     // Sorting the list of PersonProxies will also change the UI display
     Collections.sort(displayedList, new Comparator<PersonProxy>() {
       public int compare(PersonProxy o1, PersonProxy o2) {
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/MentorSelector.java Tue Oct 5 11:03:13 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/MentorSelector.java Thu Oct 14 08:15:26 2010
@@ -74,7 +74,7 @@
   }

   @UiHandler("choose")
-  void onChoose(@SuppressWarnings("unused") ClickEvent event) {
+  void onChoose(ClickEvent event) {
     setEnabled(false);
     factory.schoolCalendarRequest().getRandomPerson().to(
         new Receiver<PersonProxy>() {
@@ -87,7 +87,7 @@
   }

   @UiHandler("clear")
-  void onClear(@SuppressWarnings("unused") ClickEvent event) {
+  void onClear(ClickEvent event) {
     setValue(null);
   }

=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/NameLabel.java Sun Oct 3 11:35:25 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/NameLabel.java Thu Oct 14 08:15:26 2010
@@ -31,6 +31,10 @@
  * the displayed object.
  */
class NameLabel extends Composite implements ValueAwareEditor<PersonProxy> {
+  /**
+   * Many of the GWT UI widgets that implement TakesValue also implement
+   * IsEditor and are directly usable as sub-Editors.
+   */
   final Label nameEditor = new Label();
   private PersonProxy person;
   private HandlerRegistration subscription;
@@ -59,7 +63,9 @@
   }

   public void setDelegate(EditorDelegate<PersonProxy> delegate) {
-    assert subscription == null;
+    if (subscription != null) {
+      subscription.removeHandler();
+    }
     subscription = delegate.subscribe();
   }

=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/SummaryWidget.java Tue Oct 5 11:03:13 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/SummaryWidget.java Thu Oct 14 08:15:26 2010
@@ -15,8 +15,12 @@
  */
 package com.google.gwt.sample.dynatablerf.client.widgets;

+import static com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory.SchoolCalendarRequest.ALL_DAYS;
+
 import com.google.gwt.cell.client.TextCell;
 import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.core.client.Scheduler.ScheduledCommand;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.shared.EventBus;
 import com.google.gwt.requestfactory.shared.EntityProxyChange;
@@ -28,15 +32,15 @@
 import com.google.gwt.sample.dynatablerf.client.events.FilterChangeEvent;
 import com.google.gwt.sample.dynatablerf.shared.AddressProxy;
 import com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory;
-import com.google.gwt.sample.dynatablerf.shared.PersonProxy;
import com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory.PersonRequest;
+import com.google.gwt.sample.dynatablerf.shared.PersonProxy;
 import com.google.gwt.uibinder.client.UiBinder;
 import com.google.gwt.uibinder.client.UiField;
 import com.google.gwt.uibinder.client.UiHandler;
 import com.google.gwt.user.cellview.client.CellTable;
 import com.google.gwt.user.cellview.client.Column;
-import com.google.gwt.user.cellview.client.SimplePager;
import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
+import com.google.gwt.user.cellview.client.SimplePager;
 import com.google.gwt.user.client.ui.Composite;
 import com.google.gwt.user.client.ui.DockLayoutPanel;
 import com.google.gwt.user.client.ui.Widget;
@@ -45,6 +49,7 @@
 import com.google.gwt.view.client.SelectionChangeEvent;
 import com.google.gwt.view.client.SingleSelectionModel;

+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;

@@ -111,13 +116,13 @@
   CellTable<PersonProxy> table;

   private final EventBus eventBus;
+  private List<Boolean> filter = new ArrayList<Boolean>(ALL_DAYS);
+  private int lastFetch;
   private final int numRows;
+  private boolean pending;
   private final DynaTableRequestFactory requestFactory;
private final SingleSelectionModel<PersonProxy> selectionModel = new SingleSelectionModel<PersonProxy>();

-  private boolean[] filter = new boolean[] {
-      true, true, true, true, true, true, true};
-
   public SummaryWidget(EventBus eventBus,
       DynaTableRequestFactory requestFactory, int numRows) {
     this.eventBus = eventBus;
@@ -148,8 +153,16 @@

     FilterChangeEvent.register(eventBus, new FilterChangeEvent.Handler() {
       public void onFilterChanged(FilterChangeEvent e) {
-        filter[e.getDay()] = e.isSelected();
-        fetch(0);
+        filter.set(e.getDay(), e.isSelected());
+        if (!pending) {
+          pending = true;
+          Scheduler.get().scheduleFinally(new ScheduledCommand() {
+            public void execute() {
+              pending = false;
+              fetch(0);
+            }
+          });
+        }
       }
     });

@@ -162,8 +175,8 @@
     fetch(0);
   }

-  @UiHandler("create")
-  void onCreate(@SuppressWarnings("unused") ClickEvent event) {
+  @UiHandler("create")
+  void onCreate(ClickEvent event) {
     PersonRequest context = requestFactory.personRequest();
     AddressProxy address = context.create(AddressProxy.class);
     PersonProxy person = context.edit(context.create(PersonProxy.class));
@@ -173,6 +186,12 @@
   }

   void onPersonChanged(EntityProxyChange<PersonProxy> event) {
+    if (WriteOperation.PERSIST.equals(event.getWriteOperation())) {
+      // Re-fetch if we're already displaying the last page
+      if (table.isRowCountExact()) {
+        fetch(lastFetch + 1);
+      }
+    }
     if (WriteOperation.UPDATE.equals(event.getWriteOperation())) {
       EntityProxyId<PersonProxy> personId = event.getProxyId();

@@ -213,14 +232,15 @@
   }

   private void fetch(final int start) {
-    // XXX add back filter
-    requestFactory.schoolCalendarRequest().getPeople(start, numRows).fire(
+    lastFetch = start;
+ requestFactory.schoolCalendarRequest().getPeople(start, numRows, filter).fire(
         new Receiver<List<PersonProxy>>() {
           @Override
           public void onSuccess(List<PersonProxy> response) {
             int responses = response.size();
             table.setRowData(start, response);
-            if (!table.isRowCountExact()) {
+            pager.setPageStart(start);
+            if (start == 0 || !table.isRowCountExact()) {
               table.setRowCount(start + responses, responses < numRows);
             }
           }
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Address.java Fri Sep 24 12:10:50 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Address.java Thu Oct 14 08:15:26 2010
@@ -19,6 +19,7 @@

 import javax.validation.constraints.DecimalMin;
 import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
 import javax.validation.constraints.Size;

 /**
@@ -53,8 +54,8 @@
   private Integer version = 0;

   @NotNull
-  @DecimalMin("10000")
-  private Integer zip;
+  @Pattern(regexp = "\\d{5}(-\\d{4})?")
+  private String zip;

   public Address() {
   }
@@ -92,7 +93,7 @@
     return version;
   }

-  public Integer getZip() {
+  public String getZip() {
     return zip;
   }

@@ -129,7 +130,7 @@
     this.version = version;
   }

-  public void setZip(Integer zip) {
+  public void setZip(String zip) {
     this.zip = zip;
   }
 }
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Person.java Sun Oct 3 11:35:25 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Person.java Thu Oct 14 08:15:26 2010
@@ -15,8 +15,12 @@
  */
 package com.google.gwt.sample.dynatablerf.domain;

+import static com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory.SchoolCalendarRequest.ALL_DAYS;
+
 import com.google.gwt.sample.dynatablerf.server.SchoolCalendarService;

+import java.util.List;
+
 import javax.validation.constraints.DecimalMin;
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Size;
@@ -69,8 +73,7 @@

   private String note;

-  private final boolean[] daysFilters = new boolean[] {
-      true, true, true, true, true, true, true};
+  private List<Boolean> daysFilters = ALL_DAYS;

   public Person() {
   }
@@ -129,7 +132,7 @@
     return getScheduleWithFilter(daysFilters);
   }

-  public String getScheduleWithFilter(boolean[] daysFilter) {
+  public String getScheduleWithFilter(List<Boolean> daysFilter) {
     return classSchedule.getDescription(daysFilter);
   }

@@ -158,10 +161,9 @@
     this.address.copyFrom(address);
   }

-  public void setDaysFilter(boolean[] daysFilter) {
-    assert daysFilter.length == this.daysFilters.length;
-    System.arraycopy(daysFilter, 0, this.daysFilters, 0,
-        this.daysFilters.length);
+  public void setDaysFilter(List<Boolean> daysFilter) {
+    assert daysFilter.size() == this.daysFilters.size();
+    this.daysFilters = daysFilter;
   }

   public void setDescription(String description) {
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Schedule.java Fri Jul 23 15:42:40 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Schedule.java Thu Oct 14 08:15:26 2010
@@ -32,10 +32,10 @@
     timeSlots.add(timeSlot);
   }

-  public String getDescription(boolean[] daysFilter) {
+  public String getDescription(List<Boolean> daysFilter) {
     String s = null;
     for (TimeSlot timeSlot : timeSlots) {
-      if (daysFilter[timeSlot.getDayOfWeek()]) {
+      if (daysFilter.get(timeSlot.getDayOfWeek())) {
         if (s == null) {
           s = timeSlot.getDescription();
         } else {
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/AddressFuzzer.java Fri Aug 27 09:23:17 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/AddressFuzzer.java Thu Oct 14 08:15:26 2010
@@ -49,6 +49,11 @@
     a.setStreet(r.nextInt(4096) + " "
         + STREET_NAMES[r.nextInt(STREET_NAMES.length)]);
     a.setState(STATE_NAMES[r.nextInt(STATE_NAMES.length)]);
-    a.setZip(10000 + r.nextInt(89999));
+    StringBuilder zip = new StringBuilder();
+    zip.append(String.format("%05d", r.nextInt(99999)));
+    if (r.nextBoolean()) {
+      zip.append(String.format("-%04d", r.nextInt(9999)));
+    }
+    a.setZip(zip.toString());
   }
 }
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonSource.java Fri Sep 24 12:10:50 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonSource.java Thu Oct 14 08:15:26 2010
@@ -15,17 +15,24 @@
  */
 package com.google.gwt.sample.dynatablerf.server;

+import static com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory.SchoolCalendarRequest.ALL_DAYS; +import static com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory.SchoolCalendarRequest.NO_DAYS;
+
 import com.google.gwt.sample.dynatablerf.domain.Address;
 import com.google.gwt.sample.dynatablerf.domain.Person;

 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;

 /**
- *
+ * Provides a number of Person objects as a demonstration datasource. Many of + * the operations in this implementation would be much more efficient in a real + * database, but are implemented is a straightforward fashion because they're
+ * not really important for understanding the RequestFactory framework.
  */
 public abstract class PersonSource {
   static class Backing extends PersonSource {
@@ -43,7 +50,7 @@

     @Override
     public List<Person> getPeople(int startIndex, int maxCount,
-        boolean[] daysFilter) {
+        List<Boolean> daysFilter) {
       int peopleCount = countPeople();

       int start = startIndex;
@@ -55,8 +62,31 @@
       if (start == end) {
         return Collections.emptyList();
       }
-      // This is ugly, but a real backend would have a skip mechanism
-      return new ArrayList<Person>(people.values()).subList(start, end);
+
+      // If there's a simple filter, use a fast path
+      if (ALL_DAYS.equals(daysFilter)) {
+        return new ArrayList<Person>(people.values()).subList(start, end);
+      } else if (NO_DAYS.equals(daysFilter)) {
+        return new ArrayList<Person>();
+      }
+
+      /*
+       * Otherwise, iterate from the start position until we collect enough
+       * People or hit the end of the list.
+       */
+      Iterator<Person> it = people.values().iterator();
+      int skipped = 0;
+      List<Person> toReturn = new ArrayList<Person>(maxCount);
+      while (toReturn.size() < maxCount && it.hasNext()) {
+        Person person = it.next();
+        if (person.getScheduleWithFilter(daysFilter).length() > 0) {
+          if (skipped++ < startIndex) {
+            continue;
+          }
+          toReturn.add(person);
+        }
+      }
+      return toReturn;
     }

     @Override
@@ -107,13 +137,13 @@

     @Override
     public List<Person> getPeople(int startIndex, int maxCount,
-        boolean[] daysFilter) {
+        List<Boolean> daysFilter) {
       List<Person> toReturn = new ArrayList<Person>(maxCount);
       for (Person person : backingStore.getPeople(startIndex, maxCount,
           daysFilter)) {
         Person copy = findPerson(person.getId());
-        toReturn.add(copy);
         copy.setDaysFilter(daysFilter);
+        toReturn.add(copy);
       }
       return toReturn;
     }
@@ -155,7 +185,7 @@
   public abstract Person findPerson(String id);

   public abstract List<Person> getPeople(int startIndex, int maxCount,
-      boolean[] daysFilter);
+      List<Boolean> daysFilter);

   public abstract void persist(Address address);

=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/SchoolCalendarService.java Sun Oct 3 11:35:25 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/SchoolCalendarService.java Thu Oct 14 08:15:26 2010
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.sample.dynatablerf.server;

+import static com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory.SchoolCalendarRequest.ALL_DAYS;
+
 import com.google.gwt.sample.dynatablerf.domain.Address;
 import com.google.gwt.sample.dynatablerf.domain.Person;

@@ -33,8 +35,6 @@
  * The server side service class.
  */
 public class SchoolCalendarService implements Filter {
-  private static final boolean[] ALL_DAYS = new boolean[] {
-      true, true, true, true, true, true, true};

private static final ThreadLocal<PersonSource> PERSON_SOURCE = new ThreadLocal<PersonSource>();

@@ -50,8 +50,10 @@
     return PERSON_SOURCE.get().findPerson(id);
   }

-  public static List<Person> getPeople(int startIndex, int maxCount) {
-    return getPeople(startIndex, maxCount, ALL_DAYS);
+  public static List<Person> getPeople(int startIndex, int maxCount,
+      List<Boolean> filter) {
+    checkPersonSource();
+    return PERSON_SOURCE.get().getPeople(startIndex, maxCount, filter);
   }

   public static Person getRandomPerson() {
@@ -76,15 +78,6 @@
           "Calling service method outside of HTTP request");
     }
   }
-
-  /**
-   * XXX private due to inability to add method overloads.
-   */
-  private static List<Person> getPeople(int startIndex, int maxCount,
-      boolean[] filter) {
-    checkPersonSource();
-    return PERSON_SOURCE.get().getPeople(startIndex, maxCount, filter);
-  }

   private PersonSource backingStore;

=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/AddressProxy.java Fri Oct 8 13:01:19 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/AddressProxy.java Thu Oct 14 08:15:26 2010
@@ -31,7 +31,7 @@

   String getStreet();

-  Integer getZip();
+  String getZip();

   void setCity(String city);

@@ -39,7 +39,7 @@

   void setStreet(String street);

-  void setZip(Integer zip);
+  void setZip(String zip);

   EntityProxyId<AddressProxy> stableId();
 }
=======================================
--- /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/DynaTableRequestFactory.java Sun Oct 3 11:35:25 2010 +++ /trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/DynaTableRequestFactory.java Thu Oct 14 08:15:26 2010
@@ -25,6 +25,8 @@
 import com.google.gwt.sample.dynatablerf.domain.Person;
 import com.google.gwt.sample.dynatablerf.server.SchoolCalendarService;

+import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;

 /**
@@ -54,7 +56,14 @@
    */
   @Service(SchoolCalendarService.class)
   interface SchoolCalendarRequest extends RequestContext {
-    Request<List<PersonProxy>> getPeople(int startIndex, int maxCount);
+ List<Boolean> ALL_DAYS = Collections.unmodifiableList(Arrays.asList(true,
+        true, true, true, true, true, true));
+ List<Boolean> NO_DAYS = Collections.unmodifiableList(Arrays.asList(false,
+        false, false, false, false, false, false));
+
+    Request<List<PersonProxy>> getPeople(int startIndex, int maxCount,
+        List<Boolean> dayFilter);
+
     Request<PersonProxy> getRandomPerson();
   }

=======================================
--- /trunk/user/src/com/google/gwt/editor/client/IsEditor.java Fri Sep 10 12:29:13 2010 +++ /trunk/user/src/com/google/gwt/editor/client/IsEditor.java Thu Oct 14 08:15:26 2010
@@ -17,7 +17,28 @@

 /**
* Extended by view objects that wish to participate in an Editor hierarchy, but
- * that do not implement the {...@link Editor} contract directly.
+ * that do not implement the {...@link Editor} contract directly. The primary
+ * advantage of the IsEditor interface is that is allows composition of behavior
+ * without the need to implement delegate methods for every interface
+ * implemented by the common editor logic.
+ * <p>
+ * For example, an editor Widget that supports adding and removing elements from
+ * a list might wish to re-use the provided
+ * {...@link com.google.gwt.editor.client.adapters.ListEditor ListEditor}
+ * controller. It might be roughly built as:
+ *
+ * <pre>
+ * class MyListEditor extends Composite implements IsEditor&lt;ListEditor&lt;Foo, FooEditor>> { + * private ListEditor&lt;Foo, FooEditor> controller = ListEditor.of(new FooEditorSource());
+ *   public ListEditor&lt;Foo, FooEditor> asEditor() {return controller;}
+ *   void onAddButtonClicked() { controller.getList().add(new Foo()); }
+ *   void onClearButtonClicked() { controller.getList().clear(); }
+ * }
+ * </pre>
+ * By implementing only the one <code>asEditor()</code> method, the
+ * <code>MyListEditor</code> type is able to incorporate the
+ * <code>ListEditor</code> behavior without needing to write delegate methods
+ * for every method in <code>ListEditor</code>.
  * <p>
* It is legal for a type to implement both Editor and IsEditor. In this case,
  * the Editor returned from {...@link #asEditor()} will be a co-Editor of the
=======================================
--- /trunk/user/test/com/google/gwt/editor/client/SimpleBeanEditorTest.java Sun Oct 3 11:35:25 2010 +++ /trunk/user/test/com/google/gwt/editor/client/SimpleBeanEditorTest.java Thu Oct 14 08:15:26 2010
@@ -41,6 +41,14 @@
       return addressEditor;
     }
   }
+
+  class AddressEditorPartOne implements Editor<Address> {
+    SimpleEditor<String> city = SimpleEditor.of(UNINITIALIZED);
+  }
+
+  class AddressEditorPartTwo implements Editor<Address> {
+    SimpleEditor<String> street = SimpleEditor.of(UNINITIALIZED);
+  }

   class AddressEditorView implements IsEditor<LeafAddressEditor> {
     LeafAddressEditor addressEditor = new LeafAddressEditor();
@@ -104,6 +112,18 @@
   interface PersonEditorWithLeafAddressEditorDriver extends
       SimpleBeanEditorDriver<Person, PersonEditorWithLeafAddressEditor> {
   }
+
+  class PersonEditorWithMultipleBindings implements Editor<Person> {
+    @Editor.Path("address")
+    AddressEditorPartOne one = new AddressEditorPartOne();
+
+    @Editor.Path("address")
+    AddressEditorPartTwo two = new AddressEditorPartTwo();
+  }
+
+  interface PersonEditorWithMultipleBindingsDriver extends
+      SimpleBeanEditorDriver<Person, PersonEditorWithMultipleBindings> {
+  }

   interface PersonEditorWithOptionalAddressDriver extends
SimpleBeanEditorDriver<Person, PersonEditorWithOptionalAddressEditor> {
@@ -361,10 +381,10 @@

     List<SimpleEditor<String>> editors = editor.getEditors();
     assertEquals(rawData.size(), editors.size());
- assertEquals(rawData, Arrays.asList(editors.get(0).getValue(), editors.get(
-        1).getValue(), editors.get(2).getValue()));
-    assertEquals(editors, new ArrayList<SimpleEditor<String>>(
-        positionMap.values()));
+    assertEquals(rawData, Arrays.asList(editors.get(0).getValue(),
+        editors.get(1).getValue(), editors.get(2).getValue()));
+    assertEquals(editors,
+        new ArrayList<SimpleEditor<String>>(positionMap.values()));

     List<String> mutableList = editor.getList();
     assertEquals(rawData, mutableList);
@@ -383,8 +403,8 @@
     mutableList.add("quux");
     assertEquals(4, editors.size());
     assertEquals("quux", editors.get(3).getValue());
-    assertEquals(editors, new ArrayList<SimpleEditor<String>>(
-        positionMap.values()));
+    assertEquals(editors,
+        new ArrayList<SimpleEditor<String>>(positionMap.values()));

     // Delete an element
     SimpleEditor<String> expectedDisposed = editors.get(0);
@@ -392,8 +412,8 @@
     assertSame(expectedDisposed, disposed[0]);
     assertEquals(3, editors.size());
     assertEquals("quux", editors.get(2).getValue());
-    assertEquals(editors, new ArrayList<SimpleEditor<String>>(
-        positionMap.values()));
+    assertEquals(editors,
+        new ArrayList<SimpleEditor<String>>(positionMap.values()));
   }

   /**
@@ -433,6 +453,23 @@
     driver.flush();
     assertEquals("edited", person.addresses.get(1).getCity());
   }
+
+  public void testMultipleBinding() {
+ PersonEditorWithMultipleBindingsDriver driver = GWT.create(PersonEditorWithMultipleBindingsDriver.class); + PersonEditorWithMultipleBindings editor = new PersonEditorWithMultipleBindings();
+
+    driver.initialize(editor);
+    driver.edit(person);
+    assertEquals("City", editor.one.city.getValue());
+    assertEquals("Street", editor.two.street.getValue());
+
+    editor.one.city.setValue("Foo");
+    editor.two.street.setValue("Bar");
+    driver.flush();
+
+    assertEquals("Foo", person.getAddress().getCity());
+    assertEquals("Bar", person.getAddress().getStreet());
+  }

   public void testOptionalField() {
PersonEditorWithOptionalAddressDriver driver = GWT.create(PersonEditorWithOptionalAddressDriver.class);

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

Reply via email to