aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTeemu Suo-Anttila <teemusa@vaadin.com>2016-11-21 17:35:19 +0200
committerTeemu Suo-Anttila <teemusa@vaadin.com>2016-11-21 18:57:51 +0200
commitc75f71c74a53c0ca538da4785589ffb756dc0f2e (patch)
tree1cc212d7e29324e5cadd19d981e93009ff2bb76a
parenta8f5b1fb08b4eba5918aadb936030842aa9d9aed (diff)
downloadvaadin-framework-c75f71c74a53c0ca538da4785589ffb756dc0f2e.tar.gz
vaadin-framework-c75f71c74a53c0ca538da4785589ffb756dc0f2e.zip
Add DataProvider.convertFilter, fix ListDataProvider filtering
Change-Id: Ic90ae83acf5d77aa9b0f485dff4e55bba5296fa7
-rw-r--r--server/src/main/java/com/vaadin/server/data/DataCommunicator.java15
-rw-r--r--server/src/main/java/com/vaadin/server/data/DataProvider.java34
-rw-r--r--server/src/main/java/com/vaadin/server/data/FilteringDataProviderWrapper.java75
-rw-r--r--server/src/main/java/com/vaadin/server/data/ListDataProvider.java17
-rw-r--r--server/src/main/java/com/vaadin/server/data/Query.java5
-rw-r--r--server/src/test/java/com/vaadin/server/data/provider/ListDataProviderTest.java72
-rw-r--r--uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectAddRemoveItems.java18
7 files changed, 183 insertions, 53 deletions
diff --git a/server/src/main/java/com/vaadin/server/data/DataCommunicator.java b/server/src/main/java/com/vaadin/server/data/DataCommunicator.java
index fc66185aee..852c583a56 100644
--- a/server/src/main/java/com/vaadin/server/data/DataCommunicator.java
+++ b/server/src/main/java/com/vaadin/server/data/DataCommunicator.java
@@ -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);
}
diff --git a/server/src/main/java/com/vaadin/server/data/DataProvider.java b/server/src/main/java/com/vaadin/server/data/DataProvider.java
index 441a57b559..19c657be70 100644
--- a/server/src/main/java/com/vaadin/server/data/DataProvider.java
+++ b/server/src/main/java/com/vaadin/server/data/DataProvider.java
@@ -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;
/**
@@ -89,6 +90,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
* the contents in the Collection.
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
index 0000000000..10384a9c9c
--- /dev/null
+++ b/server/src/main/java/com/vaadin/server/data/FilteringDataProviderWrapper.java
@@ -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)));
+ }
+
+}
diff --git a/server/src/main/java/com/vaadin/server/data/ListDataProvider.java b/server/src/main/java/com/vaadin/server/data/ListDataProvider.java
index a67ce42eab..0184a2e37b 100644
--- a/server/src/main/java/com/vaadin/server/data/ListDataProvider.java
+++ b/server/src/main/java/com/vaadin/server/data/ListDataProvider.java
@@ -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();
}
}
diff --git a/server/src/main/java/com/vaadin/server/data/Query.java b/server/src/main/java/com/vaadin/server/data/Query.java
index 3762039a72..3aeaac6acc 100644
--- a/server/src/main/java/com/vaadin/server/data/Query.java
+++ b/server/src/main/java/com/vaadin/server/data/Query.java
@@ -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) {
diff --git a/server/src/test/java/com/vaadin/server/data/provider/ListDataProviderTest.java b/server/src/test/java/com/vaadin/server/data/provider/ListDataProviderTest.java
index 37b4214a45..6a53aae528 100644
--- a/server/src/test/java/com/vaadin/server/data/provider/ListDataProviderTest.java
+++ b/server/src/test/java/com/vaadin/server/data/provider/ListDataProviderTest.java
@@ -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"))));
}
}
diff --git a/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectAddRemoveItems.java b/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectAddRemoveItems.java
index 0ee679e891..fdcb15c1f0 100644
--- a/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectAddRemoveItems.java
+++ b/uitest/src/main/java/com/vaadin/tests/components/listselect/ListSelectAddRemoveItems.java
@@ -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) {