]> source.dussan.org Git - vaadin-framework.git/commitdiff
Add DataProvider.convertFilter, fix ListDataProvider filtering
authorTeemu Suo-Anttila <teemusa@vaadin.com>
Mon, 21 Nov 2016 15:35:19 +0000 (17:35 +0200)
committerTeemu Suo-Anttila <teemusa@vaadin.com>
Mon, 21 Nov 2016 16:57:51 +0000 (18:57 +0200)
Change-Id: Ic90ae83acf5d77aa9b0f485dff4e55bba5296fa7

server/src/main/java/com/vaadin/server/data/DataCommunicator.java
server/src/main/java/com/vaadin/server/data/DataProvider.java
server/src/main/java/com/vaadin/server/data/FilteringDataProviderWrapper.java [new file with mode: 0644]
server/src/main/java/com/vaadin/server/data/ListDataProvider.java
server/src/main/java/com/vaadin/server/data/Query.java
server/src/test/java/com/vaadin/server/data/provider/ListDataProviderTest.java
uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectAddRemoveItems.java

index fc66185aeec74a0fe2a387531d4b98c94c1662ed..852c583a56c5fda17f4604788edaa5f212b1b9e8 100644 (file)
@@ -18,7 +18,6 @@ package com.vaadin.server.data;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
@@ -223,16 +222,14 @@ public class DataCommunicator<T> extends AbstractExtension {
             return;
         }
 
-        // FIXME: Sorting and Filtering with Backend
-        Set<Object> filters = Collections.emptySet();
-
         if (initial || reset) {
             int dataProviderSize;
             if (getDataProvider().isInMemory() && inMemoryFilter != null) {
-                dataProviderSize = (int) getDataProvider().fetch(new Query())
+                dataProviderSize = (int) getDataProvider().fetch(new Query<>())
                         .filter(inMemoryFilter).count();
             } else {
-                dataProviderSize = getDataProvider().size(new Query(filters));
+                // TODO: Apply filter
+                dataProviderSize = getDataProvider().size(new Query<>());
             }
             rpc.reset(dataProviderSize);
         }
@@ -245,7 +242,7 @@ public class DataCommunicator<T> extends AbstractExtension {
 
             if (getDataProvider().isInMemory()) {
                 // We can safely request all the data when in memory
-                rowsToPush = getDataProvider().fetch(new Query());
+                rowsToPush = getDataProvider().fetch(new Query<>());
                 if (inMemoryFilter != null) {
                     rowsToPush = rowsToPush.filter(inMemoryFilter);
                 }
@@ -254,8 +251,8 @@ public class DataCommunicator<T> extends AbstractExtension {
                 }
                 rowsToPush = rowsToPush.skip(offset).limit(limit);
             } else {
-                Query query = new Query(offset, limit, backEndSorting, filters);
-                rowsToPush = getDataProvider().fetch(query);
+                rowsToPush = getDataProvider().fetch(
+                        new Query<>(offset, limit, backEndSorting, null));
             }
             pushData(offset, rowsToPush);
         }
index 441a57b559c6b48bfbac5b2e88701ffa31f92154..19c657be70eb1b74ce0c036ca103235a86d03305 100644 (file)
@@ -20,6 +20,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.stream.Stream;
 
+import com.vaadin.server.SerializableFunction;
 import com.vaadin.shared.Registration;
 
 /**
@@ -88,6 +89,39 @@ public interface DataProvider<T, F> extends Serializable {
      */
     Registration addDataProviderListener(DataProviderListener listener);
 
+    /**
+     * Convert the data provider to use a different filter type. It is used for
+     * adapting this data provider to a filter type provided by a Component such
+     * as ComboBox.
+     * <p>
+     * For example receiving a String from ComboBox and making a Predicate based
+     * on it:
+     *
+     * <pre>
+     * DataProvider&lt;Person, Predicate&lt;Person&gt;&gt; dataProvider;
+     * // ComboBox uses String as the filter type
+     * DataProvider&lt;Person, String&gt; wrappedProvider = dataProvider
+     *         .convertFilter(filterText -> {
+     *             Predicate&lt;Person&gt; predicate = person -> person.getName()
+     *                     .startsWith(filterText);
+     *             return predicate;
+     *         });
+     * comboBox.setDataProvider(wrappedProvider);
+     * </pre>
+     *
+     * @param mapper
+     *            the mapper from new filter type to old filter type
+     *
+     * @param <M>
+     *            the filter type to map from; typically provided by a Component
+     *
+     * @return wrapped data provider
+     */
+    default <M> DataProvider<T, M> convertFilter(
+            SerializableFunction<M, F> mapper) {
+        return new FilteringDataProviderWrapper<>(this, mapper);
+    }
+
     /**
      * This method creates a new {@link ListDataProvider} from a given
      * Collection. The ListDataProvider creates a protective List copy of all
diff --git a/server/src/main/java/com/vaadin/server/data/FilteringDataProviderWrapper.java b/server/src/main/java/com/vaadin/server/data/FilteringDataProviderWrapper.java
new file mode 100644 (file)
index 0000000..10384a9
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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.stream.Stream;
+
+import com.vaadin.server.SerializableFunction;
+import com.vaadin.shared.Registration;
+
+/**
+ * Wrapper class for modifying filters in a query. Used to create a suitable
+ * {@link Query} for the underlying data provider.
+ *
+ * @author Vaadin Ltd.
+ *
+ * @param <T>
+ *            data provider data type
+ * @param <F>
+ *            wrapper query filter type
+ * @param <M>
+ *            underlying data provider filter type
+ */
+public class FilteringDataProviderWrapper<T, F, M>
+        implements DataProvider<T, F> {
+
+    private DataProvider<T, M> dataProvider;
+    private SerializableFunction<F, M> mapper;
+
+    public FilteringDataProviderWrapper(DataProvider<T, M> dataProvider,
+            SerializableFunction<F, M> mapper) {
+        this.dataProvider = dataProvider;
+        this.mapper = mapper;
+    }
+
+    @Override
+    public boolean isInMemory() {
+        return dataProvider.isInMemory();
+    }
+
+    @Override
+    public void refreshAll() {
+        dataProvider.refreshAll();
+    }
+
+    @Override
+    public Registration addDataProviderListener(DataProviderListener listener) {
+        return dataProvider.addDataProviderListener(listener);
+    }
+
+    @Override
+    public int size(Query<F> t) {
+        return dataProvider.size(new Query<M>(t.getOffset(), t.getLimit(),
+                t.getSortOrders(), t.getFilter().map(mapper).orElse(null)));
+    }
+
+    @Override
+    public Stream<T> fetch(Query<F> t) {
+        return dataProvider.fetch(new Query<M>(t.getOffset(), t.getLimit(),
+                t.getSortOrders(), t.getFilter().map(mapper).orElse(null)));
+    }
+
+}
index a67ce42eaba1be24d3191db51da13dff57487f76..0184a2e37bf0c461cd7cb3a0b3bd44ca18ea0611 100644 (file)
@@ -66,8 +66,9 @@ public class ListDataProvider<T>
     }
 
     @Override
-    public Stream<T> fetch(Query query) {
-        Stream<T> stream = backend.stream();
+    public Stream<T> fetch(Query<SerializablePredicate<T>> query) {
+        Stream<T> stream = backend.stream()
+                .filter(t -> query.getFilter().orElse(p -> true).test(t));
         if (sortOrder != null) {
             stream = stream.sorted(sortOrder);
         }
@@ -113,15 +114,11 @@ public class ListDataProvider<T>
         return true;
     }
 
-    /**
-     * {@inheritDoc}
-     * <p>
-     * For in-memory data provider the query is not handled, and it will always
-     * return the full size.
-     */
     @Override
-    public int size(Query query) {
-        return backend.size();
+    public int size(Query<SerializablePredicate<T>> query) {
+        return (int) backend.stream()
+                .filter(t -> query.getFilter().orElse(p -> true).test(t))
+                .count();
     }
 
 }
index 3762039a722353b2752922b53efb03af85dc82dd..3aeaac6acc2a8cbca197f3624feb8c035889f781 100644 (file)
@@ -52,7 +52,8 @@ public class Query<F> implements Serializable {
      * filtering.
      *
      * @param filter
-     *            back end filter of a suitable type for the data provider
+     *            back end filter of a suitable type for the data provider; can
+     *            be null
      */
     public Query(F filter) {
         offset = 0;
@@ -72,7 +73,7 @@ public class Query<F> implements Serializable {
      * @param sortOrders
      *            sorting order for fetching
      * @param filter
-     *            filtering for fetching
+     *            filtering for fetching; can be null
      */
     public Query(int offset, int limit, List<SortOrder<String>> sortOrders,
             F filter) {
index 37b4214a4560f6b06e437e71094d1af40d6589e4..6a53aae528b87074a16d893f8e22170bce43b33c 100644 (file)
@@ -7,11 +7,11 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import com.vaadin.server.data.DataProvider;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
+import com.vaadin.server.data.DataProvider;
 import com.vaadin.server.data.ListDataProvider;
 import com.vaadin.server.data.Query;
 
@@ -29,7 +29,7 @@ public class ListDataProviderTest {
     @Test
     public void testListContainsAllData() {
         List<StrBean> list = new LinkedList<>(data);
-        dataProvider.fetch(new Query())
+        dataProvider.fetch(new Query<>())
                 .forEach(str -> assertTrue(
                         "Data provider contained values not in original data",
                         list.remove(str)));
@@ -42,7 +42,7 @@ public class ListDataProviderTest {
         Comparator<StrBean> comp = Comparator.comparing(StrBean::getValue)
                 .thenComparing(StrBean::getRandomNumber)
                 .thenComparing(StrBean::getId);
-        List<StrBean> list = dataProvider.sortingBy(comp).fetch(new Query())
+        List<StrBean> list = dataProvider.sortingBy(comp).fetch(new Query<>())
                 .collect(Collectors.toList());
 
         // First value in data is { Xyz, 10, 100 } which should be last in list
@@ -63,7 +63,7 @@ public class ListDataProviderTest {
     public void testDefatulSortWithSpecifiedPostSort() {
         Comparator<StrBean> comp = Comparator.comparing(StrBean::getValue)
                 .thenComparing(Comparator.comparing(StrBean::getId).reversed());
-        List<StrBean> list = dataProvider.sortingBy(comp).fetch(new Query())
+        List<StrBean> list = dataProvider.sortingBy(comp).fetch(new Query<>())
                 // The sort here should come e.g from a Component
                 .sorted(Comparator.comparing(StrBean::getRandomNumber))
                 .collect(Collectors.toList());
@@ -91,7 +91,7 @@ public class ListDataProviderTest {
     @Test
     public void testDefatulSortWithFunction() {
         List<StrBean> list = dataProvider.sortingBy(StrBean::getValue)
-                .fetch(new Query()).collect(Collectors.toList());
+                .fetch(new Query<>()).collect(Collectors.toList());
 
         Assert.assertEquals("Sorted data and original data sizes don't match",
                 data.size(), list.size());
@@ -108,44 +108,41 @@ public class ListDataProviderTest {
     @Test
     public void refreshAll_changeBeanInstance() {
         StrBean bean = new StrBean("foo", -1, hashCode());
-        Query query = new Query();
-        int size = dataProvider.size(query);
+        int size = dataProvider.size(new Query<>());
 
         data.set(0, bean);
         dataProvider.refreshAll();
 
-        List<StrBean> list = dataProvider.fetch(query)
+        List<StrBean> list = dataProvider.fetch(new 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, dataProvider.size(query));
+        Assert.assertEquals(size, dataProvider.size(new Query<>()));
     }
 
     @Test
     public void refreshAll_updateBean() {
-        Query query = new Query();
-        int size = dataProvider.size(query);
+        int size = dataProvider.size(new Query<>());
 
         StrBean bean = data.get(0);
         bean.setValue("foo");
         dataProvider.refreshAll();
 
-        List<StrBean> list = dataProvider.fetch(query)
+        List<StrBean> list = dataProvider.fetch(new Query<>())
                 .collect(Collectors.toList());
         StrBean first = list.get(0);
         Assert.assertEquals("foo", first.getValue());
 
-        Assert.assertEquals(size, dataProvider.size(query));
+        Assert.assertEquals(size, dataProvider.size(new Query<>()));
     }
 
     @Test
     public void refreshAll_sortingBy_changeBeanInstance() {
         StrBean bean = new StrBean("foo", -1, hashCode());
-        Query query = new Query();
-        int size = dataProvider.size(query);
+        int size = dataProvider.size(new Query<>());
 
         data.set(0, bean);
 
@@ -153,43 +150,70 @@ public class ListDataProviderTest {
                 .sortingBy(Comparator.comparing(StrBean::getId));
         dSource.refreshAll();
 
-        List<StrBean> list = dSource.fetch(query).collect(Collectors.toList());
+        List<StrBean> list = dSource.fetch(new 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, dataProvider.size(query));
+        Assert.assertEquals(size, dataProvider.size(new Query<>()));
     }
 
     @Test
     public void refreshAll_addBeanInstance() {
         StrBean bean = new StrBean("foo", -1, hashCode());
 
-        Query query = new Query();
-        int size = dataProvider.size(query);
+        int size = dataProvider.size(new Query<>());
 
         data.add(0, bean);
         dataProvider.refreshAll();
 
-        List<StrBean> list = dataProvider.fetch(query)
+        List<StrBean> list = dataProvider.fetch(new 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, dataProvider.size(query));
+        Assert.assertEquals(size + 1, dataProvider.size(new Query<>()));
     }
 
     @Test
     public void refreshAll_removeBeanInstance() {
-        Query query = new Query();
-        int size = dataProvider.size(query);
+        int size = dataProvider.size(new Query<>());
 
         data.remove(0);
         dataProvider.refreshAll();
 
-        Assert.assertEquals(size - 1, dataProvider.size(query));
+        Assert.assertEquals(size - 1, dataProvider.size(new Query<>()));
+    }
+
+    @Test
+    public void filteringListDataProvider_convertFilter() {
+        DataProvider<StrBean, String> strFilterDataProvider = dataProvider
+                .convertFilter(
+                        text -> strBean -> strBean.getValue().contains(text));
+        Assert.assertEquals("Only one item should match 'Xyz'", 1,
+                strFilterDataProvider.size(new Query<>("Xyz")));
+        Assert.assertEquals("No item should match 'Zyx'", 0,
+                strFilterDataProvider.size(new Query<>("Zyx")));
+        Assert.assertEquals("Unexpected number of matches for 'Foo'", 36,
+                strFilterDataProvider.size(new Query<>("Foo")));
+
+        Assert.assertEquals("No items should've been filtered out", data.size(),
+                strFilterDataProvider.size(new Query<>()));
+    }
+
+    @Test
+    public void filteringListDataProvider_defaultFilterType() {
+        Assert.assertEquals("Only one item should match 'Xyz'", 1,
+                dataProvider.size(new Query<>(
+                        strBean -> strBean.getValue().contains("Xyz"))));
+        Assert.assertEquals("No item should match 'Zyx'", 0, dataProvider.size(
+                new Query<>(strBean -> strBean.getValue().contains("Zyx"))));
+        Assert.assertEquals("Unexpected number of matches for 'Foo'", 36,
+                dataProvider.size(new Query<>(
+                        strBean -> strBean.getValue().contains("Foo"))));
     }
 }
index 0ee679e891d732b62073846e9e902d0cd64d4042..fdcb15c1f0c6fa623bac4c8320ee5aa77e564ad0 100644 (file)
@@ -22,6 +22,7 @@ import java.util.stream.Collectors;
 
 import com.vaadin.server.VaadinRequest;
 import com.vaadin.server.data.ListDataProvider;
+import com.vaadin.server.data.Query;
 import com.vaadin.tests.components.AbstractTestUIWithLog;
 import com.vaadin.ui.Button;
 import com.vaadin.ui.ListSelect;
@@ -50,7 +51,7 @@ public class ListSelectAddRemoveItems extends AbstractTestUIWithLog {
         }));
 
         addComponent(new Button("Add first", event -> {
-            List<String> list = dataProvider.fetch(null)
+            List<String> list = dataProvider.fetch(new Query<>())
                     .collect(Collectors.toList());
             list.add(0, "first");
             dataProvider = new ListDataProvider<>(list);
@@ -59,7 +60,7 @@ public class ListSelectAddRemoveItems extends AbstractTestUIWithLog {
         }));
 
         addComponent(new Button("Add middle", event -> {
-            List<String> list = dataProvider.fetch(null)
+            List<String> list = dataProvider.fetch(new Query<>())
                     .collect(Collectors.toList());
             list.add(list.size() / 2, "middle");
             dataProvider = new ListDataProvider<>(list);
@@ -68,7 +69,7 @@ public class ListSelectAddRemoveItems extends AbstractTestUIWithLog {
         }));
 
         addComponent(new Button("Add last", event -> {
-            List<String> list = dataProvider.fetch(null)
+            List<String> list = dataProvider.fetch(new Query<>())
                     .collect(Collectors.toList());
             list.add("last");
             dataProvider = new ListDataProvider<>(list);
@@ -77,7 +78,7 @@ public class ListSelectAddRemoveItems extends AbstractTestUIWithLog {
         }));
 
         addComponent(new Button("Swap", event -> {
-            List<String> list = dataProvider.fetch(null)
+            List<String> list = dataProvider.fetch(new Query<>())
                     .collect(Collectors.toList());
             Collections.swap(list, 0, list.size() - 1);
             dataProvider = new ListDataProvider<>(list);
@@ -87,7 +88,7 @@ public class ListSelectAddRemoveItems extends AbstractTestUIWithLog {
         }));
 
         addComponent(new Button("Remove first", event -> {
-            List<String> list = dataProvider.fetch(null)
+            List<String> list = dataProvider.fetch(new Query<>())
                     .collect(Collectors.toList());
             list.remove(0);
 
@@ -98,7 +99,7 @@ public class ListSelectAddRemoveItems extends AbstractTestUIWithLog {
         }));
 
         addComponent(new Button("Remove middle", event -> {
-            List<String> list = dataProvider.fetch(null)
+            List<String> list = dataProvider.fetch(new Query<>())
                     .collect(Collectors.toList());
             list.remove(list.size() / 2);
             dataProvider = new ListDataProvider<>(list);
@@ -107,7 +108,7 @@ public class ListSelectAddRemoveItems extends AbstractTestUIWithLog {
         }));
 
         addComponent(new Button("Remove last", event -> {
-            List<String> list = dataProvider.fetch(null)
+            List<String> list = dataProvider.fetch(new Query<>())
                     .collect(Collectors.toList());
             list.remove(list.size() - 1);
 
@@ -121,7 +122,8 @@ public class ListSelectAddRemoveItems extends AbstractTestUIWithLog {
 
     private void logContainer() {
         StringBuilder b = new StringBuilder();
-        List<String> list = dataProvider.fetch(null).collect(Collectors.toList());
+        List<String> list = dataProvider.fetch(new Query<>())
+                .collect(Collectors.toList());
         for (int i = 0; i < list.size(); i++) {
             Object id = list.get(i);
             if (i != 0) {