diff options
author | John Ahlroos <john@vaadin.com> | 2013-12-18 15:11:10 +0200 |
---|---|---|
committer | Vaadin Code Review <review@vaadin.com> | 2013-12-19 12:55:03 +0000 |
commit | 4ca43cb3ff03996fc9e90ad29a5cdafd1e1a39e2 (patch) | |
tree | e4868d9e3f665eb152e17be470d94bb3d92db75c /client | |
parent | bf116b3c254e442d023eef82da5071a39552e31e (diff) | |
download | vaadin-framework-4ca43cb3ff03996fc9e90ad29a5cdafd1e1a39e2.tar.gz vaadin-framework-4ca43cb3ff03996fc9e90ad29a5cdafd1e1a39e2.zip |
Convinience data source for rendering in memory lists in Grid #12878
Change-Id: I681f580291ae7b4f5b55b8365fa1014d19688fd1
Diffstat (limited to 'client')
-rw-r--r-- | client/src/com/vaadin/client/ui/grid/datasources/ListDataSource.java | 357 | ||||
-rw-r--r-- | client/tests/src/com/vaadin/client/ui/grid/ListDataSourceTest.java | 178 |
2 files changed, 535 insertions, 0 deletions
diff --git a/client/src/com/vaadin/client/ui/grid/datasources/ListDataSource.java b/client/src/com/vaadin/client/ui/grid/datasources/ListDataSource.java new file mode 100644 index 0000000000..0a3edbd349 --- /dev/null +++ b/client/src/com/vaadin/client/ui/grid/datasources/ListDataSource.java @@ -0,0 +1,357 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.client.ui.grid.datasources; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import com.vaadin.client.data.DataChangeHandler; +import com.vaadin.client.data.DataSource; + +/** + * A simple list based on an in-memory data source for simply adding a list of + * row pojos to the grid. Based on a wrapped list instance which supports adding + * and removing of items. + * + * <p> + * Usage: + * + * <pre> + * ListDataSource<Integer> ds = new ListDataSource<Integer>(1, 2, 3, 4); + * + * // Add item to the data source + * ds.asList().add(5); + * + * // Remove item from the data source + * ds.asList().remove(3); + * + * // Add multiple items + * ds.asList().addAll(Arrays.asList(5, 6, 7)); + * </pre> + * + * @since 7.2 + * @author Vaadin Ltd + */ +public class ListDataSource<T> implements DataSource<T> { + + /** + * Wraps the datasource list and notifies the change handler of changing to + * the list + */ + private class ListWrapper implements List<T> { + + @Override + public int size() { + return ds.size(); + } + + @Override + public boolean isEmpty() { + return ds.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return contains(o); + } + + @Override + public Iterator<T> iterator() { + return new ListWrapperIterator(ds.iterator()); + } + + @Override + public Object[] toArray() { + return ds.toArray(); + } + + @Override + public <T> T[] toArray(T[] a) { + return toArray(a); + } + + @Override + public boolean add(T e) { + if (ds.add(e)) { + if (changeHandler != null) { + changeHandler.dataAdded(ds.size() - 1, 1); + } + return true; + } + return false; + } + + @Override + public boolean remove(Object o) { + int index = ds.indexOf(o); + if (ds.remove(o)) { + if (changeHandler != null) { + changeHandler.dataRemoved(index, 1); + } + return true; + } + return false; + } + + @Override + public boolean containsAll(Collection<?> c) { + return ds.containsAll(c); + } + + @Override + public boolean addAll(Collection<? extends T> c) { + int idx = ds.size(); + if (ds.addAll(c)) { + if (changeHandler != null) { + changeHandler.dataAdded(idx, c.size()); + } + return true; + } + return false; + } + + @Override + public boolean addAll(int index, Collection<? extends T> c) { + if (ds.addAll(index, c)) { + if (changeHandler != null) { + changeHandler.dataAdded(index, c.size()); + } + return true; + } + return false; + } + + @Override + public boolean removeAll(Collection<?> c) { + if (ds.removeAll(c)) { + if (changeHandler != null) { + // Have to update the whole list as the removal does not + // have to be a continuous range + changeHandler.dataUpdated(0, ds.size()); + } + return true; + } + return false; + } + + @Override + public boolean retainAll(Collection<?> c) { + if (ds.retainAll(c)) { + if (changeHandler != null) { + // Have to update the whole list as the retain does not + // have to be a continuous range + changeHandler.dataUpdated(0, ds.size()); + } + return true; + } + return false; + } + + @Override + public void clear() { + int size = ds.size(); + ds.clear(); + if (changeHandler != null) { + changeHandler.dataRemoved(0, size); + } + } + + @Override + public T get(int index) { + return ds.get(index); + } + + @Override + public T set(int index, T element) { + T prev = ds.set(index, element); + if (changeHandler != null) { + changeHandler.dataUpdated(index, 1); + } + return prev; + } + + @Override + public void add(int index, T element) { + ds.add(index, element); + if (changeHandler != null) { + changeHandler.dataAdded(index, 1); + } + } + + @Override + public T remove(int index) { + T removed = ds.remove(index); + if (changeHandler != null) { + changeHandler.dataRemoved(index, 1); + } + return removed; + } + + @Override + public int indexOf(Object o) { + return ds.indexOf(o); + } + + @Override + public int lastIndexOf(Object o) { + return ds.lastIndexOf(o); + } + + @Override + public ListIterator<T> listIterator() { + // TODO could be implemented by a custom iterator. + throw new UnsupportedOperationException( + "List iterators not supported at this time."); + } + + @Override + public ListIterator<T> listIterator(int index) { + // TODO could be implemented by a custom iterator. + throw new UnsupportedOperationException( + "List iterators not supported at this time."); + } + + @Override + public List<T> subList(int fromIndex, int toIndex) { + throw new UnsupportedOperationException("Sub lists not supported."); + } + } + + /** + * Iterator returned by {@link ListWrapper} + */ + private class ListWrapperIterator implements Iterator<T> { + + private final Iterator<T> iterator; + + /** + * Constructs a new iterator + */ + public ListWrapperIterator(Iterator<T> iterator) { + this.iterator = iterator; + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public T next() { + return iterator.next(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException( + "Iterator.remove() is not supported by this iterator."); + } + } + + /** + * Datasource for providing row pojo's + */ + private final List<T> ds; + + /** + * Wrapper that wraps the data source + */ + private final ListWrapper wrapper; + + /** + * Handler for listening to changes in the underlying list. + */ + private DataChangeHandler changeHandler; + + /** + * Constructs a new list data source. + * <p> + * Note: Modifications to the original list will not be reflected in the + * data source after the data source has been constructed. To add or remove + * items to the data source after it has been constructed use + * {@link ListDataSource#asList()}. + * + * + * @param datasource + * The list to use for providing the data to the grid + */ + public ListDataSource(List<T> datasource) { + if (datasource == null) { + throw new IllegalArgumentException("datasource cannot be null"); + } + ds = new ArrayList<T>(datasource); + wrapper = new ListWrapper(); + } + + /** + * Constructs a data source with a set of rows. You can dynamically add and + * remove rows from the data source via the list you get from + * {@link ListDataSource#asList()} + * + * @param rows + * The rows to initially add to the data source + */ + public ListDataSource(T... rows) { + if (rows == null) { + ds = new ArrayList<T>(); + } else { + ds = new ArrayList<T>(Arrays.asList(rows)); + } + wrapper = new ListWrapper(); + } + + @Override + public void ensureAvailability(int firstRowIndex, int numberOfRows) { + if (firstRowIndex >= ds.size()) { + throw new IllegalStateException( + "Trying to fetch rows outside of array"); + } + } + + @Override + public T getRow(int rowIndex) { + return ds.get(rowIndex); + } + + @Override + public int getEstimatedSize() { + return ds.size(); + } + + @Override + public void setDataChangeHandler(DataChangeHandler dataChangeHandler) { + this.changeHandler = dataChangeHandler; + } + + /** + * Gets the list that backs this datasource. Any changes made to this list + * will be reflected in the datasource. + * <p> + * Note: The list is not the same list as passed into the data source via + * the constructor. + * + * @return Returns a list implementation that wraps the real list that backs + * the data source and provides events for the data source + * listeners. + */ + public List<T> asList() { + return wrapper; + } +} diff --git a/client/tests/src/com/vaadin/client/ui/grid/ListDataSourceTest.java b/client/tests/src/com/vaadin/client/ui/grid/ListDataSourceTest.java new file mode 100644 index 0000000000..5c5e88bf69 --- /dev/null +++ b/client/tests/src/com/vaadin/client/ui/grid/ListDataSourceTest.java @@ -0,0 +1,178 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.client.ui.grid; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; + +import org.easymock.EasyMock; +import org.junit.Test; + +import com.vaadin.client.data.DataChangeHandler; +import com.vaadin.client.ui.grid.datasources.ListDataSource; + +/** + * + * @since 7.2 + * @author Vaadin Ltd + */ +public class ListDataSourceTest { + + @Test + public void testDataSourceConstruction() throws Exception { + + ListDataSource<Integer> ds = new ListDataSource<Integer>(0, 1, 2, 3); + + assertEquals(4, ds.getEstimatedSize()); + assertEquals(0, (int) ds.getRow(0)); + assertEquals(1, (int) ds.getRow(1)); + assertEquals(2, (int) ds.getRow(2)); + assertEquals(3, (int) ds.getRow(3)); + + ds = new ListDataSource<Integer>(Arrays.asList(0, 1, 2, 3)); + + assertEquals(4, ds.getEstimatedSize()); + assertEquals(0, (int) ds.getRow(0)); + assertEquals(1, (int) ds.getRow(1)); + assertEquals(2, (int) ds.getRow(2)); + assertEquals(3, (int) ds.getRow(3)); + } + + @Test + public void testListAddOperation() throws Exception { + + ListDataSource<Integer> ds = new ListDataSource<Integer>(0, 1, 2, 3); + + DataChangeHandler handler = EasyMock + .createNiceMock(DataChangeHandler.class); + ds.setDataChangeHandler(handler); + + handler.dataAdded(4, 1); + EasyMock.expectLastCall(); + + EasyMock.replay(handler); + + ds.asList().add(4); + + assertEquals(5, ds.getEstimatedSize()); + assertEquals(0, (int) ds.getRow(0)); + assertEquals(1, (int) ds.getRow(1)); + assertEquals(2, (int) ds.getRow(2)); + assertEquals(3, (int) ds.getRow(3)); + assertEquals(4, (int) ds.getRow(4)); + } + + @Test + public void testListAddAllOperation() throws Exception { + + ListDataSource<Integer> ds = new ListDataSource<Integer>(0, 1, 2, 3); + + DataChangeHandler handler = EasyMock + .createNiceMock(DataChangeHandler.class); + ds.setDataChangeHandler(handler); + + handler.dataAdded(4, 3); + EasyMock.expectLastCall(); + + EasyMock.replay(handler); + + ds.asList().addAll(Arrays.asList(4, 5, 6)); + + assertEquals(7, ds.getEstimatedSize()); + assertEquals(0, (int) ds.getRow(0)); + assertEquals(1, (int) ds.getRow(1)); + assertEquals(2, (int) ds.getRow(2)); + assertEquals(3, (int) ds.getRow(3)); + assertEquals(4, (int) ds.getRow(4)); + assertEquals(5, (int) ds.getRow(5)); + assertEquals(6, (int) ds.getRow(6)); + } + + @Test + public void testListRemoveOperation() throws Exception { + + ListDataSource<Integer> ds = new ListDataSource<Integer>(0, 1, 2, 3); + + DataChangeHandler handler = EasyMock + .createNiceMock(DataChangeHandler.class); + ds.setDataChangeHandler(handler); + + handler.dataRemoved(3, 1); + EasyMock.expectLastCall(); + + EasyMock.replay(handler); + + ds.asList().remove(2); + + assertEquals(3, ds.getEstimatedSize()); + assertEquals(0, (int) ds.getRow(0)); + assertEquals(1, (int) ds.getRow(1)); + assertEquals(3, (int) ds.getRow(2)); + } + + @Test + public void testListRemoveAllOperation() throws Exception { + + ListDataSource<Integer> ds = new ListDataSource<Integer>(0, 1, 2, 3); + + DataChangeHandler handler = EasyMock + .createNiceMock(DataChangeHandler.class); + ds.setDataChangeHandler(handler); + + handler.dataRemoved(0, 3); + EasyMock.expectLastCall(); + + EasyMock.replay(handler); + + ds.asList().removeAll(Arrays.asList(0, 2, 3)); + + assertEquals(1, ds.getEstimatedSize()); + assertEquals(1, (int) ds.getRow(0)); + } + + @Test + public void testListClearOperation() throws Exception { + + ListDataSource<Integer> ds = new ListDataSource<Integer>(0, 1, 2, 3); + + DataChangeHandler handler = EasyMock + .createNiceMock(DataChangeHandler.class); + ds.setDataChangeHandler(handler); + + handler.dataRemoved(0, 4); + EasyMock.expectLastCall(); + + EasyMock.replay(handler); + + ds.asList().clear(); + + assertEquals(0, ds.getEstimatedSize()); + } + + @Test(expected = IllegalStateException.class) + public void testFetchingNonExistantItem() { + ListDataSource<Integer> ds = new ListDataSource<Integer>(0, 1, 2, 3); + ds.ensureAvailability(5, 1); + } + + @Test(expected = UnsupportedOperationException.class) + public void testUnsupportedIteratorRemove() { + ListDataSource<Integer> ds = new ListDataSource<Integer>(0, 1, 2, 3); + ds.asList().iterator().remove(); + } + +} |