From 67d69c8d5d6bb066c792cfaa65f2b68f6ec8cc2b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Leif=20=C3=85strand?= Date: Thu, 19 Jan 2017 08:56:06 +0200 Subject: [PATCH] Allow setting filters directly in ListDataProvider (#8267) * Allow setting filters directly in ListDataProvider This is one of many steps towards #8245 --- .../data/provider/ListDataProvider.java | 169 +++++++++++++++++- .../data/provider/DataProviderTestBase.java | 4 + .../data/provider/ListDataProviderTest.java | 112 ++++++++++++ 3 files changed, 280 insertions(+), 5 deletions(-) diff --git a/server/src/main/java/com/vaadin/data/provider/ListDataProvider.java b/server/src/main/java/com/vaadin/data/provider/ListDataProvider.java index e1c8e8867c..3d021dc0e6 100644 --- a/server/src/main/java/com/vaadin/data/provider/ListDataProvider.java +++ b/server/src/main/java/com/vaadin/data/provider/ListDataProvider.java @@ -38,6 +38,9 @@ public class ListDataProvider implements AppendableFilterDataProvider> { private SerializableComparator sortOrder = null; + + private SerializablePredicate filter; + private final Collection backend; /** @@ -58,8 +61,7 @@ public class ListDataProvider @Override public Stream fetch(Query> query) { - Stream stream = backend.stream() - .filter(t -> query.getFilter().orElse(p -> true).test(t)); + Stream stream = getFilteredStream(query); Optional> comparing = Stream .of(query.getInMemorySorting(), sortOrder) @@ -80,9 +82,22 @@ public class ListDataProvider @Override public int size(Query> query) { - return (int) backend.stream() - .filter(t -> query.getFilter().orElse(p -> true).test(t)) - .count(); + return (int) getFilteredStream(query).count(); + } + + private Stream getFilteredStream( + Query> query) { + Stream stream = backend.stream(); + + // Apply our own filters first so that query filters never see the items + // that would already have been filtered out + if (filter != null) { + stream = stream.filter(filter); + } + + stream = query.getFilter().map(stream::filter).orElse(stream); + + return stream; } /** @@ -209,6 +224,150 @@ public class ListDataProvider return comparator; } + /** + * Sets a filter to be applied to all queries. The filter replaces any + * filter that has been set or added previously. + * + * @see #setFilter(ValueProvider, SerializablePredicate) + * @see #setFilterByValue(ValueProvider, Object) + * @see #addFilter(SerializablePredicate) + * + * @param filter + * the filter to set, or null to remove any set + * filters + */ + public void setFilter(SerializablePredicate filter) { + this.filter = filter; + refreshAll(); + } + + /** + * Adds a filter to be applied to all queries. The filter will be used in + * addition to any filter that has been set or added previously. + * + * @see #addFilter(ValueProvider, SerializablePredicate) + * @see #addFilterByValue(ValueProvider, Object) + * @see #setFilter(SerializablePredicate) + * + * @param filter + * the filter to add, not null + */ + public void addFilter(SerializablePredicate filter) { + Objects.requireNonNull(filter, "Filter cannot be null"); + + if (this.filter == null) { + setFilter(filter); + } else { + SerializablePredicate oldFilter = this.filter; + setFilter(item -> oldFilter.test(item) && filter.test(item)); + } + } + + /** + * Removes any filter that has been set or added previously. + * + * @see #setFilter(SerializablePredicate) + */ + public void clearFilters() { + setFilter(null); + } + + /** + * Sets a filter for an item property. The filter replaces any filter that + * has been set or added previously. + * + * @see #setFilter(SerializablePredicate) + * @see #setFilterByValue(ValueProvider, Object) + * @see #addFilter(ValueProvider, SerializablePredicate) + * + * @param valueProvider + * value provider that gets the property value, not + * null + * @param valueFilter + * filter for testing the property value, not null + */ + public void setFilter(ValueProvider valueProvider, + SerializablePredicate valueFilter) { + setFilter(createValueProviderFilter(valueProvider, valueFilter)); + } + + /** + * Adds a filter for an item property. The filter will be used in addition + * to any filter that has been set or added previously. + * + * @see #addFilter(SerializablePredicate) + * @see #addFilterByValue(ValueProvider, Object) + * @see #setFilter(ValueProvider, SerializablePredicate) + * + * @param valueProvider + * value provider that gets the property value, not + * null + * @param valueFilter + * filter for testing the property value, not null + */ + public void addFilter(ValueProvider valueProvider, + SerializablePredicate valueFilter) { + Objects.requireNonNull(valueProvider, "Value provider cannot be null"); + Objects.requireNonNull(valueFilter, "Value filter cannot be null"); + + addFilter(createValueProviderFilter(valueProvider, valueFilter)); + } + + private static SerializablePredicate createValueProviderFilter( + ValueProvider valueProvider, + SerializablePredicate valueFilter) { + return item -> valueFilter.test(valueProvider.apply(item)); + } + + /** + * Sets a filter that requires an item property to have a specific value. + * The property value and the provided value are compared using + * {@link Object#equals(Object)}. The filter replaces any filter that has + * been set or added previously. + * + * @see #setFilter(SerializablePredicate) + * @see #setFilter(ValueProvider, SerializablePredicate) + * @see #addFilterByValue(ValueProvider, Object) + * + * @param valueProvider + * value provider that gets the property value, not + * null + * @param requiredValue + * the value that the property must have for the filter to pass + */ + public void setFilterByValue(ValueProvider valueProvider, + V requiredValue) { + setFilter(createEqualsFilter(valueProvider, requiredValue)); + } + + /** + * Adds a filter that requires an item property to have a specific value. + * The property value and the provided value are compared using + * {@link Object#equals(Object)}.The filter will be used in addition to any + * filter that has been set or added previously. + * + * @see #setFilterByValue(ValueProvider, Object) + * @see #addFilter(SerializablePredicate) + * @see #addFilter(ValueProvider, SerializablePredicate) + * + * @param valueProvider + * value provider that gets the property value, not + * null + * @param requiredValue + * the value that the property must have for the filter to pass + */ + public void addFilterByValue(ValueProvider valueProvider, + V requiredValue) { + addFilter(createEqualsFilter(valueProvider, requiredValue)); + } + + private static SerializablePredicate createEqualsFilter( + ValueProvider valueProvider, V requiredValue) { + Objects.requireNonNull(valueProvider, "Value provider cannot be null"); + + return item -> Objects.equals(valueProvider.apply(item), requiredValue); + } + @Override public SerializablePredicate combineFilters( SerializablePredicate filter1, diff --git a/server/src/test/java/com/vaadin/data/provider/DataProviderTestBase.java b/server/src/test/java/com/vaadin/data/provider/DataProviderTestBase.java index ccbb0b0c1a..9d1115d2af 100644 --- a/server/src/test/java/com/vaadin/data/provider/DataProviderTestBase.java +++ b/server/src/test/java/com/vaadin/data/provider/DataProviderTestBase.java @@ -186,4 +186,8 @@ public abstract class DataProviderTestBase(fooFilter))); } + + protected long sizeWithUnfilteredQuery() { + return dataProvider.fetch(new Query<>()).count(); + } } diff --git a/server/src/test/java/com/vaadin/data/provider/ListDataProviderTest.java b/server/src/test/java/com/vaadin/data/provider/ListDataProviderTest.java index ad7c3b1f46..e10c071c41 100644 --- a/server/src/test/java/com/vaadin/data/provider/ListDataProviderTest.java +++ b/server/src/test/java/com/vaadin/data/provider/ListDataProviderTest.java @@ -120,6 +120,118 @@ public class ListDataProviderTest Assert.assertEquals(new StrBean("Foo", 91, 2), threeFirstItems.get(2)); } + @Test + public void setFilter() { + dataProvider.setFilter(item -> item.getValue().equals("Foo")); + + Assert.assertEquals(36, sizeWithUnfilteredQuery()); + + dataProvider.setFilter(item -> !item.getValue().equals("Foo")); + + Assert.assertEquals( + "Previous filter should be reset when setting a new one", 64, + sizeWithUnfilteredQuery()); + + dataProvider.setFilter(null); + + Assert.assertEquals("Setting filter to null should remove all filters", + 100, sizeWithUnfilteredQuery()); + } + + @Test + public void setFilter_valueProvider() { + dataProvider.setFilter(StrBean::getValue, "Foo"::equals); + + Assert.assertEquals(36, sizeWithUnfilteredQuery()); + + dataProvider.setFilter(StrBean::getValue, + value -> !value.equals("Foo")); + + Assert.assertEquals( + "Previous filter should be reset when setting a new one", 64, + sizeWithUnfilteredQuery()); + } + + @Test + public void setFilterEquals() { + dataProvider.setFilterByValue(StrBean::getValue, "Foo"); + + Assert.assertEquals(36, sizeWithUnfilteredQuery()); + + dataProvider.setFilterByValue(StrBean::getValue, "Bar"); + + Assert.assertEquals(23, sizeWithUnfilteredQuery()); + } + + @Test + public void addFilter_withPreviousFilter() { + dataProvider.setFilterByValue(StrBean::getValue, "Foo"); + + dataProvider.addFilter(item -> item.getId() > 50); + + Assert.assertEquals("Both filters should be used", 17, + sizeWithUnfilteredQuery()); + } + + @Test + public void addFilter_noPreviousFilter() { + dataProvider.addFilter(item -> item.getId() > 50); + + Assert.assertEquals(48, sizeWithUnfilteredQuery()); + } + + @Test + public void addFilter_valueProvider() { + dataProvider.setFilter(item -> item.getId() > 50); + + dataProvider.addFilter(StrBean::getValue, "Foo"::equals); + + Assert.assertEquals("Both filters should be used", 17, + sizeWithUnfilteredQuery()); + } + + @Test + public void addFilterEquals() { + dataProvider.setFilter(item -> item.getId() > 50); + + dataProvider.addFilterByValue(StrBean::getValue, "Foo"); + + Assert.assertEquals("Both filters should be used", 17, + sizeWithUnfilteredQuery()); + } + + @Test + public void addFilter_firstAddedUsedFirst() { + dataProvider.addFilter(item -> false); + dataProvider.addFilter(item -> { + Assert.fail("This filter should never be invoked"); + return true; + }); + + Assert.assertEquals(0, sizeWithUnfilteredQuery()); + } + + @Test + public void combineProviderAndQueryFilters() { + dataProvider.addFilterByValue(StrBean::getValue, "Foo"); + + int size = dataProvider.size(new Query<>(item -> item.getId() > 50)); + + Assert.assertEquals("Both filters should be used", 17, size); + } + + @Test + public void providerFilterBeforeQueryFilter() { + dataProvider.setFilter(item -> false); + + int size = dataProvider.size(new Query<>(item -> { + Assert.fail("This filter should never be invoked"); + return true; + })); + + Assert.assertEquals(0, size); + } + @Override protected void setSortOrder(List> sortOrder, Comparator comp) { -- 2.39.5