]> source.dussan.org Git - vaadin-framework.git/commitdiff
DataSource.refreshAll() for notifying components to reload (#233).
authorDenis Anisimov <denis@vaadin.com>
Mon, 12 Sep 2016 13:52:13 +0000 (16:52 +0300)
committerVaadin Code Review <review@vaadin.com>
Wed, 14 Sep 2016 11:41:25 +0000 (11:41 +0000)
The method refreshAll() fires an event which is handled by UI components
to reload their content.

Change-Id: Ibbbe1f24b08ed883f0dda93c3ff6f05f380e9eaa

14 files changed:
server/src/main/java/com/vaadin/server/data/AbstractDataSource.java [new file with mode: 0644]
server/src/main/java/com/vaadin/server/data/BackEndDataSource.java
server/src/main/java/com/vaadin/server/data/DataChangeEvent.java [new file with mode: 0644]
server/src/main/java/com/vaadin/server/data/DataCommunicator.java
server/src/main/java/com/vaadin/server/data/DataSource.java
server/src/main/java/com/vaadin/server/data/DataSourceListener.java [new file with mode: 0644]
server/src/main/java/com/vaadin/server/data/ListDataSource.java
server/src/test/java/com/vaadin/server/data/AbstractDataSourceTest.java [new file with mode: 0644]
server/src/test/java/com/vaadin/server/data/DataCommunicatorTest.java [new file with mode: 0644]
server/src/test/java/com/vaadin/server/data/datasource/ListDataSourceTest.java
server/src/test/java/com/vaadin/server/data/datasource/StrBean.java
server/src/test/java/com/vaadin/ui/AbstractListingTest.java
uitest/src/main/java/com/vaadin/tests/components/grid/basics/RefreshDataSource.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/grid/basics/RefreshDataSourceTest.java [new file with mode: 0644]

diff --git a/server/src/main/java/com/vaadin/server/data/AbstractDataSource.java b/server/src/main/java/com/vaadin/server/data/AbstractDataSource.java
new file mode 100644 (file)
index 0000000..9dba0bd
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2000-2016 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.server.data;
+
+import java.lang.reflect.Method;
+import java.util.EventObject;
+import java.util.Objects;
+
+import com.vaadin.event.EventRouter;
+import com.vaadin.shared.Registration;
+
+/**
+ * Abstract data source implementation which takes care of refreshing data from
+ * the underlying data provider.
+ * 
+ * @author Vaadin Ltd
+ * @since 8.0
+ *
+ */
+public abstract class AbstractDataSource<T> implements DataSource<T> {
+
+    private EventRouter eventRouter;
+
+    @Override
+    public Registration addDataSourceListener(DataSourceListener listener) {
+        Objects.requireNonNull(listener, "listener cannot be null");
+        addListener(DataChangeEvent.class, listener,
+                DataSourceListener.class.getMethods()[0]);
+        return () -> removeListener(DataChangeEvent.class, listener);
+    }
+
+    @Override
+    public void refreshAll() {
+        fireEvent(new DataChangeEvent(this));
+    }
+
+    /**
+     * Registers a new listener with the specified activation method to listen
+     * events generated by this component. If the activation method does not
+     * have any arguments the event object will not be passed to it when it's
+     * called.
+     *
+     * @param eventType
+     *            the type of the listened event. Events of this type or its
+     *            subclasses activate the listener.
+     * @param listener
+     *            the object instance who owns the activation method.
+     * @param method
+     *            the activation method.
+     *
+     */
+    protected void addListener(Class<?> eventType, DataSourceListener listener,
+            Method method) {
+        if (eventRouter == null) {
+            eventRouter = new EventRouter();
+        }
+        eventRouter.addListener(eventType, listener, method);
+    }
+
+    /**
+     * Removes all registered listeners matching the given parameters. Since
+     * this method receives the event type and the listener object as
+     * parameters, it will unregister all <code>object</code>'s methods that are
+     * registered to listen to events of type <code>eventType</code> generated
+     * by this component.
+     *
+     * @param eventType
+     *            the exact event type the <code>object</code> listens to.
+     * @param listener
+     *            the target object that has registered to listen to events of
+     *            type <code>eventType</code> with one or more methods.
+     */
+    protected void removeListener(Class<?> eventType,
+            DataSourceListener listener) {
+        if (eventRouter != null) {
+            eventRouter.removeListener(eventType, listener);
+        }
+    }
+
+    /**
+     * Sends the event to all listeners.
+     *
+     * @param event
+     *            the Event to be sent to all listeners.
+     */
+    protected void fireEvent(EventObject event) {
+        if (eventRouter != null) {
+            eventRouter.fireEvent(event);
+        }
+    }
+}
index 34379deb60838635a17a19564dac4fd074eb394e..e0cfc8f48b5fb94f1726e91b6eb4411247a88bf2 100644 (file)
@@ -27,7 +27,7 @@ import java.util.stream.Stream;
  * @param <T>
  *            data source data type
  */
-public class BackEndDataSource<T> implements DataSource<T> {
+public class BackEndDataSource<T> extends AbstractDataSource<T> {
 
     private Function<Query, Stream<T>> request;
     private Function<Query, Integer> sizeCallback;
diff --git a/server/src/main/java/com/vaadin/server/data/DataChangeEvent.java b/server/src/main/java/com/vaadin/server/data/DataChangeEvent.java
new file mode 100644 (file)
index 0000000..cf2e962
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2000-2016 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.server.data;
+
+import java.util.EventObject;
+
+/**
+ * An event fired when the data of a {@code DataSource} changes.
+ * 
+ * 
+ * @see DataSourceListener
+ * 
+ * @author Vaadin Ltd
+ * @since 8.0
+ *
+ */
+public class DataChangeEvent extends EventObject {
+
+    /**
+     * Creates a new {@code DataChangeEvent} event originating from the given
+     * data source.
+     *
+     * @param source
+     *            the data source, not null
+     */
+    public DataChangeEvent(DataSource<?> source) {
+        super(source);
+    }
+
+    @Override
+    public DataSource<?> getSource() {
+        return (DataSource<?>) super.getSource();
+    }
+
+}
index b0ade744def81407d833657bfd59158198f3468e..5fc11bb15d804c64c46540c7a7f800db4e2905e3 100644 (file)
@@ -32,6 +32,7 @@ import java.util.stream.Stream;
 import com.vaadin.server.AbstractExtension;
 import com.vaadin.server.KeyMapper;
 import com.vaadin.shared.Range;
+import com.vaadin.shared.Registration;
 import com.vaadin.shared.data.DataCommunicatorClientRpc;
 import com.vaadin.shared.data.DataCommunicatorConstants;
 import com.vaadin.shared.data.DataRequestRpc;
@@ -50,6 +51,8 @@ import elemental.json.JsonObject;
  */
 public class DataCommunicator<T> extends AbstractExtension {
 
+    private Registration dataSourceUpdateRegistration;
+
     /**
      * Simple implementation of collection data provider communication. All data
      * is sent by server automatically and no data is requested by client.
@@ -195,6 +198,18 @@ public class DataCommunicator<T> extends AbstractExtension {
         keyMapper = createKeyMapper();
     }
 
+    @Override
+    public void attach() {
+        super.attach();
+        attachDataSourceListener();
+    }
+
+    @Override
+    public void detach() {
+        super.detach();
+        detachDataSourceListener();
+    }
+
     /**
      * Initially and in the case of a reset all data should be pushed to the
      * client.
@@ -459,6 +474,22 @@ public class DataCommunicator<T> extends AbstractExtension {
     public void setDataSource(DataSource<T> dataSource) {
         Objects.requireNonNull(dataSource, "data source cannot be null");
         this.dataSource = dataSource;
+        detachDataSourceListener();
+        if (isAttached()) {
+            attachDataSourceListener();
+        }
         reset();
     }
+
+    private void attachDataSourceListener() {
+        dataSourceUpdateRegistration = getDataSource()
+                .addDataSourceListener(event -> reset());
+    }
+
+    private void detachDataSourceListener() {
+        if (dataSourceUpdateRegistration != null) {
+            dataSourceUpdateRegistration.remove();
+            dataSourceUpdateRegistration = null;
+        }
+    }
 }
index 25679a3c89bc731b76ad6923c46196194db7e383..f767f60cf081fa2f0cef7cc8011daea5089ee66b 100644 (file)
@@ -21,6 +21,8 @@ import java.util.Collection;
 import java.util.function.Function;
 import java.util.stream.Stream;
 
+import com.vaadin.shared.Registration;
+
 /**
  * Minimal DataSource API for communication between the DataProvider and a back
  * end service.
@@ -55,6 +57,27 @@ public interface DataSource<T>
      */
     int size(Query t);
 
+    /**
+     * Refreshes all data based on currently available data in the underlying
+     * provider.
+     */
+    void refreshAll();
+
+    /**
+     * Adds a data source listener. The listener is called when some piece of
+     * data is updated.
+     * <p>
+     * The {@link #refreshAll()} method fires {@link DataChangeEvent} each time
+     * when it's called. It allows to update UI components when user changes
+     * something in the underlying data.
+     *
+     * @see #refreshAll()
+     * @param listener
+     *            the data change listener, not null
+     * @return a registration for the listener
+     */
+    Registration addDataSourceListener(DataSourceListener listener);
+
     /**
      * This method creates a new {@link ListDataSource} from a given Collection.
      * The ListDataSource creates a protective List copy of all the contents in
diff --git a/server/src/main/java/com/vaadin/server/data/DataSourceListener.java b/server/src/main/java/com/vaadin/server/data/DataSourceListener.java
new file mode 100644 (file)
index 0000000..c9c486c
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2000-2016 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.server.data;
+
+import java.io.Serializable;
+
+/**
+ * Interface for listening for a data change events fired by a
+ * {@link DataSource}.
+ * 
+ * @author Vaadin Ltd
+ * @since 8.0
+ */
+public interface DataSourceListener extends Serializable {
+
+    /**
+     * Invoked when this listener receives a data change event from a data
+     * source to which it has been added.
+     * <p>
+     * This event is fired when something has changed in the underlying data. It
+     * doesn't allow to distinguish different kind of events
+     * (add/remove/update). It means that the method implementation normally
+     * just reloads the whole data to refresh.
+     *
+     * @param event
+     *            the received event, not null
+     */
+    void onDataChange(DataChangeEvent event);
+}
index ecac378182de0cb8de896b9a9b31091f57431d0e..64b7c03cb11a1eaf5b8e212ac0f27631e1812850 100644 (file)
  */
 package com.vaadin.server.data;
 
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
-import java.util.List;
 import java.util.Objects;
 import java.util.function.Function;
 import java.util.stream.Stream;
@@ -30,10 +28,10 @@ import java.util.stream.Stream;
  * @param <T>
  *            data type
  */
-public class ListDataSource<T> implements DataSource<T> {
+public class ListDataSource<T> extends AbstractDataSource<T> {
 
-    private Function<Query, Stream<T>> request;
-    private int size;
+    private Comparator<T> sortOrder;
+    private final Collection<T> backend;
 
     /**
      * Constructs a new ListDataSource. This method makes a protective copy of
@@ -44,26 +42,33 @@ public class ListDataSource<T> implements DataSource<T> {
      */
     public ListDataSource(Collection<T> items) {
         Objects.requireNonNull(items, "items cannot be null");
-        final List<T> backend = new ArrayList<>(items);
-        request = query -> backend.stream();
-        size = backend.size();
+        backend = items;
+        sortOrder = null;
     }
 
     /**
      * Chaining constructor for making modified {@link ListDataSource}s. This
      * Constructor is used internally for making sorted and filtered variants of
      * a base data source with actual data.
+     * 
+     * @param items
+     *            the backend data from the original list data source
+     * @param sortOrder
+     *            a {@link Comparator} providing the needed sorting order
      *
-     * @param request
-     *            request for the new data source
      */
-    protected ListDataSource(Function<Query, Stream<T>> request) {
-        this.request = request;
+    protected ListDataSource(Collection<T> items, Comparator<T> sortOrder) {
+        this(items);
+        this.sortOrder = sortOrder;
     }
 
     @Override
     public Stream<T> apply(Query query) {
-        return request.apply(query);
+        Stream<T> stream = backend.stream();
+        if (sortOrder != null) {
+            stream = stream.sorted(sortOrder);
+        }
+        return stream;
     }
 
     /**
@@ -77,7 +82,7 @@ public class ListDataSource<T> implements DataSource<T> {
      * @return new data source with modified sorting
      */
     public ListDataSource<T> sortingBy(Comparator<T> sortOrder) {
-        return new ListDataSource<>(q -> request.apply(q).sorted(sortOrder));
+        return new ListDataSource<>(backend, sortOrder);
     }
 
     /**
@@ -113,6 +118,7 @@ public class ListDataSource<T> implements DataSource<T> {
      */
     @Override
     public int size(Query t) {
-        return size;
+        return backend.size();
     }
+
 }
diff --git a/server/src/test/java/com/vaadin/server/data/AbstractDataSourceTest.java b/server/src/test/java/com/vaadin/server/data/AbstractDataSourceTest.java
new file mode 100644 (file)
index 0000000..4898869
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2000-2016 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.server.data;
+
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Stream;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.vaadin.shared.Registration;
+
+/**
+ * @author Vaadin Ltd
+ *
+ */
+public class AbstractDataSourceTest {
+
+    private static class TestDataSource extends AbstractDataSource<Object> {
+        @Override
+        public Stream<Object> apply(Query t) {
+            return null;
+        }
+
+        @Override
+        public int size(Query t) {
+            return 0;
+        }
+
+        @Override
+        public boolean isInMemory() {
+            return false;
+        }
+    }
+
+    @Test
+    public void refreshAll_notifyListeners() {
+        AbstractDataSource<Object> dataSource = new TestDataSource();
+        AtomicReference<DataChangeEvent> event = new AtomicReference<>();
+        dataSource.addDataSourceListener(ev -> {
+            Assert.assertNull(event.get());
+            event.set(ev);
+        });
+        dataSource.refreshAll();
+        Assert.assertNotNull(event.get());
+        Assert.assertEquals(dataSource, event.get().getSource());
+    }
+
+    @Test
+    public void removeListener_listenerIsNotNotified() {
+        AbstractDataSource<Object> dataSource = new TestDataSource();
+        AtomicReference<DataChangeEvent> event = new AtomicReference<>();
+        Registration registration = dataSource
+                .addDataSourceListener(ev -> event.set(ev));
+        registration.remove();
+        dataSource.refreshAll();
+        Assert.assertNull(event.get());
+    }
+}
diff --git a/server/src/test/java/com/vaadin/server/data/DataCommunicatorTest.java b/server/src/test/java/com/vaadin/server/data/DataCommunicatorTest.java
new file mode 100644 (file)
index 0000000..b82d571
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2000-2016 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.server.data;
+
+import java.util.Collections;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import com.vaadin.server.MockVaadinSession;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.server.VaadinService;
+import com.vaadin.server.VaadinSession;
+import com.vaadin.shared.Registration;
+import com.vaadin.ui.UI;
+
+/**
+ * @author Vaadin Ltd
+ *
+ */
+public class DataCommunicatorTest {
+
+    private static class TestUI extends UI {
+
+        private final VaadinSession session;
+
+        TestUI(VaadinSession session) {
+            this.session = session;
+        }
+
+        @Override
+        protected void init(VaadinRequest request) {
+        }
+
+        @Override
+        public VaadinSession getSession() {
+            return session;
+        }
+    }
+
+    private static class TestDataSource extends ListDataSource<Object>
+            implements Registration {
+
+        private Registration registration;
+
+        public TestDataSource() {
+            super(Collections.singleton(new Object()));
+        }
+
+        @Override
+        public Registration addDataSourceListener(DataSourceListener listener) {
+            registration = super.addDataSourceListener(listener);
+            return this;
+        }
+
+        @Override
+        public void remove() {
+            registration.remove();
+            registration = null;
+        }
+
+        public boolean isListenerAdded() {
+            return registration != null;
+        }
+
+    }
+
+    private static class TestDataCommunicator extends DataCommunicator<Object> {
+        protected void extend(UI ui) {
+            super.extend(ui);
+        }
+    }
+
+    private MockVaadinSession session = new MockVaadinSession(
+            Mockito.mock(VaadinService.class));
+
+    @Test
+    public void attach_dataSourceListenerIsNotAddedBeforeAttachAndAddedAfter() {
+        session.lock();
+
+        UI ui = new TestUI(session);
+
+        TestDataCommunicator communicator = new TestDataCommunicator();
+
+        TestDataSource dataSource = new TestDataSource();
+        communicator.setDataSource(dataSource);
+
+        Assert.assertFalse(dataSource.isListenerAdded());
+
+        communicator.extend(ui);
+
+        Assert.assertTrue(dataSource.isListenerAdded());
+    }
+
+    @Test
+    public void detach_dataSourceListenerIsRemovedAfterDetach() {
+        session.lock();
+
+        UI ui = new TestUI(session);
+
+        TestDataCommunicator communicator = new TestDataCommunicator();
+
+        TestDataSource dataSource = new TestDataSource();
+        communicator.setDataSource(dataSource);
+
+        communicator.extend(ui);
+
+        Assert.assertTrue(dataSource.isListenerAdded());
+
+        communicator.detach();
+
+        Assert.assertFalse(dataSource.isListenerAdded());
+    }
+
+}
index 15ee2d5f2d3f4f8cbc62b6baed8d3d6d6c5033f5..a6dcc77474e8fbd75732556ab6d757b08c748c4c 100644 (file)
@@ -3,6 +3,7 @@ package com.vaadin.server.data.datasource;
 import static org.junit.Assert.assertTrue;
 
 import java.util.Comparator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -27,12 +28,13 @@ public class ListDataSourceTest {
 
     @Test
     public void testListContainsAllData() {
+        List<StrBean> list = new LinkedList<>(data);
         dataSource.apply(new Query())
                 .forEach(str -> assertTrue(
                         "Data source contained values not in original data",
-                        data.remove(str)));
+                        list.remove(str)));
         assertTrue("Not all values from original data were in data source",
-                data.isEmpty());
+                list.isEmpty());
     }
 
     @Test
@@ -102,4 +104,92 @@ public class ListDataSourceTest {
             Assert.assertTrue(prev.getValue().compareTo(cur.getValue()) <= 0);
         }
     }
+
+    @Test
+    public void refreshAll_changeBeanInstance() {
+        StrBean bean = new StrBean("foo", -1, hashCode());
+        Query query = new Query();
+        int size = dataSource.size(query);
+
+        data.set(0, bean);
+        dataSource.refreshAll();
+
+        List<StrBean> list = dataSource.apply(query)
+                .collect(Collectors.toList());
+        StrBean first = list.get(0);
+        Assert.assertEquals(bean.getValue(), first.getValue());
+        Assert.assertEquals(bean.getRandomNumber(), first.getRandomNumber());
+        Assert.assertEquals(bean.getId(), first.getId());
+
+        Assert.assertEquals(size, dataSource.size(query));
+    }
+
+    @Test
+    public void refreshAll_updateBean() {
+        Query query = new Query();
+        int size = dataSource.size(query);
+
+        StrBean bean = data.get(0);
+        bean.setValue("foo");
+        dataSource.refreshAll();
+
+        List<StrBean> list = dataSource.apply(query)
+                .collect(Collectors.toList());
+        StrBean first = list.get(0);
+        Assert.assertEquals("foo", first.getValue());
+
+        Assert.assertEquals(size, dataSource.size(query));
+    }
+
+    @Test
+    public void refreshAll_sortingBy_changeBeanInstance() {
+        StrBean bean = new StrBean("foo", -1, hashCode());
+        Query query = new Query();
+        int size = dataSource.size(query);
+
+        data.set(0, bean);
+
+        ListDataSource<StrBean> dSource = dataSource
+                .sortingBy(Comparator.comparing(StrBean::getId));
+        dSource.refreshAll();
+
+        List<StrBean> list = dSource.apply(query).collect(Collectors.toList());
+        StrBean first = list.get(0);
+        Assert.assertEquals(bean.getValue(), first.getValue());
+        Assert.assertEquals(bean.getRandomNumber(), first.getRandomNumber());
+        Assert.assertEquals(bean.getId(), first.getId());
+
+        Assert.assertEquals(size, dataSource.size(query));
+    }
+
+    @Test
+    public void refreshAll_addBeanInstance() {
+        StrBean bean = new StrBean("foo", -1, hashCode());
+
+        Query query = new Query();
+        int size = dataSource.size(query);
+
+        data.add(0, bean);
+        dataSource.refreshAll();
+
+        List<StrBean> list = dataSource.apply(query)
+                .collect(Collectors.toList());
+        StrBean first = list.get(0);
+        Assert.assertEquals(bean.getValue(), first.getValue());
+        Assert.assertEquals(bean.getRandomNumber(), first.getRandomNumber());
+        Assert.assertEquals(bean.getId(), first.getId());
+
+        Assert.assertEquals(size + 1, dataSource.size(query));
+    }
+
+    @Test
+    public void refreshAll_removeBeanInstance() {
+        Query query = new Query();
+        int size = dataSource.size(query);
+
+        data.remove(0);
+        dataSource.refreshAll();
+
+        Assert.assertEquals(size - 1, dataSource.size(query));
+    }
 }
index 52d58be310b60b77c3bf4f20aa37ba0d8712f114..24f478ad47ad24c130db11f85617c384a0a46a1d 100644 (file)
@@ -31,6 +31,10 @@ class StrBean implements Serializable {
         return randomNumber;
     }
 
+    public void setValue(String value) {
+        this.value = value;
+    }
+
     public static List<StrBean> generateRandomBeans(int max) {
         List<StrBean> data = new ArrayList<>();
         Random r = new Random(13337);
index 6367a445853729b06c7595c988ae5bbe24bd0dcc..bbe0fd01aa25065179de19dd8c94d1579012e3a9 100644 (file)
@@ -2,6 +2,7 @@ package com.vaadin.ui;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.stream.Stream;
 
@@ -19,8 +20,7 @@ import elemental.json.JsonObject;
 
 public class AbstractListingTest {
 
-    private final class TestListing extends
-            AbstractSingleSelect<String> {
+    private final class TestListing extends AbstractSingleSelect<String> {
 
         protected TestListing() {
             setSelectionModel(new SimpleSingleSelection());
@@ -69,11 +69,12 @@ public class AbstractListingTest {
     @Test
     public void testSetItemsWithCollection() {
         listing.setItems(items);
+        List<String> list = new LinkedList<>(items);
         listing.getDataSource().apply(new Query()).forEach(
                 str -> Assert.assertTrue("Unexpected item in data source",
-                        items.remove(str)));
+                        list.remove(str)));
         Assert.assertTrue("Not all items from list were in data source",
-                items.isEmpty());
+                list.isEmpty());
     }
 
     @Test
diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/basics/RefreshDataSource.java b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/RefreshDataSource.java
new file mode 100644 (file)
index 0000000..2623d61
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2000-2016 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.tests.components.grid.basics;
+
+import java.util.List;
+
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.server.data.DataSource;
+import com.vaadin.server.data.ListDataSource;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Grid;
+
+/**
+ * @author Vaadin Ltd
+ *
+ */
+public class RefreshDataSource extends AbstractTestUI {
+
+    @Override
+    protected void setup(VaadinRequest request) {
+        Grid<DataObject> grid = new Grid<>();
+        List<DataObject> data = DataObject.generateObjects();
+
+        ListDataSource<DataObject> dataSource = DataSource.create(data);
+        grid.setDataSource(dataSource);
+
+        grid.setDataSource(dataSource);
+        grid.addColumn("Coordinates", DataObject::getCoordinates);
+        addComponent(grid);
+
+        Button update = new Button("Update data",
+                event -> updateData(dataSource, data));
+        update.setId("update");
+        addComponent(update);
+
+        Button add = new Button("Add data", event -> addData(dataSource, data));
+        add.setId("add");
+        addComponent(add);
+
+        Button remove = new Button("Remove data",
+                event -> removeData(dataSource, data));
+        remove.setId("remove");
+        addComponent(remove);
+    }
+
+    private void updateData(DataSource<DataObject> dataSource,
+            List<DataObject> data) {
+        data.get(0).setCoordinates("Updated coordinates");
+        dataSource.refreshAll();
+    }
+
+    private void addData(DataSource<DataObject> dataSource,
+            List<DataObject> data) {
+        DataObject dataObject = new DataObject();
+        dataObject.setCoordinates("Added");
+        data.add(0, dataObject);
+        dataSource.refreshAll();
+    }
+
+    private void removeData(DataSource<DataObject> dataSource,
+            List<DataObject> data) {
+        data.remove(0);
+        dataSource.refreshAll();
+    }
+}
diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/RefreshDataSourceTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/RefreshDataSourceTest.java
new file mode 100644 (file)
index 0000000..e73bbd6
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2000-2016 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.tests.components.grid.basics;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import com.vaadin.tests.tb3.MultiBrowserTest;
+
+/**
+ * @author Vaadin Ltd
+ *
+ */
+public class RefreshDataSourceTest extends MultiBrowserTest {
+
+    @Test
+    public void updateFirstRow() {
+        openTestURL();
+
+        findElement(By.id("update")).click();
+        WebElement first = findElement(By.tagName("td"));
+        Assert.assertEquals(
+                "UI component is not refreshed after update in data",
+                "Updated coordinates", first.getText());
+    }
+
+    @Test
+    public void addFirstRow() {
+        openTestURL();
+
+        findElement(By.id("add")).click();
+        WebElement first = findElement(By.tagName("td"));
+
+        Assert.assertEquals("UI component is not refreshed after add new data",
+                "Added", first.getText());
+    }
+
+    @Test
+    public void removeFirstRow() {
+        openTestURL();
+
+        WebElement first = findElement(By.tagName("td"));
+        String old = first.getText();
+        first = findElement(By.id("remove"));
+        Assert.assertNotEquals("UI component is not refreshed after removal",
+                old, first.getText());
+    }
+
+}
\ No newline at end of file