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;
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);
}
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);
}
}
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);
}
import java.util.Collection;
import java.util.stream.Stream;
+import com.vaadin.server.SerializableFunction;
import com.vaadin.shared.Registration;
/**
*/
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<Person, Predicate<Person>> dataProvider;
+ * // ComboBox uses String as the filter type
+ * DataProvider<Person, String> wrappedProvider = dataProvider
+ * .convertFilter(filterText -> {
+ * Predicate<Person> 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
--- /dev/null
+/*
+ * 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)));
+ }
+
+}
}
@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);
}
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();
}
}
* 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;
* @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) {
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;
@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)));
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
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());
@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());
@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);
.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"))));
}
}
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;
}));
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);
}));
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);
}));
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);
}));
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);
}));
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);
}));
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);
}));
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);
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) {