Revision: 8305
Author: gwt.mirror...@gmail.com
Date: Wed Jun 23 13:30:51 2010
Log: 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: j...@google.com
http://code.google.com/p/google-web-toolkit/source/detail?r=8305
Modified:
/trunk/user/src/com/google/gwt/view/client/ListViewAdapter.java
=======================================
--- /trunk/user/src/com/google/gwt/view/client/ListViewAdapter.java Mon
Jun 7 12:20:31 2010
+++ /trunk/user/src/com/google/gwt/view/client/ListViewAdapter.java Wed Jun
23 13:30:51 2010
@@ -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;
@@ -118,6 +137,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
@@ -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 (modified) {
- int length = maxModified - minModified;
- updateViewData(minModified, length, list.subList(minModified,
- maxModified));
- modified = false;
- }
- minModified = Integer.MAX_VALUE;
- maxModified = Integer.MIN_VALUE;
+ if (flushCancelled) {
+ flushCancelled = false;
+ return;
+ }
+ flushNow();
}
};
@@ -154,15 +173,20 @@
*/
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;
}
}
@@ -375,6 +440,18 @@
public ListViewAdapter(List<T> wrappee) {
listWrapper = new ListWrapper(wrappee);
}
+
+ /**
+ * 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
@@ -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
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors