Skip to content

Commit

Permalink
Change BackEndDataProvider into an interface (#8268)
Browse files Browse the repository at this point in the history
* Change BackEndDataProvider into an interface

BackEndDataProvider is now an interface with methods for setting
sorting options based on SortOrder instances. 

AbstractBackEndDataProvider stores sorting options, combines them with
the sorting provided in the query and invokes its own abstract fetch and
size methods.

CallbackDataProvider implements a BackEndDataProvider based on two
lambdas.

This is one of many steps towards #8245
  • Loading branch information
Legioth authored and Denis committed Jan 19, 2017
1 parent 67d69c8 commit dafc831
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 90 deletions.
4 changes: 2 additions & 2 deletions documentation/datamodel/datamodel-providers.asciidoc
Expand Up @@ -215,7 +215,7 @@ The sorting options set through the component will be available through [interfa


[source, java] [source, java]
---- ----
DataProvider<Person, Void> dataProvider = new BackEndDataProvider<>( DataProvider<Person, Void> dataProvider = new CallbackDataProvider<>(
query -> { query -> {
List<PersonSort> sortOrders = new ArrayList<>(); List<PersonSort> sortOrders = new ArrayList<>();
for(SortOrder<String> queryOrder : query.getSortOrders()) { for(SortOrder<String> queryOrder : query.getSortOrders()) {
Expand Down Expand Up @@ -362,7 +362,7 @@ It would then look for a string to filter by in the query and pass it to the ser


[source, java] [source, java]
---- ----
DataProvider<Person, String> dataProvider = new BackEndDataProvider<>( DataProvider<Person, String> dataProvider = new CallbackDataProvider<>(
query -> { query -> {
// getFilter returns Optional<String> // getFilter returns Optional<String>
String filter = query.getFilter().orElse(null); String filter = query.getFilter().orElse(null);
Expand Down
@@ -0,0 +1,95 @@
/*
* 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 java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* Abstract base class for implementing back end data providers.
*
* @param <T>
* data provider data type
* @param <F>
* data provider filter type
*/
public abstract class AbstractBackEndDataProvider<T, F> extends
AbstractDataProvider<T, F> implements BackEndDataProvider<T, F> {

private List<SortOrder<String>> sortOrders = new ArrayList<>();

private Query<T, F> mixInSortOrders(Query<T, F> query) {
if (sortOrders.isEmpty()) {
return query;
}

Set<String> sortedPropertyNames = query.getSortOrders().stream()
.map(SortOrder::getSorted).collect(Collectors.toSet());

List<SortOrder<String>> combinedSortOrders = Stream
.concat(query.getSortOrders().stream(),
sortOrders.stream()
.filter(order -> !sortedPropertyNames
.contains(order.getSorted())))
.collect(Collectors.toList());

return new Query<>(query.getOffset(), query.getLimit(),
combinedSortOrders, query.getInMemorySorting(),
query.getFilter().orElse(null));
}

@Override
public Stream<T> fetch(Query<T, F> query) {
return fetchFromBackEnd(mixInSortOrders(query));
}

@Override
public int size(Query<T, F> query) {
return sizeInBackEnd(mixInSortOrders(query));
}

/**
* Fetches data from the back end using the given query.
*
* @param query
* the query that defines sorting, filtering and paging for
* fetching the data
* @return a stream of items matching the query
*/
protected abstract Stream<T> fetchFromBackEnd(Query<T, F> query);

/**
* Counts the number of items available in the back end.
*
* @param query
* the query that defines filtering to be used for counting the
* number of items
* @return the number of available items
*/
protected abstract int sizeInBackEnd(Query<T, F> query);

@Override
public void setSortOrders(List<SortOrder<String>> sortOrders) {
this.sortOrders = Objects.requireNonNull(sortOrders,
"Sort orders cannot be null");
refreshAll();
}

}
Expand Up @@ -15,75 +15,18 @@
*/ */
package com.vaadin.data.provider; package com.vaadin.data.provider;


import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.vaadin.server.SerializableFunction;
import com.vaadin.server.SerializableToIntFunction;


/** /**
* A {@link DataProvider} for any back end. * A data provider that lazy loads items from a back end.
* *
* @param <T> * @param <T>
* data provider data type * data provider data type
* @param <F> * @param <F>
* data provider filter type * data provider filter type
*/ */
public class BackEndDataProvider<T, F> extends AbstractDataProvider<T, F> { public interface BackEndDataProvider<T, F> extends DataProvider<T, F> {

private List<SortOrder<String>> sortOrders = new ArrayList<>();

private final SerializableFunction<Query<T, F>, Stream<T>> request;
private final SerializableToIntFunction<Query<T, F>> sizeCallback;

/**
* Constructs a new DataProvider to request data from an arbitrary back end
* request function.
*
* @param request
* function that requests data from back end based on query
* @param sizeCallback
* function that return the amount of data in back end for query
*/
public BackEndDataProvider(
SerializableFunction<Query<T, F>, Stream<T>> request,
SerializableToIntFunction<Query<T, F>> sizeCallback) {
Objects.requireNonNull(request, "Request function can't be null");
Objects.requireNonNull(sizeCallback, "Size callback can't be null");
this.request = request;
this.sizeCallback = sizeCallback;
}

@Override
public Stream<T> fetch(Query<T, F> query) {
return request.apply(mixInSortOrders(query));
}

@Override
public int size(Query<T, F> query) {
return sizeCallback.applyAsInt(mixInSortOrders(query));
}

private Query<T, F> mixInSortOrders(Query<T, F> query) {
Set<String> sortedPropertyNames = query.getSortOrders().stream()
.map(SortOrder::getSorted).collect(Collectors.toSet());

List<SortOrder<String>> combinedSortOrders = Stream
.concat(query.getSortOrders().stream(),
sortOrders.stream()
.filter(order -> !sortedPropertyNames
.contains(order.getSorted())))
.collect(Collectors.toList());

return new Query<>(query.getOffset(), query.getLimit(),
combinedSortOrders, query.getInMemorySorting(),
query.getFilter().orElse(null));
}


/** /**
* Sets a list of sort orders to use as the default sorting for this data * Sets a list of sort orders to use as the default sorting for this data
Expand All @@ -99,11 +42,7 @@ private Query<T, F> mixInSortOrders(Query<T, F> query) {
* @param sortOrders * @param sortOrders
* a list of sort orders to set, not <code>null</code> * a list of sort orders to set, not <code>null</code>
*/ */
public void setSortOrders(List<SortOrder<String>> sortOrders) { void setSortOrders(List<SortOrder<String>> sortOrders);
this.sortOrders = Objects.requireNonNull(sortOrders,
"Sort orders cannot be null");
refreshAll();
}


/** /**
* Sets a single sort order to use as the default sorting for this data * Sets a single sort order to use as the default sorting for this data
Expand All @@ -120,7 +59,7 @@ public void setSortOrders(List<SortOrder<String>> sortOrders) {
* a sort order to set, or <code>null</code> to clear any * a sort order to set, or <code>null</code> to clear any
* previously set sort orders * previously set sort orders
*/ */
public void setSortOrder(SortOrder<String> sortOrder) { default void setSortOrder(SortOrder<String> sortOrder) {
if (sortOrder == null) { if (sortOrder == null) {
setSortOrders(Collections.emptyList()); setSortOrders(Collections.emptyList());
} else { } else {
Expand All @@ -129,8 +68,7 @@ public void setSortOrder(SortOrder<String> sortOrder) {
} }


@Override @Override
public boolean isInMemory() { default boolean isInMemory() {
return false; return false;
} }

} }
@@ -0,0 +1,69 @@
/*
* 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 java.util.Objects;
import java.util.stream.Stream;

import com.vaadin.server.SerializableFunction;
import com.vaadin.server.SerializableToIntFunction;

/**
* Data provider that uses one callback for fetching items from a back end and
* another callback for counting the number of available items.
*
* @author Vaadin Ltd
*
* @param <T>
* data provider data type
* @param <F>
* data provider filter type
*/
public class CallbackDataProvider<T, F>
extends AbstractBackEndDataProvider<T, F> {
private final SerializableFunction<Query<T, F>, Stream<T>> fetchCallback;
private final SerializableToIntFunction<Query<T, F>> sizeCallback;

/**
* Constructs a new DataProvider to request data using callbacks for
* fetching and counting items in the back end.
*
* @param fetchCallback
* function that returns a stream of items from the back end for
* a query
* @param sizeCallback
* function that return the number of items in the back end for a
* query
*/
public CallbackDataProvider(
SerializableFunction<Query<T, F>, Stream<T>> fetchCallback,
SerializableToIntFunction<Query<T, F>> sizeCallback) {
Objects.requireNonNull(fetchCallback, "Request function can't be null");
Objects.requireNonNull(sizeCallback, "Size callback can't be null");
this.fetchCallback = fetchCallback;
this.sizeCallback = sizeCallback;
}

@Override
public Stream<T> fetchFromBackEnd(Query<T, F> query) {
return fetchCallback.apply(query);
}

@Override
protected int sizeInBackEnd(Query<T, F> query) {
return sizeCallback.applyAsInt(query);
}
}
Expand Up @@ -85,9 +85,9 @@ public void dropRows(JsonArray keys) {
* <p> * <p>
* When the {@link DataCommunicator} is pushing new data to the client-side * When the {@link DataCommunicator} is pushing new data to the client-side
* via {@link DataCommunicator#pushData(int, Stream)}, * via {@link DataCommunicator#pushData(int, Stream)},
* {@link #addActiveData(Stream)} and {@link #cleanUp(Stream)} are * {@link #addActiveData(Stream)} and {@link #cleanUp(Stream)} are called
* called with the same parameter. In the clean up method any dropped data * with the same parameter. In the clean up method any dropped data objects
* objects that are not in the given collection will be cleaned up and * that are not in the given collection will be cleaned up and
* {@link DataGenerator#destroyData(Object)} will be called for them. * {@link DataGenerator#destroyData(Object)} will be called for them.
*/ */
protected class ActiveDataHandler protected class ActiveDataHandler
Expand Down Expand Up @@ -190,8 +190,8 @@ public void destroyAllData() {
private final ActiveDataHandler handler = new ActiveDataHandler(); private final ActiveDataHandler handler = new ActiveDataHandler();


/** Empty default data provider */ /** Empty default data provider */
private DataProvider<T, F> dataProvider = new BackEndDataProvider<>( private DataProvider<T, F> dataProvider = new CallbackDataProvider<>(
q -> Stream.of(), q -> 0); q -> Stream.empty(), q -> 0);
private final DataKeyMapper<T> keyMapper; private final DataKeyMapper<T> keyMapper;


private boolean reset = false; private boolean reset = false;
Expand Down
Expand Up @@ -22,8 +22,6 @@
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;


import com.vaadin.data.HasDataProvider;
import com.vaadin.data.HasFilterableDataProvider;
import com.vaadin.server.SerializableFunction; import com.vaadin.server.SerializableFunction;
import com.vaadin.shared.Registration; import com.vaadin.shared.Registration;


Expand Down Expand Up @@ -67,11 +65,11 @@ public interface DataProvider<T, F> extends Serializable {
/** /**
* Gets the amount of data in this DataProvider. * Gets the amount of data in this DataProvider.
* *
* @param t * @param query
* query with sorting and filtering * query with sorting and filtering
* @return the size of the data provider * @return the size of the data provider
*/ */
int size(Query<T, F> t); int size(Query<T, F> query);


/** /**
* Fetches data from this DataProvider using given {@code query}. * Fetches data from this DataProvider using given {@code query}.
Expand Down
Expand Up @@ -34,7 +34,7 @@ private Comparator<StrBean> getComparator(SortOrder<String> so) {


@Override @Override
protected BackEndDataProvider<StrBean, SerializablePredicate<StrBean>> createDataProvider() { protected BackEndDataProvider<StrBean, SerializablePredicate<StrBean>> createDataProvider() {
return dataProvider = new BackEndDataProvider<>(query -> { return dataProvider = new CallbackDataProvider<>(query -> {
Stream<StrBean> stream = data.stream() Stream<StrBean> stream = data.stream()
.filter(t -> query.getFilter().orElse(s -> true).test(t)); .filter(t -> query.getFilter().orElse(s -> true).test(t));
if (!query.getSortOrders().isEmpty()) { if (!query.getSortOrders().isEmpty()) {
Expand Down
Expand Up @@ -25,7 +25,7 @@
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;


import com.vaadin.data.provider.BackEndDataProvider; import com.vaadin.data.provider.CallbackDataProvider;
import com.vaadin.data.provider.DataProvider; import com.vaadin.data.provider.DataProvider;
import com.vaadin.data.provider.SortOrder; import com.vaadin.data.provider.SortOrder;
import com.vaadin.shared.data.sort.SortDirection; import com.vaadin.shared.data.sort.SortDirection;
Expand Down Expand Up @@ -102,7 +102,7 @@ public void testPersons() {
} }


private DataProvider<Person, ?> createUnsortedDataProvider() { private DataProvider<Person, ?> createUnsortedDataProvider() {
DataProvider<Person, ?> dataProvider = new BackEndDataProvider<>( DataProvider<Person, ?> dataProvider = new CallbackDataProvider<>(
// First callback fetches items based on a query // First callback fetches items based on a query
query -> { query -> {
// The index of the first item to load // The index of the first item to load
Expand Down Expand Up @@ -130,7 +130,7 @@ public void testSortedPersons() {
} }


private DataProvider<Person, ?> createSortedDataProvider() { private DataProvider<Person, ?> createSortedDataProvider() {
DataProvider<Person, ?> dataProvider = new BackEndDataProvider<>( DataProvider<Person, ?> dataProvider = new CallbackDataProvider<>(
// First callback fetches items based on a query // First callback fetches items based on a query
query -> { query -> {
List<PersonService.PersonSort> sortOrders = new ArrayList<>(); List<PersonService.PersonSort> sortOrders = new ArrayList<>();
Expand Down
Expand Up @@ -24,7 +24,7 @@
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito; import org.mockito.Mockito;


import com.vaadin.data.provider.BackEndDataProvider; import com.vaadin.data.provider.CallbackDataProvider;
import com.vaadin.data.provider.bov.Person; import com.vaadin.data.provider.bov.Person;
import com.vaadin.event.selection.MultiSelectionEvent; import com.vaadin.event.selection.MultiSelectionEvent;
import com.vaadin.event.selection.MultiSelectionListener; import com.vaadin.event.selection.MultiSelectionListener;
Expand Down Expand Up @@ -664,7 +664,7 @@ public void selectAllCheckboxVisible__lazyDataProvider() {
model.getSelectAllCheckBoxVisibility()); model.getSelectAllCheckBoxVisibility());


grid.setDataProvider( grid.setDataProvider(
new BackEndDataProvider<String, String>( new CallbackDataProvider<String, String>(
q -> IntStream q -> IntStream
.range(q.getOffset(), .range(q.getOffset(),
Math.max(q.getOffset() + q.getLimit() Math.max(q.getOffset() + q.getLimit()
Expand Down

0 comments on commit dafc831

Please sign in to comment.