diff options
author | Leif Åstrand <legioth@gmail.com> | 2017-01-20 10:34:33 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-01-20 10:34:33 +0200 |
commit | cb264dacfe74cba1266b54df8ce8c6ded1510004 (patch) | |
tree | def7d7988d42ce2add93391a002399a4ce2949ff /server/src | |
parent | 1fb40df8742791803e0a08328028bbbd67deb78b (diff) | |
download | vaadin-framework-cb264dacfe74cba1266b54df8ce8c6ded1510004.tar.gz vaadin-framework-cb264dacfe74cba1266b54df8ce8c6ded1510004.zip |
Add a data provider wrapper with a configurable filter (#8280)
* Add a data provider wrapper with a configurable filter
This is one of many steps towards #8245
Diffstat (limited to 'server/src')
8 files changed, 331 insertions, 20 deletions
diff --git a/server/src/main/java/com/vaadin/data/provider/BackEndDataProvider.java b/server/src/main/java/com/vaadin/data/provider/BackEndDataProvider.java index 96d5e54394..199d0b64fe 100644 --- a/server/src/main/java/com/vaadin/data/provider/BackEndDataProvider.java +++ b/server/src/main/java/com/vaadin/data/provider/BackEndDataProvider.java @@ -18,6 +18,8 @@ package com.vaadin.data.provider; import java.util.Collections; import java.util.List; +import com.vaadin.server.SerializableBiFunction; + /** * A data provider that lazy loads items from a back end. * @@ -71,4 +73,44 @@ public interface BackEndDataProvider<T, F> extends DataProvider<T, F> { default boolean isInMemory() { return false; } + + /** + * Wraps this data provider to create a data provider that supports + * programmatically setting a filter that will be combined with a filter + * provided through the query. + * + * @see #withConfigurableFilter() + * + * @param filterCombiner + * a callback for combining and the configured filter with the + * filter from the query to get a filter to pass to the wrapped + * provider. Will only be called if the query contains a filter. + * + * @return a data provider with a configurable filter, not <code>null</code> + */ + public default <C> ConfigurableFilterDataProvider<T, C, F> withConfigurableFilter( + SerializableBiFunction<F, C, F> filterCombiner) { + return new ConfigurableFilterDataProviderWrapper<T, C, F>(this) { + @Override + protected F combineFilters(F configuredFilter, C queryFilter) { + return filterCombiner.apply(configuredFilter, queryFilter); + } + }; + } + + /** + * Wraps this data provider to create a data provider that supports + * programmatically setting a filter but no filtering through the query. + * + * @see #withConfigurableFilter(SerializableBiFunction) + * + * @return a data provider with a configurable filter, not <code>null</code> + */ + public default ConfigurableFilterDataProvider<T, Void, F> withConfigurableFilter() { + return withConfigurableFilter((configuredFilter, queryFilter) -> { + assert queryFilter == null : "Filter from Void query must be null"; + + return configuredFilter; + }); + } } diff --git a/server/src/main/java/com/vaadin/data/provider/ConfigurableFilterDataProvider.java b/server/src/main/java/com/vaadin/data/provider/ConfigurableFilterDataProvider.java new file mode 100644 index 0000000000..8521e5498a --- /dev/null +++ b/server/src/main/java/com/vaadin/data/provider/ConfigurableFilterDataProvider.java @@ -0,0 +1,43 @@ +/* + * 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.data.provider; + +/** + * A data provider that supports programmatically setting a filter that will be + * applied to all queries. + * + * @author Vaadin Ltd + * + * @param <T> + * the data provider item type + * @param <Q> + * the query filter type + * @param <C> + * the configurable filter type + */ +public interface ConfigurableFilterDataProvider<T, Q, C> + extends DataProvider<T, Q> { + + /** + * Sets the filter to use for all queries handled by this data provider. + * + * @param filter + * the filter to set, or <code>null</code> to clear any + * previously set filter + */ + public void setFilter(C filter); + +} diff --git a/server/src/main/java/com/vaadin/data/provider/ConfigurableFilterDataProviderWrapper.java b/server/src/main/java/com/vaadin/data/provider/ConfigurableFilterDataProviderWrapper.java new file mode 100644 index 0000000000..c6256787ce --- /dev/null +++ b/server/src/main/java/com/vaadin/data/provider/ConfigurableFilterDataProviderWrapper.java @@ -0,0 +1,79 @@ +/* + * 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.data.provider; + +/** + * A configurable data provider that wraps another data provider by combining + * any filter from the component with the configured filter and passing that to + * the wrapped provider through the query. + * + * @author Vaadin Ltd + * + * @param <T> + * the data provider item type + * @param <Q> + * the query filter type + * @param <C> + * the configurable filter type + */ +public abstract class ConfigurableFilterDataProviderWrapper<T, Q, C> + extends DataProviderWrapper<T, Q, C> + implements ConfigurableFilterDataProvider<T, Q, C> { + + private C configuredFilter; + + /** + * Creates a new configurable filter data provider by wrapping an existing + * data provider. + * + * @param dataProvider + * the data provider to wrap, not <code>null</code> + */ + public ConfigurableFilterDataProviderWrapper( + DataProvider<T, C> dataProvider) { + super(dataProvider); + } + + @Override + protected C getFilter(Query<T, Q> query) { + return query.getFilter().map( + queryFilter -> combineFilters(configuredFilter, queryFilter)) + .orElse(configuredFilter); + } + + /** + * Combines the configured filter and the filter from the query into one + * filter instance that can be passed to the wrapped data provider. This + * method is called only if there is a query filter, otherwise the + * configured filter will be directly passed to the query. + * + * @param configuredFilter + * the filter that this data provider is configured to use, or + * <code>null</code> if no filter has been configured + * @param queryFilter + * the filter received through the query, not <code>null</code> + * @return a filter that combines the two provided queries, or + * <code>null</code> to not pass any filter to the wrapped data + * provider + */ + protected abstract C combineFilters(C configuredFilter, Q queryFilter); + + @Override + public void setFilter(C filter) { + this.configuredFilter = filter; + refreshAll(); + } +} diff --git a/server/src/main/java/com/vaadin/data/provider/DataProviderWrapper.java b/server/src/main/java/com/vaadin/data/provider/DataProviderWrapper.java index f792c8c255..b6f835ac42 100644 --- a/server/src/main/java/com/vaadin/data/provider/DataProviderWrapper.java +++ b/server/src/main/java/com/vaadin/data/provider/DataProviderWrapper.java @@ -15,6 +15,7 @@ */ package com.vaadin.data.provider; +import java.util.Objects; import java.util.stream.Stream; import com.vaadin.server.SerializableFunction; @@ -78,10 +79,11 @@ public abstract class DataProviderWrapper<T, F, M> * Constructs a filtering wrapper for a data provider. * * @param dataProvider - * the wrapped data provider + * the wrapped data provider, not <code>null</code> */ protected DataProviderWrapper(DataProvider<T, M> dataProvider) { - this.dataProvider = dataProvider; + this.dataProvider = Objects.requireNonNull(dataProvider, + "The wrapped data provider cannot be null."); } @Override 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 3d021dc0e6..470339bc2a 100644 --- a/server/src/main/java/com/vaadin/data/provider/ListDataProvider.java +++ b/server/src/main/java/com/vaadin/data/provider/ListDataProvider.java @@ -35,7 +35,8 @@ import com.vaadin.shared.data.sort.SortDirection; */ public class ListDataProvider<T> extends AbstractDataProvider<T, SerializablePredicate<T>> - implements AppendableFilterDataProvider<T, SerializablePredicate<T>> { + implements AppendableFilterDataProvider<T, SerializablePredicate<T>>, + ConfigurableFilterDataProvider<T, SerializablePredicate<T>, SerializablePredicate<T>> { private SerializableComparator<T> sortOrder = null; @@ -236,6 +237,7 @@ public class ListDataProvider<T> * the filter to set, or <code>null</code> to remove any set * filters */ + @Override public void setFilter(SerializablePredicate<T> filter) { this.filter = filter; refreshAll(); diff --git a/server/src/main/java/com/vaadin/server/SerializableBiFunction.java b/server/src/main/java/com/vaadin/server/SerializableBiFunction.java new file mode 100644 index 0000000000..04d082c283 --- /dev/null +++ b/server/src/main/java/com/vaadin/server/SerializableBiFunction.java @@ -0,0 +1,39 @@ +/* + * 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; + +import java.io.Serializable; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * A {@link BiFunction} that is also {@link Serializable}. + * + * @see Function + * @author Vaadin Ltd + * @since 8.0 + * @param <T> + * the type of the the first function parameter + * @param <U> + * the type of the the second function parameter + * @param <R> + * the type of the result of the function + */ +@FunctionalInterface +public interface SerializableBiFunction<T, U, R> + extends BiFunction<T, U, R>, Serializable { + // Only method inherited from BiFunction +} diff --git a/server/src/test/java/com/vaadin/data/provider/BackendDataProviderTest.java b/server/src/test/java/com/vaadin/data/provider/BackendDataProviderTest.java index 797f14957c..e2f5211d00 100644 --- a/server/src/test/java/com/vaadin/data/provider/BackendDataProviderTest.java +++ b/server/src/test/java/com/vaadin/data/provider/BackendDataProviderTest.java @@ -23,7 +23,7 @@ public class BackendDataProviderTest extends Comparator.comparing(StrBean::getRandomNumber)); } - private Comparator<StrBean> getComparator(SortOrder<String> so) { + private static Comparator<StrBean> getComparator(SortOrder<String> so) { Comparator<StrBean> comparator = propertyToComparatorMap .get(so.getSorted()); if (so.getDirection() == SortDirection.DESCENDING) { @@ -32,24 +32,32 @@ public class BackendDataProviderTest extends return comparator; } + public static class StrBeanBackEndDataProvider extends + CallbackDataProvider<StrBean, SerializablePredicate<StrBean>> { + + public StrBeanBackEndDataProvider(List<StrBean> data) { + super(query -> { + Stream<StrBean> stream = data.stream().filter( + t -> query.getFilter().orElse(s -> true).test(t)); + if (!query.getSortOrders().isEmpty()) { + Comparator<StrBean> sorting = query.getSortOrders().stream() + .map(BackendDataProviderTest::getComparator) + .reduce((c1, c2) -> c1.thenComparing(c2)).get(); + stream = stream.sorted(sorting); + } + List<StrBean> list = stream.skip(query.getOffset()) + .limit(query.getLimit()).collect(Collectors.toList()); + list.forEach(s -> System.err.println(s.toString())); + return list.stream(); + }, query -> (int) data.stream() + .filter(t -> query.getFilter().orElse(s -> true).test(t)) + .count()); + } + } + @Override protected BackEndDataProvider<StrBean, SerializablePredicate<StrBean>> createDataProvider() { - return dataProvider = new CallbackDataProvider<>(query -> { - Stream<StrBean> stream = data.stream() - .filter(t -> query.getFilter().orElse(s -> true).test(t)); - if (!query.getSortOrders().isEmpty()) { - Comparator<StrBean> sorting = query.getSortOrders().stream() - .map(this::getComparator) - .reduce((c1, c2) -> c1.thenComparing(c2)).get(); - stream = stream.sorted(sorting); - } - List<StrBean> list = stream.skip(query.getOffset()) - .limit(query.getLimit()).collect(Collectors.toList()); - list.forEach(s -> System.err.println(s.toString())); - return list.stream(); - }, query -> (int) data.stream() - .filter(t -> query.getFilter().orElse(s -> true).test(t)) - .count()); + return dataProvider = new StrBeanBackEndDataProvider(data); } @Override diff --git a/server/src/test/java/com/vaadin/data/provider/ConfigurableFilterDataProviderWrapperTest.java b/server/src/test/java/com/vaadin/data/provider/ConfigurableFilterDataProviderWrapperTest.java new file mode 100644 index 0000000000..115e4fd6bd --- /dev/null +++ b/server/src/test/java/com/vaadin/data/provider/ConfigurableFilterDataProviderWrapperTest.java @@ -0,0 +1,96 @@ +/* + * 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.data.provider; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.data.provider.BackendDataProviderTest.StrBeanBackEndDataProvider; +import com.vaadin.server.SerializablePredicate; + +public class ConfigurableFilterDataProviderWrapperTest { + private static SerializablePredicate<StrBean> xyzFilter = item -> item + .getValue().equals("Xyz"); + + private StrBeanBackEndDataProvider backEndProvider = new StrBeanBackEndDataProvider( + StrBean.generateRandomBeans(100)); + private ConfigurableFilterDataProvider<StrBean, Void, SerializablePredicate<StrBean>> configurableVoid = backEndProvider + .withConfigurableFilter(); + private ConfigurableFilterDataProvider<StrBean, SerializablePredicate<StrBean>, SerializablePredicate<StrBean>> configurablePredicate = backEndProvider + .withConfigurableFilter((configuredFilter, queryFilter) -> item -> { + if (configuredFilter != null && !configuredFilter.test(item)) { + return false; + } + + return queryFilter.test(item); + }); + + @Test + public void void_setFilter() { + configurableVoid.setFilter(xyzFilter); + + Assert.assertEquals("Set filter should be used", 1, + configurableVoid.size(new Query<>())); + + configurableVoid.setFilter(null); + + Assert.assertEquals("null filter should return all items", 100, + configurableVoid.size(new Query<>())); + } + + @Test(expected = Exception.class) + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void void_nonNullQueryFilter_throws() { + configurableVoid + .size((Query) new Query<StrBean, String>("invalid filter")); + } + + @Test + public void predicate_setFilter() { + configurablePredicate.setFilter(xyzFilter); + + Assert.assertEquals("Set filter should be used", 1, + configurablePredicate.size(new Query<>())); + + configurablePredicate.setFilter(null); + + Assert.assertEquals("null filter should return all items", 100, + configurablePredicate.size(new Query<>())); + } + + @Test + public void predicate_queryFilter() { + Assert.assertEquals("Query filter should be used", 1, + configurablePredicate.size(new Query<>(xyzFilter))); + + Assert.assertEquals("null query filter should return all items", 100, + configurablePredicate.size(new Query<>())); + } + + @Test + public void predicate_combinedFilters() { + configurablePredicate.setFilter(item -> item.getValue().equals("Foo")); + + Assert.assertEquals("Both filters should be used", 0, + configurablePredicate.size(new Query<>(xyzFilter))); + + configurablePredicate.setFilter(null); + + Assert.assertEquals("Only zyz filter should be used", 1, + configurablePredicate.size(new Query<>(xyzFilter))); + } + +} |