diff options
author | Leif Åstrand <legioth@gmail.com> | 2017-01-17 16:22:55 +0200 |
---|---|---|
committer | Denis <denis@vaadin.com> | 2017-01-17 16:22:55 +0200 |
commit | 83b16a8ee742b676bd8ac8ef48e62b8fd64326e3 (patch) | |
tree | 23321774daa70bf2faf41be546fbae169b5dcf61 /server/src/main | |
parent | 487cb4ea0c5e51e7a9b85d6bbb6ab9200f6772f7 (diff) | |
download | vaadin-framework-83b16a8ee742b676bd8ac8ef48e62b8fd64326e3.tar.gz vaadin-framework-83b16a8ee742b676bd8ac8ef48e62b8fd64326e3.zip |
Make data providers statefull with regards to default sort orders (#8247)
* Make data providers statefull with regards to default sort orders
This is one of many steps towards #8245
Diffstat (limited to 'server/src/main')
-rw-r--r-- | server/src/main/java/com/vaadin/data/provider/BackEndDataProvider.java | 89 | ||||
-rw-r--r-- | server/src/main/java/com/vaadin/data/provider/ListDataProvider.java | 185 |
2 files changed, 180 insertions, 94 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 5f789e8a55..3606a99ade 100644 --- a/server/src/main/java/com/vaadin/data/provider/BackEndDataProvider.java +++ b/server/src/main/java/com/vaadin/data/provider/BackEndDataProvider.java @@ -16,13 +16,15 @@ package com.vaadin.data.provider; import java.util.ArrayList; +import java.util.Collections; 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; -import com.vaadin.shared.Registration; /** * A {@link DataProvider} for any back end. @@ -34,6 +36,8 @@ import com.vaadin.shared.Registration; */ public class BackEndDataProvider<T, F> extends AbstractDataProvider<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; @@ -57,46 +61,71 @@ public class BackEndDataProvider<T, F> extends AbstractDataProvider<T, F> { @Override public Stream<T> fetch(Query<T, F> query) { - return request.apply(query); + return request.apply(mixInSortOrders(query)); } @Override public int size(Query<T, F> query) { - return sizeCallback.applyAsInt(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 default sorting order to the data provider. + * Sets a list of sort orders to use as the default sorting for this data + * provider. This overrides the sorting set by any other method that + * manipulates the default sorting of this data provider. + * <p> + * The default sorting is used if the query defines no sorting. The default + * sorting is also used to determine the ordering of items that are + * considered equal by the sorting defined in the query. + * + * @see #setSortOrder(SortOrder) * * @param sortOrders - * a list of sorting information containing field ids and - * directions - * @return new data provider with modified sorting + * a list of sort orders to set, not <code>null</code> */ - @SuppressWarnings("serial") - public BackEndDataProvider<T, F> sortingBy( - List<SortOrder<String>> sortOrders) { - BackEndDataProvider<T, F> parent = this; - return new BackEndDataProvider<T, F>(query -> { - List<SortOrder<String>> queryOrder = new ArrayList<>( - query.getSortOrders()); - queryOrder.addAll(sortOrders); - return parent.fetch(new Query<>(query.getOffset(), query.getLimit(), - queryOrder, query.getInMemorySorting(), - query.getFilter().orElse(null))); - }, sizeCallback) { - - @Override - public Registration addDataProviderListener( - DataProviderListener listener) { - return parent.addDataProviderListener(listener); - } + public void setSortOrders(List<SortOrder<String>> sortOrders) { + this.sortOrders = Objects.requireNonNull(sortOrders, + "Sort orders cannot be null"); + refreshAll(); + } - @Override - public void refreshAll() { - parent.refreshAll(); - } - }; + /** + * Sets a single sort order to use as the default sorting for this data + * provider. This overrides the sorting set by any other method that + * manipulates the default sorting of this data provider. + * <p> + * The default sorting is used if the query defines no sorting. The default + * sorting is also used to determine the ordering of items that are + * considered equal by the sorting defined in the query. + * + * @see #setSortOrders(List) + * + * @param sortOrder + * a sort order to set, or <code>null</code> to clear any + * previously set sort orders + */ + public void setSortOrder(SortOrder<String> sortOrder) { + if (sortOrder == null) { + setSortOrders(Collections.emptyList()); + } else { + setSortOrders(Collections.singletonList(sortOrder)); + } } @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 c6860c26cc..e1c8e8867c 100644 --- a/server/src/main/java/com/vaadin/data/provider/ListDataProvider.java +++ b/server/src/main/java/com/vaadin/data/provider/ListDataProvider.java @@ -19,11 +19,12 @@ import java.util.Collection; import java.util.Comparator; import java.util.Objects; import java.util.Optional; -import java.util.function.Function; import java.util.stream.Stream; +import com.vaadin.data.ValueProvider; +import com.vaadin.server.SerializableComparator; import com.vaadin.server.SerializablePredicate; -import com.vaadin.shared.Registration; +import com.vaadin.shared.data.sort.SortDirection; /** * {@link DataProvider} wrapper for {@link Collection}s. This class does not @@ -36,7 +37,7 @@ public class ListDataProvider<T> extends AbstractDataProvider<T, SerializablePredicate<T>> implements AppendableFilterDataProvider<T, SerializablePredicate<T>> { - private Comparator<T> sortOrder = null; + private SerializableComparator<T> sortOrder = null; private final Collection<T> backend; /** @@ -55,26 +56,6 @@ public class ListDataProvider<T> sortOrder = null; } - /** - * Chaining constructor for making modified {@link ListDataProvider}s. This - * Constructor is used internally for making sorted and filtered variants of - * a base data provider with actual data. - * <p> - * No protective copy is made of the list, and changes in the provided - * backing Collection will be visible via this data provider. The caller - * should copy the list if necessary. - * - * @param items - * the backend data from the original list data provider - * @param sortOrder - * a {@link Comparator} providing the needed sorting order - * - */ - protected ListDataProvider(Collection<T> items, Comparator<T> sortOrder) { - this(items); - this.sortOrder = sortOrder; - } - @Override public Stream<T> fetch(Query<T, SerializablePredicate<T>> query) { Stream<T> stream = backend.stream() @@ -92,64 +73,140 @@ public class ListDataProvider<T> return stream.skip(query.getOffset()).limit(query.getLimit()); } + @Override + public boolean isInMemory() { + return true; + } + + @Override + public int size(Query<T, SerializablePredicate<T>> query) { + return (int) backend.stream() + .filter(t -> query.getFilter().orElse(p -> true).test(t)) + .count(); + } + /** - * Creates a new list data provider based on this list data provider with - * the given sort order. + * Sets the comparator to use as the default sorting for this data provider. + * This overrides the sorting set by any other method that manipulates the + * default sorting of this data provider. * <p> - * <b>NOTE</b>: this data provider is not modified in any way. + * The default sorting is used if the query defines no sorting. The default + * sorting is also used to determine the ordering of items that are + * considered equal by the sorting defined in the query. + * + * @see #setSortOrder(ValueProvider, SortDirection) + * @see #addSortComparator(SerializableComparator) * * @param sortOrder - * a {@link Comparator} providing the needed sorting order - * @return new data provider with modified sorting + * a comparator to use, or <code>null</code> to clear any + * previously set sort order */ - @SuppressWarnings("serial") - public ListDataProvider<T> sortingBy(Comparator<T> sortOrder) { - ListDataProvider<T> parent = this; - return new ListDataProvider<T>(backend, sortOrder) { - - @Override - public Registration addDataProviderListener( - DataProviderListener listener) { - return parent.addDataProviderListener(listener); - } - - @Override - public void refreshAll() { - parent.refreshAll(); - } - }; + public void setSortComparator(SerializableComparator<T> sortOrder) { + this.sortOrder = sortOrder; + refreshAll(); } /** - * Creates a new list data provider based on this list data provider with - * the given sort order. + * Sets the property and direction to use as the default sorting for this + * data provider. This overrides the sorting set by any other method that + * manipulates the default sorting of this data provider. * <p> - * <b>NOTE</b>: this data provider is not modified in any way. + * The default sorting is used if the query defines no sorting. The default + * sorting is also used to determine the ordering of items that are + * considered equal by the sorting defined in the query. + * + * @see #setSortComparator(SerializableComparator) + * @see #addSortOrder(ValueProvider, SortDirection) + * + * @param valueProvider + * the value provider that defines the property do sort by, not + * <code>null</code> + * @param sortDirection + * the sort direction to use, not <code>null</code> + */ + public <V extends Comparable<? super V>> void setSortOrder( + ValueProvider<T, V> valueProvider, SortDirection sortDirection) { + setSortComparator(propertyComparator(valueProvider, sortDirection)); + } + + /** + * Adds a comparator to the default sorting for this data provider. If no + * default sorting has been defined, then the provided comparator will be + * used as the default sorting. If a default sorting has been defined, then + * the provided comparator will be used to determine the ordering of items + * that are considered equal by the previously defined default sorting. * <p> - * This method is a short-hand for - * {@code sortingBy(Comparator.comparing(sortOrder))}. + * The default sorting is used if the query defines no sorting. The default + * sorting is also used to determine the ordering of items that are + * considered equal by the sorting defined in the query. + * + * @see #setSortComparator(SerializableComparator) + * @see #addSortOrder(ValueProvider, SortDirection) * * @param sortOrder - * function to sort by, not {@code null} - * @param <U> - * the type of the Comparable sort key - * @return new data provider with modified sorting + * a comparator to add, not <code>null</code> */ - public <U extends Comparable<? super U>> ListDataProvider<T> sortingBy( - Function<T, U> sortOrder) { - return sortingBy(Comparator.comparing(sortOrder)); + public void addSortComparator(SerializableComparator<T> sortOrder) { + Objects.requireNonNull(sortOrder, "Sort order to add cannot be null"); + + SerializableComparator<T> originalComparator = this.sortOrder; + if (originalComparator == null) { + setSortComparator(sortOrder); + } else { + setSortComparator((a, b) -> { + int result = originalComparator.compare(a, b); + if (result == 0) { + result = sortOrder.compare(a, b); + } + return result; + }); + } } - @Override - public boolean isInMemory() { - return true; + /** + * Adds a property and direction to the default sorting for this data + * provider. If no default sorting has been defined, then the provided sort + * order will be used as the default sorting. If a default sorting has been + * defined, then the provided sort order will be used to determine the + * ordering of items that are considered equal by the previously defined + * default sorting. + * <p> + * The default sorting is used if the query defines no sorting. The default + * sorting is also used to determine the ordering of items that are + * considered equal by the sorting defined in the query. + * + * @see #setSortOrder(ValueProvider, SortDirection) + * @see #addSortComparator(SerializableComparator) + * + * @param valueProvider + * the value provider that defines the property do sort by, not + * <code>null</code> + * @param sortDirection + * the sort direction to use, not <code>null</code> + */ + public <V extends Comparable<? super V>> void addSortOrder( + ValueProvider<T, V> valueProvider, SortDirection sortDirection) { + addSortComparator(propertyComparator(valueProvider, sortDirection)); } - @Override - public int size(Query<T, SerializablePredicate<T>> query) { - return (int) backend.stream() - .filter(t -> query.getFilter().orElse(p -> true).test(t)) - .count(); + private static <V extends Comparable<? super V>, T> SerializableComparator<T> propertyComparator( + ValueProvider<T, V> valueProvider, SortDirection sortDirection) { + Objects.requireNonNull(valueProvider, "Value provider cannot be null"); + Objects.requireNonNull(sortDirection, "Sort direction cannot be null"); + + Comparator<V> comparator = getNaturalSortComparator(sortDirection); + + return (a, b) -> comparator.compare(valueProvider.apply(a), + valueProvider.apply(b)); + } + + private static <V extends Comparable<? super V>> Comparator<V> getNaturalSortComparator( + SortDirection sortDirection) { + Comparator<V> comparator = Comparator.naturalOrder(); + if (sortDirection == SortDirection.DESCENDING) { + comparator = comparator.reversed(); + } + return comparator; } @Override |