Finishing implementation of ListViewAdapter. An extensive test class will be submitted in a later change because the rest relies on API changes that haven't been submitted yet.
Review at http://gwt-code-reviews.appspot.com/636802
Review by: jgw@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8305 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/view/client/ListViewAdapter.java b/user/src/com/google/gwt/view/client/ListViewAdapter.java
index d88f895..0694d16 100644
--- a/user/src/com/google/gwt/view/client/ListViewAdapter.java
+++ b/user/src/com/google/gwt/view/client/ListViewAdapter.java
@@ -47,7 +47,23 @@
*/
private final class WrappedListIterator implements ListIterator<T> {
- int i = 0, last = -1;
+ /**
+ * The error message when {@link #add(Object)} or {@link #remove()} is
+ * called more than once per call to {@link #next()} or
+ * {@link #previous()}.
+ */
+ private static final String IMPERMEABLE_EXCEPTION = "Cannot call add/remove more than once per call to next/previous.";
+
+ /**
+ * The index of the object that will be returned by {@link #next()}.
+ */
+ private int i = 0;
+
+ /**
+ * The index of the last object accessed through {@link #next()} or
+ * {@link #previous()}.
+ */
+ private int last = -1;
private WrappedListIterator() {
}
@@ -62,6 +78,9 @@
}
public void add(T o) {
+ if (last < 0) {
+ throw new IllegalStateException(IMPERMEABLE_EXCEPTION);
+ }
ListWrapper.this.add(i++, o);
last = -1;
}
@@ -98,7 +117,7 @@
public void remove() {
if (last < 0) {
- throw new IllegalStateException();
+ throw new IllegalStateException(IMPERMEABLE_EXCEPTION);
}
ListWrapper.this.remove(last);
i = last;
@@ -119,6 +138,16 @@
private int curSize = 0;
/**
+ * The delegate wrapper.
+ */
+ private final ListWrapper delegate;
+
+ /**
+ * Set to true if the pending flush has been cancelled.
+ */
+ private boolean flushCancelled;
+
+ /**
* We wait until the end of the current event loop before flushing changes
* so that we don't spam the views. This also allows users to clear and
* replace all of the data without forcing the view back to page 0.
@@ -126,21 +155,11 @@
private Command flushCommand = new Command() {
public void execute() {
flushPending = false;
-
- int newSize = list.size();
- if (curSize != newSize) {
- curSize = newSize;
- updateDataSize(curSize, true);
+ if (flushCancelled) {
+ flushCancelled = false;
+ return;
}
-
- if (modified) {
- int length = maxModified - minModified;
- updateViewData(minModified, length, list.subList(minModified,
- maxModified));
- modified = false;
- }
- minModified = Integer.MAX_VALUE;
- maxModified = Integer.MIN_VALUE;
+ flushNow();
}
};
@@ -155,14 +174,19 @@
private List<T> list;
/**
+ * If this is a sublist, the offset it the index relative to the main list.
+ */
+ private final int offset;
+
+ /**
* If modified is true, the smallest modified index.
*/
- private int maxModified;
+ private int maxModified = Integer.MIN_VALUE;
/**
* If modified is true, one past the largest modified index.
*/
- private int minModified;
+ private int minModified = Integer.MAX_VALUE;
/**
* True if the list data has been modified.
@@ -170,10 +194,21 @@
private boolean modified;
public ListWrapper(List<T> list) {
+ this(list, null, 0);
+ }
+
+ /**
+ * Construct a new {@link ListWrapper} that delegates flush calls to the
+ * specified delegate.
+ *
+ * @param list the list to wrap
+ * @param delegate the delegate
+ * @param offset the offset of this list
+ */
+ private ListWrapper(List<T> list, ListWrapper delegate, int offset) {
this.list = list;
- minModified = 0;
- maxModified = list.size();
- modified = true;
+ this.delegate = delegate;
+ this.offset = offset;
}
public void add(int index, T element) {
@@ -193,7 +228,8 @@
minModified = Math.min(minModified, size() - 1);
maxModified = size();
modified = true;
- return flush(toRet);
+ flush();
+ return toRet;
}
public boolean addAll(Collection<? extends T> c) {
@@ -201,7 +237,8 @@
boolean toRet = list.addAll(c);
maxModified = size();
modified = true;
- return flush(toRet);
+ flush();
+ return toRet;
}
public boolean addAll(int index, Collection<? extends T> c) {
@@ -210,7 +247,8 @@
minModified = Math.min(minModified, index);
maxModified = size();
modified = true;
- return flush(toRet);
+ flush();
+ return toRet;
} catch (IndexOutOfBoundsException e) {
throw new IndexOutOfBoundsException(e.getMessage());
}
@@ -254,8 +292,7 @@
}
public Iterator<T> iterator() {
- // TODO(jlabanca): Wrap the iterator
- return list.iterator();
+ return listIterator();
}
public int lastIndexOf(Object o) {
@@ -297,7 +334,8 @@
minModified = 0;
maxModified = size();
modified = true;
- return flush(toRet);
+ flush();
+ return toRet;
}
public boolean retainAll(Collection<?> c) {
@@ -305,7 +343,8 @@
minModified = 0;
maxModified = size();
modified = true;
- return flush(toRet);
+ flush();
+ return toRet;
}
public T set(int index, T element) {
@@ -322,8 +361,7 @@
}
public List<T> subList(int fromIndex, int toIndex) {
- // TODO(jlabanca): wrap the sublist
- return list.subList(fromIndex, toIndex);
+ return new ListWrapper(list.subList(fromIndex, toIndex), this, fromIndex);
}
public Object[] toArray() {
@@ -338,6 +376,18 @@
* Flush the data to the model.
*/
private void flush() {
+ // Defer to the delegate.
+ if (delegate != null) {
+ delegate.minModified = Math.min(minModified + offset,
+ delegate.minModified);
+ delegate.maxModified = Math.max(maxModified + offset,
+ delegate.maxModified);
+ delegate.modified = modified || delegate.modified;
+ delegate.flush();
+ return;
+ }
+
+ flushCancelled = false;
if (!flushPending) {
flushPending = true;
DeferredCommand.addCommand(flushCommand);
@@ -345,13 +395,28 @@
}
/**
- * Flush the data to the model and return the boolean.
- *
- * @param toRet the boolean to return
+ * Flush pending list changes to the views. By default,
*/
- private boolean flush(boolean toRet) {
- flush();
- return toRet;
+ private void flushNow() {
+ // Cancel any pending flush command.
+ if (flushPending) {
+ flushCancelled = true;
+ }
+
+ int newSize = list.size();
+ if (curSize != newSize) {
+ curSize = newSize;
+ updateDataSize(curSize, true);
+ }
+
+ if (modified) {
+ int length = maxModified - minModified;
+ updateViewData(minModified, length, list.subList(minModified,
+ maxModified));
+ modified = false;
+ }
+ minModified = Integer.MAX_VALUE;
+ maxModified = Integer.MIN_VALUE;
}
}
@@ -377,6 +442,18 @@
}
/**
+ * Flush pending list changes to the views. By default, views are informed of
+ * modifications to the underlying list at the end of the current event loop,
+ * which makes it possible to perform multiple operations synchronously
+ * without repeatedly refreshing the views. This method can be called to flush
+ * the changes immediately instead of waiting until the end of the current
+ * event loop.
+ */
+ public void flush() {
+ listWrapper.flushNow();
+ }
+
+ /**
* Get the list that backs this model. Changes to the list will be reflected
* in the model.
*
@@ -400,7 +477,10 @@
*/
public void setList(List<T> wrappee) {
listWrapper = new ListWrapper(wrappee);
- listWrapper.flush();
+ listWrapper.minModified = 0;
+ listWrapper.maxModified = listWrapper.size();
+ listWrapper.modified = true;
+ flush();
}
@Override