Cleanups for the DynaTableRf sample.
Resolves GWT issue 5413.
Patch by: bobv
Review by: rjrjr
Review at http://gwt-code-reviews.appspot.com/975801
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9065 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/DynaTableRf.gwt.xml b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/DynaTableRf.gwt.xml
index 4dd6e1d..63af732 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/DynaTableRf.gwt.xml
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/DynaTableRf.gwt.xml
@@ -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" />
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/DynaTableRf.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/DynaTableRf.java
index 4f78598..377e8b2 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/DynaTableRf.java
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/DynaTableRf.java
@@ -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.");
+ }
}
}
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/ErrorDialog.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/ErrorDialog.java
new file mode 100644
index 0000000..63f0f8c
--- /dev/null
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/ErrorDialog.java
@@ -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();
+ }
+}
\ No newline at end of file
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/ErrorDialog.ui.xml b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/ErrorDialog.ui.xml
new file mode 100644
index 0000000..5ca904c
--- /dev/null
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/ErrorDialog.ui.xml
@@ -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>
\ No newline at end of file
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/FavoritesManager.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/FavoritesManager.java
index b291835..a2696aa 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/FavoritesManager.java
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/FavoritesManager.java
@@ -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);
}
}
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/PersonEditorWorkflow.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/PersonEditorWorkflow.java
index 6e36f94..81dae75 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/PersonEditorWorkflow.java
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/PersonEditorWorkflow.java
@@ -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();
}
}
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/events/MarkFavoriteEvent.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/events/MarkFavoriteEvent.java
index 0f04762..a63d1f0 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/events/MarkFavoriteEvent.java
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/events/MarkFavoriteEvent.java
@@ -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() {
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/AddressEditor.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/AddressEditor.java
index 3a210a2..f4578a6 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/AddressEditor.java
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/AddressEditor.java
@@ -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));
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/AddressEditor.ui.xml b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/AddressEditor.ui.xml
index 52c2792..992b10c 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/AddressEditor.ui.xml
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/AddressEditor.ui.xml
@@ -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 />
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/DayFilterWidget.ui.xml b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/DayFilterWidget.ui.xml
index f6ec9f9..658a9ab 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/DayFilterWidget.ui.xml
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/DayFilterWidget.ui.xml
@@ -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}" />
+ <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" 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>
+ <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>
\ No newline at end of file
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/FavoritesWidget.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/FavoritesWidget.java
index 62e87d6..cc4b598 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/FavoritesWidget.java
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/FavoritesWidget.java
@@ -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) {
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/MentorSelector.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/MentorSelector.java
index 7925ceb..b48101f 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/MentorSelector.java
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/MentorSelector.java
@@ -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);
}
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/NameLabel.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/NameLabel.java
index d7ebe00..b169461 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/NameLabel.java
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/NameLabel.java
@@ -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();
}
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/SummaryWidget.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/SummaryWidget.java
index 348d2f2..213fd21 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/SummaryWidget.java
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/SummaryWidget.java
@@ -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);
}
}
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/ZipPlusFourBox.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/ZipPlusFourBox.java
new file mode 100644
index 0000000..7712995
--- /dev/null
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/ZipPlusFourBox.java
@@ -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);
+ }
+}
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Address.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Address.java
index b87ee48..c7059bd 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Address.java
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Address.java
@@ -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;
}
}
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Person.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Person.java
index d7eec2d..3c10fd8 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Person.java
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Person.java
@@ -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) {
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Schedule.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Schedule.java
index 6a7cfad..14b6ddb 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Schedule.java
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/domain/Schedule.java
@@ -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 {
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/AddressFuzzer.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/AddressFuzzer.java
index 16bcc4d..25bdb93 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/AddressFuzzer.java
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/AddressFuzzer.java
@@ -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());
}
}
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonSource.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonSource.java
index 08ec81c..19b06e5 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonSource.java
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/PersonSource.java
@@ -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);
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/SchoolCalendarService.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/SchoolCalendarService.java
index 7bcdcd3..5493cd7 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/SchoolCalendarService.java
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/server/SchoolCalendarService.java
@@ -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() {
@@ -77,15 +79,6 @@
}
}
- /**
- * 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;
public void destroy() {
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/AddressProxy.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/AddressProxy.java
index 3f59f78..f96e5c8 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/AddressProxy.java
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/AddressProxy.java
@@ -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();
}
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/DynaTableRequestFactory.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/DynaTableRequestFactory.java
index f7aa832..0a0e38a 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/DynaTableRequestFactory.java
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/shared/DynaTableRequestFactory.java
@@ -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();
}
diff --git a/user/src/com/google/gwt/editor/client/IsEditor.java b/user/src/com/google/gwt/editor/client/IsEditor.java
index 265f936..f787df2 100644
--- a/user/src/com/google/gwt/editor/client/IsEditor.java
+++ b/user/src/com/google/gwt/editor/client/IsEditor.java
@@ -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<ListEditor<Foo, FooEditor>> {
+ * private ListEditor<Foo, FooEditor> controller = ListEditor.of(new FooEditorSource());
+ * public ListEditor<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
diff --git a/user/test/com/google/gwt/editor/client/SimpleBeanEditorTest.java b/user/test/com/google/gwt/editor/client/SimpleBeanEditorTest.java
index b5a9beb..1f15edb 100644
--- a/user/test/com/google/gwt/editor/client/SimpleBeanEditorTest.java
+++ b/user/test/com/google/gwt/editor/client/SimpleBeanEditorTest.java
@@ -42,6 +42,14 @@
}
}
+ 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();
@@ -105,6 +113,18 @@
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()));
}
/**
@@ -434,6 +454,23 @@
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);
person.address = null;