/* * 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.ArrayList; 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.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. *

* 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 * data type * @param * 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 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 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 fetch(Query 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. *

* 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. *

* 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. *

* Note: This method will be called often by the Framework. * It should not do any expensive operations. * * @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. *

* 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 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. *

* For example receiving a String from ComboBox and making a Predicate based * on it: * *

     * DataProvider<Person, Predicate<Person>> dataProvider;
     * // ComboBox uses String as the filter type
     * DataProvider<Person, String> wrappedProvider = dataProvider
     *         .withConvertedFilter(filterText -> {
     *             SerializablePredicate<Person> predicate = person -> person.getName()
     *                     .startsWith(filterText);
     *             return predicate;
     *         });
     * comboBox.setDataProvider(wrappedProvider);
     * 
* * @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 * null * * @param * the filter type that the wrapped data provider accepts; * typically provided by a Component * * @return wrapped data provider, not null */ public default DataProvider withConvertedFilter( SerializableFunction filterConverter) { Objects.requireNonNull(filterConverter, "Filter converter can't be null"); return new DataProviderWrapper(this) { @Override protected F getFilter(Query 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 null, but the * callback will not be invoked at all if both would be * null. Not null. * * @return a data provider with a configurable filter, not null */ public default ConfigurableFilterDataProvider withConfigurableFilter( SerializableBiFunction filterCombiner) { return new ConfigurableFilterDataProviderWrapper(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 null */ public default ConfigurableFilterDataProvider 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. *

* 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 * the data item type * @param items * the collection of data, not null * @return a new list data provider */ public static ListDataProvider ofCollection(Collection items) { return new ListDataProvider<>(items); } /** * Creates a new data provider from the given items. *

* 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 * the data item type * @param items * the data items * @return a new list data provider */ @SafeVarargs public static ListDataProvider ofItems(T... items) { return new ListDataProvider<>(new ArrayList<>(Arrays.asList(items))); } /** * Creates a new data provider from the given stream. All items in the * stream are eagerly collected to a list. *

* 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));}. *

* Using big streams is not recommended, you should instead use a * lazy data provider. See * {@link #fromCallbacks(FetchCallback, CountCallback)} or * {@link BackEndDataProvider} for more info. * * @param * the data item type * @param items * a stream of data items, not {@code null} * @return a new list data provider */ public static ListDataProvider fromStream(Stream 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. *

* 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 CallbackDataProvider fromFilteringCallbacks( FetchCallback fetchCallback, CountCallback countCallback) { return new CallbackDataProvider<>(fetchCallback, countCallback); } /** * Creates a new data provider that uses callbacks for fetching and counting * items from any backing store. *

* 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 CallbackDataProvider fromCallbacks( FetchCallback fetchCallback, CountCallback countCallback) { return fromFilteringCallbacks(fetchCallback, countCallback); } }