diff options
author | Leif Åstrand <legioth@gmail.com> | 2017-01-19 08:56:06 +0200 |
---|---|---|
committer | Denis <denis@vaadin.com> | 2017-01-19 08:56:06 +0200 |
commit | 67d69c8d5d6bb066c792cfaa65f2b68f6ec8cc2b (patch) | |
tree | 731f29b398ea52cd31677dcf97645169ac901c05 | |
parent | c99a911278fcf058679aefc841c25a3cdf59d74a (diff) | |
download | vaadin-framework-67d69c8d5d6bb066c792cfaa65f2b68f6ec8cc2b.tar.gz vaadin-framework-67d69c8d5d6bb066c792cfaa65f2b68f6ec8cc2b.zip |
Allow setting filters directly in ListDataProvider (#8267)
* Allow setting filters directly in ListDataProvider
This is one of many steps towards #8245
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<T> implements AppendableFilterDataProvider<T, SerializablePredicate<T>> { private SerializableComparator<T> sortOrder = null; + + private SerializablePredicate<T> filter; + private final Collection<T> backend; /** @@ -58,8 +61,7 @@ public class ListDataProvider<T> @Override public Stream<T> fetch(Query<T, SerializablePredicate<T>> query) { - Stream<T> stream = backend.stream() - .filter(t -> query.getFilter().orElse(p -> true).test(t)); + Stream<T> stream = getFilteredStream(query); Optional<Comparator<T>> comparing = Stream .of(query.getInMemorySorting(), sortOrder) @@ -80,9 +82,22 @@ public class ListDataProvider<T> @Override public int size(Query<T, SerializablePredicate<T>> query) { - return (int) backend.stream() - .filter(t -> query.getFilter().orElse(p -> true).test(t)) - .count(); + return (int) getFilteredStream(query).count(); + } + + private Stream<T> getFilteredStream( + Query<T, SerializablePredicate<T>> query) { + Stream<T> 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<T> 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 <code>null</code> to remove any set + * filters + */ + public void setFilter(SerializablePredicate<T> 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 <code>null</code> + */ + public void addFilter(SerializablePredicate<T> filter) { + Objects.requireNonNull(filter, "Filter cannot be null"); + + if (this.filter == null) { + setFilter(filter); + } else { + SerializablePredicate<T> 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 + * <code>null</code> + * @param valueFilter + * filter for testing the property value, not <code>null</code> + */ + public <V> void setFilter(ValueProvider<T, V> valueProvider, + SerializablePredicate<V> 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 + * <code>null</code> + * @param valueFilter + * filter for testing the property value, not <code>null</code> + */ + public <V> void addFilter(ValueProvider<T, V> valueProvider, + SerializablePredicate<V> valueFilter) { + Objects.requireNonNull(valueProvider, "Value provider cannot be null"); + Objects.requireNonNull(valueFilter, "Value filter cannot be null"); + + addFilter(createValueProviderFilter(valueProvider, valueFilter)); + } + + private static <T, V> SerializablePredicate<T> createValueProviderFilter( + ValueProvider<T, V> valueProvider, + SerializablePredicate<V> 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 + * <code>null</code> + * @param requiredValue + * the value that the property must have for the filter to pass + */ + public <V> void setFilterByValue(ValueProvider<T, V> 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 + * <code>null</code> + * @param requiredValue + * the value that the property must have for the filter to pass + */ + public <V> void addFilterByValue(ValueProvider<T, V> valueProvider, + V requiredValue) { + addFilter(createEqualsFilter(valueProvider, requiredValue)); + } + + private static <T, V> SerializablePredicate<T> createEqualsFilter( + ValueProvider<T, V> valueProvider, V requiredValue) { + Objects.requireNonNull(valueProvider, "Value provider cannot be null"); + + return item -> Objects.equals(valueProvider.apply(item), requiredValue); + } + @Override public SerializablePredicate<T> combineFilters( SerializablePredicate<T> 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<D extends DataProvider<StrBean, Seria Assert.assertEquals("Unexpected number of matches for 'Foo'", 36, dataProvider.size(new Query<>(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<String>> sortOrder, Comparator<StrBean> comp) { |