Revision: 8810
Author: [email protected]
Date: Fri Sep 17 07:40:00 2010
Log: Make DynaTableRf use a ListEditor for the favorites.
Fix potential NPE's in AED.Chain.
Widen RequestFactoryEditorDriver's type bound to allow it to drive more
than just EntityProxy types.
Patch by: bobv
Review by: rjrjr
Review at http://gwt-code-reviews.appspot.com/888801
http://code.google.com/p/google-web-toolkit/source/detail?r=8810
Modified:
/trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/FavoritesWidget.java
/trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/NameLabel.java
/trunk/user/src/com/google/gwt/editor/client/impl/AbstractEditorDelegate.java
/trunk/user/src/com/google/gwt/requestfactory/client/RequestFactoryEditorDriver.java
/trunk/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequestFactoryEditorDriver.java
/trunk/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryEditorDelegate.java
/trunk/user/src/com/google/gwt/requestfactory/client/testing/MockRequestFactoryEditorDriver.java
/trunk/user/src/com/google/gwt/user/client/ui/Label.java
=======================================
---
/trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/FavoritesWidget.java
Wed Sep 15 06:48:28 2010
+++
/trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/FavoritesWidget.java
Fri Sep 17 07:40:00 2010
@@ -16,6 +16,8 @@
package com.google.gwt.sample.dynatablerf.client.widgets;
import com.google.gwt.core.client.GWT;
+import com.google.gwt.editor.client.adapters.EditorSource;
+import com.google.gwt.editor.client.adapters.ListEditor;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.requestfactory.client.RequestFactoryEditorDriver;
@@ -34,8 +36,10 @@
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Widget;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
import java.util.Set;
/**
@@ -47,22 +51,48 @@
interface Binder extends UiBinder<Widget, FavoritesWidget> {
}
- interface Driver extends RequestFactoryEditorDriver<PersonProxy,
NameLabel> {
+ interface Driver extends RequestFactoryEditorDriver<List<PersonProxy>, //
+ ListEditor<PersonProxy, NameLabel>> {
}
interface Style extends CssResource {
String favorite();
}
+
+ /**
+ * This is used by a ListEditor.
+ */
+ private class NameLabelSource extends EditorSource<NameLabel> {
+ @Override
+ public NameLabel create(int index) {
+ NameLabel label = new NameLabel(eventBus);
+ label.setStylePrimaryName(style.favorite());
+ container.insert(label, index);
+ return label;
+ }
+
+ @Override
+ public void dispose(NameLabel subEditor) {
+ subEditor.removeFromParent();
+ subEditor.cancelSubscription();
+ }
+
+ @Override
+ public void setIndex(NameLabel editor, int index) {
+ container.insert(editor, index);
+ }
+ }
@UiField
FlowPanel container;
@UiField
Style style;
+
+ private final List<PersonProxy> displayed;
private final EventBus eventBus;
private final RequestFactory factory;
private FavoritesManager manager;
- private final Map<EntityProxyId, NameLabel> map = new
HashMap<EntityProxyId, NameLabel>();
private HandlerRegistration subscription;
public FavoritesWidget(EventBus eventBus, RequestFactory factory,
@@ -70,7 +100,27 @@
this.eventBus = eventBus;
this.factory = factory;
this.manager = manager;
+
+ // Create the UI
initWidget(GWT.<Binder> create(Binder.class).createAndBindUi(this));
+
+ // Create the driver which manages the data-bound widgets
+ Driver driver = GWT.<Driver> create(Driver.class);
+
+ // Use a ListEditor that uses our NameLabelSource
+ ListEditor<PersonProxy, NameLabel> editor = ListEditor.of(new
NameLabelSource());
+
+ // Configure the driver
+ ListEditor<PersonProxy, NameLabel> listEditor = editor;
+ driver.initialize(eventBus, factory, listEditor);
+
+ /*
+ * Notice the backing list is essentially anonymous.
+ */
+ driver.display(new ArrayList<PersonProxy>());
+
+ // Modifying this list triggers widget creation and destruction
+ displayed = listEditor.getList();
}
@Override
@@ -103,21 +153,16 @@
}
if (event.isFavorite()) {
- if (!map.containsKey(person.stableId())) {
- NameLabel label = new NameLabel(eventBus);
- Driver driver = GWT.create(Driver.class);
- driver.initialize(eventBus, factory, label);
- driver.edit(person, null);
- label.setStylePrimaryName(style.favorite());
-
- container.add(label);
- map.put(person.stableId(), label);
- }
+ displayed.add(person);
} else {
- NameLabel toRemove = map.remove(person.stableId());
- if (toRemove != null) {
- container.remove(toRemove);
- }
- }
+ displayed.remove(person);
+ }
+
+ // Sorting the list of PersonProxies will also change the UI display
+ Collections.sort(displayed, new Comparator<PersonProxy>() {
+ public int compare(PersonProxy o1, PersonProxy o2) {
+ return o1.getName().compareTo(o2.getName());
+ }
+ });
}
}
=======================================
---
/trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/NameLabel.java
Mon Sep 13 09:30:34 2010
+++
/trunk/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/widgets/NameLabel.java
Fri Sep 17 07:40:00 2010
@@ -17,7 +17,6 @@
import com.google.gwt.editor.client.EditorDelegate;
import com.google.gwt.editor.client.ValueAwareEditor;
-import com.google.gwt.editor.client.adapters.HasTextEditor;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.shared.EventBus;
@@ -32,15 +31,14 @@
* the displayed object.
*/
class NameLabel extends Composite implements ValueAwareEditor<PersonProxy>
{
- private final Label label = new Label();
- final HasTextEditor nameEditor = HasTextEditor.of(label);
+ final Label nameEditor = new Label();
private PersonProxy person;
private HandlerRegistration subscription;
public NameLabel(final EventBus eventBus) {
- initWidget(label);
-
- label.addClickHandler(new ClickHandler() {
+ initWidget(nameEditor);
+
+ nameEditor.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
eventBus.fireEvent(new EditPersonEvent(person));
}
@@ -55,6 +53,7 @@
}
public void setDelegate(EditorDelegate<PersonProxy> delegate) {
+ assert subscription == null;
subscription = delegate.subscribe();
}
@@ -63,11 +62,10 @@
}
/**
- * Unhook event notifications when not attached to the DOM.
+ * Unhook event notifications when being permanently disposed of by
+ * FavoritesWidget.
*/
- @Override
- protected void onUnload() {
- super.onUnload();
+ protected void cancelSubscription() {
if (subscription != null) {
subscription.removeHandler();
}
=======================================
---
/trunk/user/src/com/google/gwt/editor/client/impl/AbstractEditorDelegate.java
Wed Sep 15 06:48:28 2010
+++
/trunk/user/src/com/google/gwt/editor/client/impl/AbstractEditorDelegate.java
Fri Sep 17 07:40:00 2010
@@ -52,21 +52,38 @@
}
public void attach(R object, S subEditor) {
- AbstractEditorDelegate<R, S> subDelegate = createComposedDelegate();
- map.put(subEditor, subDelegate);
+ AbstractEditorDelegate<R, S> subDelegate = map.get(subEditor);
+
@SuppressWarnings("unchecked")
Editor<Object> temp = (Editor<Object>) subEditor;
- initializeSubDelegate(subDelegate, path
- + composedEditor.getPathElement(temp), object, subEditor,
delegateMap);
+ String subPath = path + composedEditor.getPathElement(temp);
+
+ if (subDelegate == null) {
+ subDelegate = createComposedDelegate();
+ map.put(subEditor, subDelegate);
+ initializeSubDelegate(subDelegate, subPath, object, subEditor,
+ delegateMap);
+ } else {
+ subDelegate.path = subPath;
+ subDelegate.refresh(object);
+ }
}
public void detach(S subEditor) {
- map.remove(subEditor).flush(errors);
+ AbstractEditorDelegate<R, S> subDelegate = map.remove(subEditor);
+ if (subDelegate != null && subDelegate.shouldFlush()) {
+ subDelegate.flush(errors);
+ }
}
public R getValue(S subEditor) {
AbstractEditorDelegate<R, S> subDelegate = map.get(subEditor);
- subDelegate.flush(errors);
+ if (subDelegate == null) {
+ return null;
+ }
+ if (subDelegate.shouldFlush()) {
+ subDelegate.flush(errors);
+ }
return subDelegate.getObject();
}
@@ -259,6 +276,14 @@
protected abstract void setEditor(E editor);
protected abstract void setObject(T object);
+
+ /**
+ * Indicates whether or not calls to {...@link #flush} are expected as part
of
+ * normal operation.
+ */
+ protected boolean shouldFlush() {
+ return true;
+ }
/**
* Collect all paths being edited.
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/client/RequestFactoryEditorDriver.java
Wed Sep 15 02:26:39 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/client/RequestFactoryEditorDriver.java
Fri Sep 17 07:40:00 2010
@@ -18,7 +18,6 @@
import com.google.gwt.editor.client.Editor;
import com.google.gwt.editor.client.EditorError;
import com.google.gwt.event.shared.EventBus;
-import com.google.gwt.requestfactory.shared.EntityProxy;
import com.google.gwt.requestfactory.shared.RequestFactory;
import com.google.gwt.requestfactory.shared.RequestObject;
import com.google.gwt.requestfactory.shared.Violation;
@@ -53,7 +52,12 @@
* @param <E> the type of Editor that will edit the Record
* @see
com.google.gwt.requestfactory.client.testing.MockRequestFactoryEditorDriver
*/
-public interface RequestFactoryEditorDriver<P extends EntityProxy, E
extends Editor<? super P>> {
+public interface RequestFactoryEditorDriver<P, E extends Editor<? super
P>> {
+ /**
+ * Initialize the Editor and its sub-editors with data for display-only
mode.
+ */
+ void display(P proxy);
+
/**
* Initialize the Editor and its sub-editors with data.
*/
@@ -65,6 +69,8 @@
* in a depth-first manner.
*
* @return the RequestObject passed into {...@link #edit}
+ * @throws IllegalStateException if {...@link #edit(Object, RequestObject)}
has
+ * not been called with a non-null {...@link RequestObject}
*/
<T> RequestObject<T> flush();
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequestFactoryEditorDriver.java
Wed Sep 15 02:26:39 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequestFactoryEditorDriver.java
Fri Sep 17 07:40:00 2010
@@ -35,7 +35,7 @@
* @param <R> the type of Record
* @param <E> the type of Editor
*/
-public abstract class AbstractRequestFactoryEditorDriver<R extends
EntityProxy, E extends Editor<R>>
+public abstract class AbstractRequestFactoryEditorDriver<R, E extends
Editor<R>>
implements RequestFactoryEditorDriver<R, E> {
private static final DelegateMap.KeyMethod PROXY_ID_KEY = new
DelegateMap.KeyMethod() {
@@ -56,6 +56,10 @@
private RequestFactory requestFactory;
private RequestObject<?> saveRequest;
+ public void display(R object) {
+ edit(object, null);
+ }
+
public void edit(R object, RequestObject<?> saveRequest) {
checkEditor();
this.saveRequest = saveRequest;
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryEditorDelegate.java
Wed Sep 15 06:48:28 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryEditorDelegate.java
Fri Sep 17 07:40:00 2010
@@ -121,4 +121,9 @@
((RequestFactoryEditorDelegate<R, S>) subDelegate).initialize(eventBus,
factory, path, object, subEditor, delegateMap, request);
}
-}
+
+ @Override
+ protected boolean shouldFlush() {
+ return request != null;
+ }
+}
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/client/testing/MockRequestFactoryEditorDriver.java
Wed Sep 15 02:26:39 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/client/testing/MockRequestFactoryEditorDriver.java
Fri Sep 17 07:40:00 2010
@@ -19,7 +19,6 @@
import com.google.gwt.editor.client.EditorError;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.requestfactory.client.RequestFactoryEditorDriver;
-import com.google.gwt.requestfactory.shared.EntityProxy;
import com.google.gwt.requestfactory.shared.RequestFactory;
import com.google.gwt.requestfactory.shared.RequestObject;
import com.google.gwt.requestfactory.shared.Violation;
@@ -34,8 +33,8 @@
* @param <P> the Proxy type being edited
* @param <E> the Editor type
*/
-public class MockRequestFactoryEditorDriver<P extends EntityProxy, E
extends Editor<P>>
- implements RequestFactoryEditorDriver<P, E> {
+public class MockRequestFactoryEditorDriver<P, E extends Editor<P>>
implements
+ RequestFactoryEditorDriver<P, E> {
private static final String[] EMPTY_STRING = new String[0];
private EventBus eventBus;
@@ -43,6 +42,13 @@
private P proxy;
private RequestObject<?> saveRequest;
private RequestFactory requestFactory;
+
+ /**
+ * Records its arguments.
+ */
+ public void display(P proxy) {
+ this.proxy = proxy;
+ }
/**
* Records its arguments.
=======================================
--- /trunk/user/src/com/google/gwt/user/client/ui/Label.java Fri Sep 10
07:37:39 2010
+++ /trunk/user/src/com/google/gwt/user/client/ui/Label.java Fri Sep 17
07:40:00 2010
@@ -17,6 +17,9 @@
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
+import com.google.gwt.editor.client.IsEditor;
+import com.google.gwt.editor.client.LeafValueEditor;
+import com.google.gwt.editor.client.adapters.HasTextEditor;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DoubleClickEvent;
@@ -64,7 +67,7 @@
public class Label extends Widget implements HasDirectionalText,
HasWordWrap,
HasDirection, HasClickHandlers, HasDoubleClickHandlers,
SourcesClickEvents,
SourcesMouseEvents, HasAllMouseHandlers, HasDirectionEstimator,
- HasAutoHorizontalAlignment {
+ HasAutoHorizontalAlignment, IsEditor<LeafValueEditor<String>> {
/**
* Creates a Label widget that wraps an existing <div> or
<span>
@@ -260,6 +263,10 @@
public void addMouseWheelListener(MouseWheelListener listener) {
ListenerWrapper.WrappedMouseWheelListener.add(this, listener);
}
+
+ public LeafValueEditor<String> asEditor() {
+ return HasTextEditor.of(this);
+ }
/**
* {...@inheritdoc}
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors