/*
* 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.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.vaadin.data.HasDataProvider;
import com.vaadin.data.HasFilterableDataProvider;
import com.vaadin.data.provider.CallbackDataProvider.CountCallback;
import com.vaadin.data.provider.CallbackDataProvider.FetchCallback;
import com.vaadin.server.SerializableBiFunction;
import com.vaadin.server.SerializableFunction;
import com.vaadin.server.SerializableToIntFunction;
import com.vaadin.shared.Registration;
/**
* A common interface for fetching data from a backend. The {@link DataProvider}
* interface is used by listing components implementing {@link HasDataProvider}
* or {@link HasFilterableDataProvider}. The listing component will provide a
* {@link Query} object with request information, and the data provider uses
* this information to return a stream containing requested beans.
* <p>
* Vaadin comes with a ready-made solution for in-memory data, known as
* {@link ListDataProvider} which can be created using static {@code create}
* methods in this interface. For custom backends such as SQL, EntityManager,
* REST APIs or SpringData, use a {@link BackEndDataProvider} or its subclass.
*
* @author Vaadin Ltd.
*
* @param <T>
* data type
* @param <F>
* filter type
*
* @see #ofCollection(Collection)
* @see #ofItems(Object...)
* @see #fromStream(Stream)
* @see #fromCallbacks(FetchCallback, CountCallback)
* @see #fromFilteringCallbacks(FetchCallback, CountCallback)
* @see ListDataProvider
* @see BackEndDataProvider
*
* @since 8.0
*/
public interface DataProvider<T, F> extends Serializable {
/**
* Gets whether the DataProvider content all available in memory or does it
* use some external backend.
*
* @return {@code true} if all data is in memory; {@code false} if not
*/
boolean isInMemory();
/**
* Gets the amount of data in this DataProvider.
*
* @param query
* query with sorting and filtering
* @return the size of the data provider
*/
int size(Query<T, F> query);
/**
* Fetches data from this DataProvider using given {@code query}.
*
* @param query
* given query to request data
* @return the result of the query request: a stream of data objects, not
* {@code null}
*/
Stream<T> fetch(Query<T, F> query);
/**
* Refreshes the given item. This method should be used to inform all
* {@link DataProviderListener DataProviderListeners} that an item has been
* updated or replaced with a new instance.
* <p>
* For this to work properly, the item must either implement
* {@link #equals(Object)} and {@link #hashCode()} to consider both the old
* and the new item instances to be equal, or alternatively
* {@link #getId(Object)} should be implemented to return an appropriate
* identifier.
*
* @see #getId(Object)
*
* @param item
* the item to refresh
*/
void refreshItem(T item);
/**
* Refreshes all data based on currently available data in the underlying
* provider.
*/
void refreshAll();
/**
* Gets an identifier for the given item. This identifier is used by the
* framework to determine equality between two items.
* <p>
* Default is to use item itself as its own identifier. If the item has
* {@link Object#equals(Object)} and {@link Object#hashCode()} implemented
* in a way that it can be compared to other items, no changes are required.
*
* @param item
* the item to get identifier for; not {@code null}
* @return the identifier for given item; not {@code null}
*/
public default Object getId(T item) {
Objects.requireNonNull(item, "Cannot provide an id for a null item.");
return item;
}
/**
* Adds a data provider listener. The listener is called when some piece of
* data is updated.
* <p>
* The {@link #refreshAll()} method fires {@link DataChangeEvent} each time
* when it's called. It allows to update UI components when user changes
* something in the underlying data.
*
* @see #refreshAll()
* @param listener
* the data change listener, not null
* @return a registration for the listener
*/
Registration addDataProviderListener(DataProviderListener<T> listener);
/**
* Wraps this data provider to create a data provider that uses a different
* filter type. This can be 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
* .withConvertedFilter(filterText -> {
* Predicate<Person> predicate = person -> person.getName()
* .startsWith(filterText);
* return predicate;
* });
* comboBox.setDataProvider(wrappedProvider);
* </pre>
*
* @param filterConverter
* callback that converts the filter in the query of the wrapped
* data provider into a filter supported by this data provider.
* Will only be called if the query contains a filter. Not
* <code>null</code>
*
* @param <C>
* the filter type that the wrapped data provider accepts;
* typically provided by a Component
*
* @return wrapped data provider, not <code>null</code>
*/
public default <C> DataProvider<T, C> withConvertedFilter(
SerializableFunction<C, F> filterConverter) {
Objects.requireNonNull(filterConverter,
"Filter converter can't be null");
return new DataProviderWrapper<T, C, F>(this) {
@Override
protected F getFilter(Query<T, C> query) {
return query.getFilter().map(filterConverter).orElse(null);
}
};
}
/**
* 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()
* @see ConfigurableFilterDataProvider#setFilter(Object)
*
* @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. Either parameter might be <code>null</code>, but the
* callback will not be invoked at all if both would be
* <code>null</code>. Not <code>null</code>.
*
* @return a data provider with a configurable filter, not <code>null</code>
*/
public default <Q, C> ConfigurableFilterDataProvider<T, Q, C> withConfigurableFilter(
SerializableBiFunction<Q, C, F> filterCombiner) {
return new ConfigurableFilterDataProviderWrapper<T, Q, C, F>(this) {
@Override
protected F combineFilters(Q queryFilter, C configuredFilter) {
return filterCombiner.apply(queryFilter, configuredFilter);
}
};
}
/**
* Wraps this data provider to create a data provider that supports
* programmatically setting a filter but no filtering through the query.
*
* @see #withConfigurableFilter(SerializableBiFunction)
* @see ConfigurableFilterDataProvider#setFilter(Object)
*
* @return a data provider with a configurable filter, not <code>null</code>
*/
public default ConfigurableFilterDataProvider<T, Void, F> withConfigurableFilter() {
return withConfigurableFilter((queryFilter, configuredFilter) -> {
assert queryFilter == null : "Filter from Void query must be null";
return configuredFilter;
});
}
/**
* Creates a new data provider backed by a collection.
* <p>
* The collection is used as-is. Changes in the collection will be visible
* via the created data provider. The caller should copy the collection if
* necessary.
*
* @param <T>
* the data item type
* @param items
* the collection of data, not <code>null</code>
* @return a new list data provider
*/
public static <T> ListDataProvider<T> ofCollection(Collection<T> items) {
return new ListDataProvider<>(items);
}
/**
* Creates a new data provider from the given items.
* <p>
* The items are copied into a new backing list, so structural changes to
* the provided array will not be visible via the created data provider.
*
* @param <T>
* the data item type
* @param items
* the data items
* @return a new list data provider
*/
@SafeVarargs
public static <T> ListDataProvider<T> ofItems(T... items) {
return new ListDataProvider<>(Arrays.asList(items));
}
/**
* Creates a new data provider from the given stream. <b>All items in the
* stream are eagerly collected to a list.</b>
* <p>
* This is a shorthand for using {@link #ofCollection(Collection)} after
* collecting the items in the stream to a list with e.g.
* {@code stream.collect(Collectors.toList));}.
* <p>
* <strong>Using big streams is not recommended, you should instead use a
* lazy data provider.</strong> See
* {@link #fromCallbacks(FetchCallback, CountCallback)}
* or {@link BackEndDataProvider} for more info.
*
* @param <T>
* the data item type
* @param items
* a stream of data items, not {@code null}
* @return a new list data provider
*/
public static <T> ListDataProvider<T> fromStream(Stream<T> items) {
return new ListDataProvider<>(items.collect(Collectors.toList()));
}
/**
* Creates a new data provider that uses filtering callbacks for fetching
* and counting items from any backing store.
* <p>
* The query that is passed to each callback may contain a filter value that
* is provided by the component querying for data.
*
* @param fetchCallback
* function that returns a stream of items from the back end for
* a query
* @param countCallback
* function that returns the number of items in the back end for
* a query
* @return a new callback data provider
*/
public static <T, F> CallbackDataProvider<T, F> fromFilteringCallbacks(
FetchCallback<T, F> fetchCallback,
CountCallback<T, F> countCallback) {
return new CallbackDataProvider<>(fetchCallback, countCallback);
}
/**
* Creates a new data provider that uses callbacks for fetching and counting
* items from any backing store.
* <p>
* The query that is passed to each callback will not contain any filter
* values.
*
* @param fetchCallback
* function that returns a stream of items from the back end for
* a query
* @param countCallback
* function that returns the number of items in the back end for
* a query
* @return a new callback data provider
*/
public static <T> CallbackDataProvider<T, Void> fromCallbacks(
FetchCallback<T, Void> fetchCallback,
CountCallback<T, Void> countCallback) {
return fromFilteringCallbacks(fetchCallback, countCallback);
}
}