]> source.dussan.org Git - vaadin-framework.git/commitdiff
Convinience data source for rendering in memory lists in Grid #12878
authorJohn Ahlroos <john@vaadin.com>
Wed, 18 Dec 2013 13:11:10 +0000 (15:11 +0200)
committerVaadin Code Review <review@vaadin.com>
Thu, 19 Dec 2013 12:55:03 +0000 (12:55 +0000)
Change-Id: I681f580291ae7b4f5b55b8365fa1014d19688fd1

client/src/com/vaadin/client/ui/grid/datasources/ListDataSource.java [new file with mode: 0644]
client/tests/src/com/vaadin/client/ui/grid/ListDataSourceTest.java [new file with mode: 0644]

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 (file)
index 0000000..0a3edbd
--- /dev/null
@@ -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&lt;Integer&gt; ds = new ListDataSource&lt;Integer&gt;(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 (file)
index 0000000..5c5e88b
--- /dev/null
@@ -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();
+    }
+
+}