]> source.dussan.org Git - vaadin-framework.git/commitdiff
Allow setting filters directly in ListDataProvider (#8267)
authorLeif Åstrand <legioth@gmail.com>
Thu, 19 Jan 2017 06:56:06 +0000 (08:56 +0200)
committerDenis <denis@vaadin.com>
Thu, 19 Jan 2017 06:56:06 +0000 (08:56 +0200)
* Allow setting filters directly in ListDataProvider

This is one of many steps towards #8245

server/src/main/java/com/vaadin/data/provider/ListDataProvider.java
server/src/test/java/com/vaadin/data/provider/DataProviderTestBase.java
server/src/test/java/com/vaadin/data/provider/ListDataProviderTest.java

index e1c8e8867c148df1f05cc0b75340c04f21668ab3..3d021dc0e658fd325a1e2497c0ef2b57044417af 100644 (file)
@@ -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,
index ccbb0b0c1a1db2f4e32295cbe801fe349159a3b9..9d1115d2afce0a39239e4d57beec243c9cebffe6 100644 (file)
@@ -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();
+    }
 }
index ad7c3b1f466ccbd95eedfa4cde472bbcb1f98090..e10c071c41676efb8245cba40df9761ab9565d92 100644 (file)
@@ -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) {