diff options
author | Aleksi Hietanen <aleksi@vaadin.com> | 2017-05-16 11:57:02 +0300 |
---|---|---|
committer | Pekka Hyvönen <pekka@vaadin.com> | 2017-05-16 11:57:02 +0300 |
commit | efa7f5a4d069556061ba4ceef4fb4d76dae84ef4 (patch) | |
tree | 767b0fdb3146930919cec37e5eaab75422b0867d /server/src/main/java | |
parent | eb743d965278d263a4c496bb4e39c067fe2b1a8c (diff) | |
download | vaadin-framework-efa7f5a4d069556061ba4ceef4fb4d76dae84ef4.tar.gz vaadin-framework-efa7f5a4d069556061ba4ceef4fb4d76dae84ef4.zip |
Refactor common methods in in-memory data providers (#9308)
* Refactor common methods of InMemoryHierarchicalDataProvider and ListDataProvider to a single interface
* Rename HierarchyData and InMemoryHierarchicalDataProvider, introduce HasHierarchicalDataProvider
* Additionally adds a helper method for recursive constructing
TreeData with a child item provider.
Diffstat (limited to 'server/src/main/java')
-rw-r--r-- | server/src/main/java/com/vaadin/data/HasHierarchicalDataProvider.java | 207 | ||||
-rw-r--r-- | server/src/main/java/com/vaadin/data/TreeData.java (renamed from server/src/main/java/com/vaadin/data/HierarchyData.java) | 47 | ||||
-rw-r--r-- | server/src/main/java/com/vaadin/data/provider/HierarchicalDataCommunicator.java | 8 | ||||
-rw-r--r-- | server/src/main/java/com/vaadin/data/provider/InMemoryDataProvider.java | 451 | ||||
-rw-r--r-- | server/src/main/java/com/vaadin/data/provider/InMemoryDataProviderHelpers.java | 185 | ||||
-rw-r--r-- | server/src/main/java/com/vaadin/data/provider/ListDataProvider.java | 471 | ||||
-rw-r--r-- | server/src/main/java/com/vaadin/data/provider/TreeDataProvider.java (renamed from server/src/main/java/com/vaadin/data/provider/InMemoryHierarchicalDataProvider.java) | 129 | ||||
-rw-r--r-- | server/src/main/java/com/vaadin/ui/Tree.java | 10 | ||||
-rw-r--r-- | server/src/main/java/com/vaadin/ui/TreeGrid.java | 124 |
9 files changed, 937 insertions, 695 deletions
diff --git a/server/src/main/java/com/vaadin/data/HasHierarchicalDataProvider.java b/server/src/main/java/com/vaadin/data/HasHierarchicalDataProvider.java new file mode 100644 index 0000000000..e43b9d6b77 --- /dev/null +++ b/server/src/main/java/com/vaadin/data/HasHierarchicalDataProvider.java @@ -0,0 +1,207 @@ +/* + * 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; + +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.provider.DataProvider; +import com.vaadin.data.provider.HierarchicalDataProvider; +import com.vaadin.data.provider.TreeDataProvider; + +/** + * A generic interface for hierarchical listing components that use a data + * provider for showing data. + * + * @author Vaadin Ltd + * @since 8.1 + * + * @param <T> + * the item data type + */ +public interface HasHierarchicalDataProvider<T> extends HasDataProvider<T> { + + /** + * Gets the backing {@link TreeData} instance of the data provider, if the + * data provider is a {@link TreeDataProvider}. + * + * @return the TreeData instance used by the data provider + * @throws IllegalStateException + * if the type of the data provider is not + * {@link TreeDataProvider} + */ + @SuppressWarnings("unchecked") + public default TreeData<T> getTreeData() { + if (getDataProvider() instanceof TreeDataProvider) { + return ((TreeDataProvider<T>) getDataProvider()).getTreeData(); + } else { + throw new IllegalStateException(""); + } + } + + /** + * Sets the root data items of this component provided as a collection and + * recursively populates them with child items with the given value + * provider. + * <p> + * The provided items are wrapped into a {@link TreeDataProvider} backed by + * a flat {@link TreeData} structure. The data provider instance is used as + * a parameter for the {@link #setDataProvider(DataProvider)} method. It + * means that the items collection can be accessed later on via + * {@link #getTreeData()}: + * + * <pre> + * <code> + * Collection<Person> grandParents = getGrandParents(); + * HasHierarchicalDataProvider<Person> treeGrid = new TreeGrid<>(); + * treeGrid.setItems(grandParents, Person::getChildren); + * ... + * + * TreeData<Person> data = treeGrid.getTreeData(); + * </code> + * </pre> + * <p> + * The returned {@link TreeData} instance may be used as-is to add, remove + * or modify items in the hierarchy. These modifications to the object are + * not automatically reflected back to the TreeGrid. Items modified should + * be refreshed with {@link HierarchicalDataProvider#refreshItem(Object)} + * and when adding or removing items + * {@link HierarchicalDataProvider#refreshAll()} should be called. + * + * @param rootItems + * the root items to display, not {@code null} + * @param childItemProvider + * the value provider used to recursively populate the given root + * items with child items, not {@code null} + */ + public default void setItems(Collection<T> rootItems, + ValueProvider<T, Collection<T>> childItemProvider) { + Objects.requireNonNull(rootItems, "Given root items may not be null"); + Objects.requireNonNull(childItemProvider, + "Given child item provider may not be null"); + setDataProvider(new TreeDataProvider<>( + new TreeData<T>().addItems(rootItems, childItemProvider))); + } + + /** + * Sets the data items of this component provided as a collection. + * <p> + * The provided items are wrapped into a {@link TreeDataProvider} backed by + * a flat {@link TreeData} structure. The data provider instance is used as + * a parameter for the {@link #setDataProvider(DataProvider)} method. It + * means that the items collection can be accessed later on via + * {@link #getTreeData()}: + * + * <pre> + * <code> + * HasHierarchicalDataProvider<String> treeGrid = new TreeGrid<>(); + * treeGrid.setItems(Arrays.asList("a","b")); + * ... + * + * TreeData<String> data = treeGrid.getTreeData(); + * </code> + * </pre> + * <p> + * The returned {@link TreeData} instance may be used as-is to add, remove + * or modify items in the hierarchy. These modifications to the object are + * not automatically reflected back to the TreeGrid. Items modified should + * be refreshed with {@link HierarchicalDataProvider#refreshItem(Object)} + * and when adding or removing items + * {@link HierarchicalDataProvider#refreshAll()} should be called. + * + * @param items + * the data items to display, not {@code null} + */ + @Override + public default void setItems(Collection<T> items) { + Objects.requireNonNull(items, "Given collection may not be null"); + setDataProvider(new TreeDataProvider<>( + new TreeData<T>().addItems(null, items))); + } + + /** + * Sets the data items of this component provided as a stream. + * <p> + * The provided items are wrapped into a {@link TreeDataProvider} backed by + * a flat {@link TreeData} structure. The data provider instance is used as + * a parameter for the {@link #setDataProvider(DataProvider)} method. It + * means that the items collection can be accessed later on via + * {@link #getTreeData()}: + * + * <pre> + * <code> + * HasHierarchicalDataProvider<String> treeGrid = new TreeGrid<>(); + * treeGrid.setItems(Stream.of("a","b")); + * ... + * + * TreeData<String> data = treeGrid.getTreeData(); + * </code> + * </pre> + * <p> + * The returned {@link TreeData} instance may be used as-is to add, remove + * or modify items in the hierarchy. These modifications to the object are + * not automatically reflected back to the TreeGrid. Items modified should + * be refreshed with {@link HierarchicalDataProvider#refreshItem(Object)} + * and when adding or removing items + * {@link HierarchicalDataProvider#refreshAll()} should be called. + * + * @param items + * the data items to display, not {@code null} + */ + @Override + public default void setItems(Stream<T> items) { + Objects.requireNonNull(items, "Given stream may not be null"); + setItems(items.collect(Collectors.toList())); + } + + /** + * Sets the data items of this listing. + * <p> + * The provided items are wrapped into a {@link TreeDataProvider} backed by + * a flat {@link TreeData} structure. The data provider instance is used as + * a parameter for the {@link #setDataProvider(DataProvider)} method. It + * means that the items collection can be accessed later on via + * {@link #getTreeData()}: + * + * <pre> + * <code> + * TreeGrid<String> treeGrid = new TreeGrid<>(); + * treeGrid.setItems("a","b"); + * ... + * + * TreeData<String> data = treeGrid.getTreeData(); + * </code> + * </pre> + * <p> + * The returned {@link TreeData} instance may be used as-is to add, remove + * or modify items in the hierarchy. These modifications to the object are + * not automatically reflected back to the TreeGrid. Items modified should + * be refreshed with {@link HierarchicalDataProvider#refreshItem(Object)} + * and when adding or removing items + * {@link HierarchicalDataProvider#refreshAll()} should be called. + * + * @param items + * the data items to display, not {@code null} + */ + @Override + public default void setItems(@SuppressWarnings("unchecked") T... items) { + Objects.requireNonNull(items, "Given items may not be null"); + setItems(Arrays.asList(items)); + } +} diff --git a/server/src/main/java/com/vaadin/data/HierarchyData.java b/server/src/main/java/com/vaadin/data/TreeData.java index 0cf680db2f..def69745ef 100644 --- a/server/src/main/java/com/vaadin/data/HierarchyData.java +++ b/server/src/main/java/com/vaadin/data/TreeData.java @@ -34,7 +34,7 @@ import java.util.stream.Stream; * @param <T> * data type */ -public class HierarchyData<T> implements Serializable { +public class TreeData<T> implements Serializable { private static class HierarchyWrapper<T> implements Serializable { private T item; @@ -86,7 +86,7 @@ public class HierarchyData<T> implements Serializable { * Creates an initially empty hierarchical data representation to which * items can be added or removed. */ - public HierarchyData() { + public TreeData() { itemToWrapperMap = new LinkedHashMap<>(); itemToWrapperMap.put(null, new HierarchyWrapper<>(null, null)); } @@ -109,7 +109,7 @@ public class HierarchyData<T> implements Serializable { * @throws NullPointerException * if item is null */ - public HierarchyData<T> addItem(T parent, T item) { + public TreeData<T> addItem(T parent, T item) { Objects.requireNonNull(item, "Item cannot be null"); if (parent != null && !contains(parent)) { throw new IllegalArgumentException( @@ -144,7 +144,7 @@ public class HierarchyData<T> implements Serializable { * @throws NullPointerException * if any of the items are null */ - public HierarchyData<T> addItems(T parent, + public TreeData<T> addItems(T parent, @SuppressWarnings("unchecked") T... items) { Arrays.asList(items).stream().forEach(item -> addItem(parent, item)); return this; @@ -170,7 +170,7 @@ public class HierarchyData<T> implements Serializable { * @throws NullPointerException * if any of the items are null */ - public HierarchyData<T> addItems(T parent, Collection<T> items) { + public TreeData<T> addItems(T parent, Collection<T> items) { items.stream().forEach(item -> addItem(parent, item)); return this; } @@ -195,12 +195,34 @@ public class HierarchyData<T> implements Serializable { * @throws NullPointerException * if any of the items are null */ - public HierarchyData<T> addItems(T parent, Stream<T> items) { + public TreeData<T> addItems(T parent, Stream<T> items) { items.forEach(item -> addItem(parent, item)); return this; } /** + * Adds the given items as root items and uses the given value provider to + * recursively populate children of the root items. + * + * @param rootItems + * the root items to add + * @param childItemProvider + * the value provider used to recursively populate this TreeData + * from the given root items + * @return this + */ + public TreeData<T> addItems(Collection<T> rootItems, + ValueProvider<T, Collection<T>> childItemProvider) { + rootItems.forEach(item -> { + addItem(null, item); + Collection<T> childItems = childItemProvider.apply(item); + addItems(item, childItems); + addItemsRecursively(childItems, childItemProvider); + }); + return this; + } + + /** * Remove a given item from this structure. Additionally, this will * recursively remove any descendants of the item. * @@ -211,7 +233,7 @@ public class HierarchyData<T> implements Serializable { * @throws IllegalArgumentException * if the item does not exist in this structure */ - public HierarchyData<T> removeItem(T item) { + public TreeData<T> removeItem(T item) { if (!contains(item)) { throw new IllegalArgumentException( "Item '" + item + "' not in the hierarchy"); @@ -232,7 +254,7 @@ public class HierarchyData<T> implements Serializable { * * @return this */ - public HierarchyData<T> clear() { + public TreeData<T> clear() { removeItem(null); return this; } @@ -275,4 +297,13 @@ public class HierarchyData<T> implements Serializable { } itemToWrapperMap.put(item, wrappedItem); } + + private void addItemsRecursively(Collection<T> items, + ValueProvider<T, Collection<T>> childItemProvider) { + items.forEach(item -> { + Collection<T> childItems = childItemProvider.apply(item); + addItems(item, childItems); + addItemsRecursively(childItems, childItemProvider); + }); + } } diff --git a/server/src/main/java/com/vaadin/data/provider/HierarchicalDataCommunicator.java b/server/src/main/java/com/vaadin/data/provider/HierarchicalDataCommunicator.java index 5d3bd81a05..4a8ae62772 100644 --- a/server/src/main/java/com/vaadin/data/provider/HierarchicalDataCommunicator.java +++ b/server/src/main/java/com/vaadin/data/provider/HierarchicalDataCommunicator.java @@ -27,7 +27,7 @@ import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; -import com.vaadin.data.HierarchyData; +import com.vaadin.data.TreeData; import com.vaadin.data.provider.HierarchyMapper.TreeLevelQuery; import com.vaadin.data.provider.HierarchyMapper.TreeNode; import com.vaadin.server.SerializableConsumer; @@ -75,12 +75,12 @@ public class HierarchicalDataCommunicator<T> extends DataCommunicator<T> { /** * Construct a new hierarchical data communicator backed by a - * {@link InMemoryHierarchicalDataProvider}. + * {@link TreeDataProvider}. */ public HierarchicalDataCommunicator() { super(); - dataProvider = new InMemoryHierarchicalDataProvider<>( - new HierarchyData<>()); + dataProvider = new TreeDataProvider<>( + new TreeData<>()); } @Override diff --git a/server/src/main/java/com/vaadin/data/provider/InMemoryDataProvider.java b/server/src/main/java/com/vaadin/data/provider/InMemoryDataProvider.java new file mode 100644 index 0000000000..bfb62b8dc6 --- /dev/null +++ b/server/src/main/java/com/vaadin/data/provider/InMemoryDataProvider.java @@ -0,0 +1,451 @@ +/* + * 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.Locale; +import java.util.Objects; + +import com.vaadin.data.ValueProvider; +import com.vaadin.server.SerializableBiPredicate; +import com.vaadin.server.SerializableComparator; +import com.vaadin.server.SerializablePredicate; +import com.vaadin.shared.data.sort.SortDirection; +import com.vaadin.ui.UI; + +/** + * A mixin interface for in-memory data providers. Contains methods for + * configuring sorting and filtering. + * + * @author Vaadin Ltd + * @since 8.1 + * + * @param <T> + * data type + */ +public interface InMemoryDataProvider<T> extends + ConfigurableFilterDataProvider<T, SerializablePredicate<T>, SerializablePredicate<T>> { + + @Override + public default boolean isInMemory() { + return true; + } + + /** + * Gets the current filter of this data provider. + * + * @return the filter of this data provider + */ + public SerializablePredicate<T> getFilter(); + + /** + * Sets a filter to be applied to all queries. The filter replaces any + * filter that has been set or added previously. + * + * @see #setFilter(ValueProvider, SerializablePredicate) + * @see #setFilterByValue(ValueProvider, Object) + * @see #addFilter(SerializablePredicate) + * + * @param filter + * the filter to set, or <code>null</code> to remove any set + * filters + */ + @Override + public void setFilter(SerializablePredicate<T> filter); + + /** + * Sets a filter for an item property. The filter replaces any filter that + * has been set or added previously. + * + * @see #setFilter(SerializablePredicate) + * @see #setFilterByValue(ValueProvider, Object) + * @see #addFilter(ValueProvider, SerializablePredicate) + * + * @param valueProvider + * value provider that gets the property value, not + * <code>null</code> + * @param valueFilter + * filter for testing the property value, not <code>null</code> + */ + public default <V> void setFilter(ValueProvider<T, V> valueProvider, + SerializablePredicate<V> valueFilter) { + setFilter(InMemoryDataProviderHelpers + .createValueProviderFilter(valueProvider, valueFilter)); + } + + /** + * Adds a filter to be applied to all queries. The filter will be used in + * addition to any filter that has been set or added previously. + * + * @see #addFilter(ValueProvider, SerializablePredicate) + * @see #addFilterByValue(ValueProvider, Object) + * @see #setFilter(SerializablePredicate) + * + * @param filter + * the filter to add, not <code>null</code> + */ + public default void addFilter(SerializablePredicate<T> filter) { + Objects.requireNonNull(filter, "Filter cannot be null"); + + if (getFilter() == null) { + setFilter(filter); + } else { + SerializablePredicate<T> oldFilter = getFilter(); + setFilter(item -> oldFilter.test(item) && filter.test(item)); + } + } + + /** + * Adds a filter for an item property. The filter will be used in addition + * to any filter that has been set or added previously. + * + * @see #addFilter(SerializablePredicate) + * @see #addFilterByValue(ValueProvider, Object) + * @see #setFilter(ValueProvider, SerializablePredicate) + * + * @param valueProvider + * value provider that gets the property value, not + * <code>null</code> + * @param valueFilter + * filter for testing the property value, not <code>null</code> + */ + public default <V> void addFilter(ValueProvider<T, V> valueProvider, + SerializablePredicate<V> valueFilter) { + Objects.requireNonNull(valueProvider, "Value provider cannot be null"); + Objects.requireNonNull(valueFilter, "Value filter cannot be null"); + + addFilter(InMemoryDataProviderHelpers + .createValueProviderFilter(valueProvider, valueFilter)); + } + + /** + * Sets a filter that requires an item property to have a specific value. + * The property value and the provided value are compared using + * {@link Object#equals(Object)}. The filter replaces any filter that has + * been set or added previously. + * + * @see #setFilter(SerializablePredicate) + * @see #setFilter(ValueProvider, SerializablePredicate) + * @see #addFilterByValue(ValueProvider, Object) + * + * @param valueProvider + * value provider that gets the property value, not + * <code>null</code> + * @param requiredValue + * the value that the property must have for the filter to pass + */ + public default <V> void setFilterByValue(ValueProvider<T, V> valueProvider, + V requiredValue) { + setFilter(InMemoryDataProviderHelpers.createEqualsFilter(valueProvider, + requiredValue)); + } + + /** + * Adds a filter that requires an item property to have a specific value. + * The property value and the provided value are compared using + * {@link Object#equals(Object)}.The filter will be used in addition to any + * filter that has been set or added previously. + * + * @see #setFilterByValue(ValueProvider, Object) + * @see #addFilter(SerializablePredicate) + * @see #addFilter(ValueProvider, SerializablePredicate) + * + * @param valueProvider + * value provider that gets the property value, not + * <code>null</code> + * @param requiredValue + * the value that the property must have for the filter to pass + */ + public default <V> void addFilterByValue(ValueProvider<T, V> valueProvider, + V requiredValue) { + addFilter(InMemoryDataProviderHelpers.createEqualsFilter(valueProvider, + requiredValue)); + } + + /** + * Removes any filter that has been set or added previously. + * + * @see #setFilter(SerializablePredicate) + */ + public default void clearFilters() { + setFilter(null); + } + + /** + * Gets the current sort comparator of this data provider. + * + * @return the sort comparator of this data provider + */ + public SerializableComparator<T> getSortComparator(); + + /** + * 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> + * 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 comparator + * a comparator to use, or <code>null</code> to clear any + * previously set sort order + */ + public void setSortComparator(SerializableComparator<T> comparator); + + /** + * 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> + * 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 comparator + * a comparator to add, not <code>null</code> + */ + public default void addSortComparator( + SerializableComparator<T> comparator) { + Objects.requireNonNull(comparator, "Comparator to add cannot be null"); + SerializableComparator<T> originalComparator = getSortComparator(); + if (originalComparator == null) { + setSortComparator(comparator); + } else { + setSortComparator((a, b) -> { + int result = originalComparator.compare(a, b); + if (result == 0) { + result = comparator.compare(a, b); + } + return result; + }); + } + } + + /** + * 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> + * 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 default <V extends Comparable<? super V>> void setSortOrder( + ValueProvider<T, V> valueProvider, SortDirection sortDirection) { + setSortComparator(InMemoryDataProviderHelpers + .propertyComparator(valueProvider, sortDirection)); + } + + /** + * 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 default <V extends Comparable<? super V>> void addSortOrder( + ValueProvider<T, V> valueProvider, SortDirection sortDirection) { + addSortComparator(InMemoryDataProviderHelpers + .propertyComparator(valueProvider, sortDirection)); + } + + /** + * Wraps this data provider to create a new data provider that is filtered + * by comparing an item to the filter value provided in the query. + * <p> + * The predicate receives the item as the first parameter and the query + * filter value as the second parameter, and should return <code>true</code> + * if the corresponding item should be included. The query filter value is + * never <code>null</code> – all items are included without running the + * predicate if the query doesn't define any filter. + * + * @param predicate + * a predicate to use for comparing the item to the query filter, + * not <code>null</code> + * + * @return a data provider that filters accordingly, not <code>null</code> + */ + public default <Q> DataProvider<T, Q> filteringBy( + SerializableBiPredicate<T, Q> predicate) { + Objects.requireNonNull(predicate, "Predicate cannot be null"); + + return withConvertedFilter( + filterValue -> item -> predicate.test(item, filterValue)); + } + + /** + * Wraps this data provider to create a new data provider that is filtered + * by comparing an item property value to the filter value provided in the + * query. + * <p> + * The predicate receives the property value as the first parameter and the + * query filter value as the second parameter, and should return + * <code>true</code> if the corresponding item should be included. The query + * filter value is never <code>null</code> – all items are included without + * running either callback if the query doesn't define any filter. + * + * @param valueProvider + * a value provider that gets the property value, not + * <code>null</code> + * @param predicate + * a predicate to use for comparing the property value to the + * query filter, not <code>null</code> + * + * @return a data provider that filters accordingly, not <code>null</code> + */ + public default <V, Q> DataProvider<T, Q> filteringBy( + ValueProvider<T, V> valueProvider, + SerializableBiPredicate<V, Q> predicate) { + Objects.requireNonNull(valueProvider, "Value provider cannot be null"); + Objects.requireNonNull(predicate, "Predicate cannot be null"); + + return filteringBy((item, filterValue) -> predicate + .test(valueProvider.apply(item), filterValue)); + } + + /** + * Wraps this data provider to create a new data provider that is filtered + * by testing whether the value of a property is equals to the filter value + * provided in the query. Equality is tested using + * {@link Objects#equals(Object, Object)}. + * + * @param valueProvider + * a value provider that gets the property value, not + * <code>null</code> + * + * @return a data provider that filters accordingly, not <code>null</code> + */ + public default <V> DataProvider<T, V> filteringByEquals( + ValueProvider<T, V> valueProvider) { + return filteringBy(valueProvider, Objects::equals); + } + + /** + * Wraps this data provider to create a new data provider that is filtered + * by a string by checking whether the lower case representation of the + * filter value provided in the query is a substring of the lower case + * representation of an item property value. The filter never passes if the + * item property value is <code>null</code>. + * + * @param valueProvider + * a value provider that gets the string property value, not + * <code>null</code> + * @param locale + * the locale to use for converting the strings to lower case, + * not <code>null</code> + * @return a data provider that filters accordingly, not <code>null</code> + */ + public default DataProvider<T, String> filteringBySubstring( + ValueProvider<T, String> valueProvider, Locale locale) { + Objects.requireNonNull(locale, "Locale cannot be null"); + return InMemoryDataProviderHelpers.filteringByCaseInsensitiveString( + this, valueProvider, + String::contains, () -> locale); + } + + /** + * Wraps this data provider to create a new data provider that is filtered + * by a string by checking whether the lower case representation of the + * filter value provided in the query is a substring of the lower case + * representation of an item property value. Conversion to lower case is + * done using the locale of the {@link UI#getCurrent() current UI} if + * available, or otherwise {@link Locale#getDefault() the default locale}. + * The filter never passes if the item property value is <code>null</code>. + * + * @param valueProvider + * a value provider that gets the string property value, not + * <code>null</code> + * @return a data provider that filters accordingly, not <code>null</code> + */ + public default DataProvider<T, String> filteringBySubstring( + ValueProvider<T, String> valueProvider) { + return InMemoryDataProviderHelpers.filteringByCaseInsensitiveString( + this, valueProvider, String::contains, + InMemoryDataProviderHelpers.CURRENT_LOCALE_SUPPLIER); + } + + /** + * Wraps this data provider to create a new data provider that is filtered + * by a string by checking whether the lower case representation of an item + * property value starts with the lower case representation of the filter + * value provided in the query. The filter never passes if the item property + * value is <code>null</code>. + * + * @param valueProvider + * a value provider that gets the string property value, not + * <code>null</code> + * @param locale + * the locale to use for converting the strings to lower case, + * not <code>null</code> + * @return a data provider that filters accordingly, not <code>null</code> + */ + public default DataProvider<T, String> filteringByPrefix( + ValueProvider<T, String> valueProvider, Locale locale) { + return InMemoryDataProviderHelpers.filteringByCaseInsensitiveString(this, valueProvider, + String::startsWith, () -> locale); + } + + /** + * Wraps this data provider to create a new data provider that is filtered + * by a string by checking whether the lower case representation of an item + * property value starts with the lower case representation of the filter + * value provided in the query. Conversion to lower case is done using the + * locale of the {@link UI#getCurrent() current UI} if available, or + * otherwise {@link Locale#getDefault() the default locale}. The filter + * never passes if the item property value is <code>null</code>. + * + * @param valueProvider + * a value provider that gets the string property value, not + * <code>null</code> + * @return a data provider that filters accordingly, not <code>null</code> + */ + public default DataProvider<T, String> filteringByPrefix( + ValueProvider<T, String> valueProvider) { + return InMemoryDataProviderHelpers.filteringByCaseInsensitiveString(this, valueProvider, + String::startsWith, InMemoryDataProviderHelpers.CURRENT_LOCALE_SUPPLIER); + } +} diff --git a/server/src/main/java/com/vaadin/data/provider/InMemoryDataProviderHelpers.java b/server/src/main/java/com/vaadin/data/provider/InMemoryDataProviderHelpers.java new file mode 100644 index 0000000000..556404911c --- /dev/null +++ b/server/src/main/java/com/vaadin/data/provider/InMemoryDataProviderHelpers.java @@ -0,0 +1,185 @@ +/* + * 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.Comparator; +import java.util.Locale; +import java.util.Objects; + +import com.vaadin.data.ValueProvider; +import com.vaadin.server.SerializableBiPredicate; +import com.vaadin.server.SerializableComparator; +import com.vaadin.server.SerializablePredicate; +import com.vaadin.server.SerializableSupplier; +import com.vaadin.shared.data.sort.SortDirection; +import com.vaadin.ui.UI; + +/** + * A class containing a number of static helper methods for implementing + * {@link InMemoryDataProvider}s. + * <p> + * This class is intended primarily for internal use. + * + * @author Vaadin Ltd + * @since 8.1 + */ +public class InMemoryDataProviderHelpers { + + /** + * Supplier that attempts to resolve a locale from the current UI. Returns + * the system's default locale as a fallback. + */ + public static final SerializableSupplier<Locale> CURRENT_LOCALE_SUPPLIER = () -> { + UI currentUi = UI.getCurrent(); + if (currentUi != null) { + return currentUi.getLocale(); + } else { + return Locale.getDefault(); + } + }; + + /** + * Wraps a given data provider so that its filter ignores null items + * returned by the given value provider. + * + * @param dataProvider + * the data provider to wrap + * @param valueProvider + * the value provider for providing values to filter + * @param predicate + * the predicate to combine null filtering with + * @return the wrapped data provider + */ + public static <T, V, Q> DataProvider<T, Q> filteringByIgnoreNull( + InMemoryDataProvider<T> dataProvider, + ValueProvider<T, V> valueProvider, + SerializableBiPredicate<V, Q> predicate) { + Objects.requireNonNull(predicate, "Predicate cannot be null"); + + return dataProvider.filteringBy(valueProvider, + (itemValue, queryFilter) -> itemValue != null + && predicate.test(itemValue, queryFilter)); + } + + /** + * Wraps a given data provider so that its filter tests the given predicate + * with the lower case string provided by the given value provider. + * + * @param dataProvider + * the data provider to wrap + * @param valueProvider + * the value provider for providing string values to filter + * @param predicate + * the predicate to use for comparing the resulting lower case + * strings + * @param localeSupplier + * the locale to use when converting strings to lower case + * @return the wrapped data provider + */ + public static <T> DataProvider<T, String> filteringByCaseInsensitiveString( + InMemoryDataProvider<T> dataProvider, + ValueProvider<T, String> valueProvider, + SerializableBiPredicate<String, String> predicate, + SerializableSupplier<Locale> localeSupplier) { + // Only assert since these are only passed from our own code + assert predicate != null; + assert localeSupplier != null; + + return filteringByIgnoreNull(dataProvider, valueProvider, + (itemString, filterString) -> { + Locale locale = localeSupplier.get(); + assert locale != null; + + return predicate.test(itemString.toLowerCase(locale), + filterString.toLowerCase(locale)); + }); + } + + /** + * Creates a comparator for the return type of the given + * {@link ValueProvider}, sorted in the direction specified by the given + * {@link SortDirection}. + * + * @param valueProvider + * the value provider to use + * @param sortDirection + * the sort direction to use + * @return the created comparator + */ + public 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)); + } + + /** + * Gets the natural order comparator for the type argument, or the natural + * order comparator reversed if the given sorting direction is + * {@link SortDirection#DESCENDING}. + * + * @param sortDirection + * the sort direction to use + * @return the natural comparator, with ordering defined by the given sort + * direction + */ + public 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; + } + + /** + * Creates a new predicate from the given predicate and value provider. This + * allows using a predicate of the value providers return type with objects + * of the value providers type. + * + * @param valueProvider + * the value provider to use + * @param valueFilter + * the original predicate + * @return the created predicate + */ + public static <T, V> SerializablePredicate<T> createValueProviderFilter( + ValueProvider<T, V> valueProvider, + SerializablePredicate<V> valueFilter) { + return item -> valueFilter.test(valueProvider.apply(item)); + } + + /** + * Creates a predicate that compares equality of the given required value to + * the value the given value provider obtains. + * + * @param valueProvider + * the value provider to use + * @param requiredValue + * the required value + * @return the created predicate + */ + public static <T, V> SerializablePredicate<T> createEqualsFilter( + ValueProvider<T, V> valueProvider, V requiredValue) { + Objects.requireNonNull(valueProvider, "Value provider cannot be null"); + + return item -> Objects.equals(valueProvider.apply(item), requiredValue); + } +} 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 fba2f94d9d..eef0b0388e 100644 --- a/server/src/main/java/com/vaadin/data/provider/ListDataProvider.java +++ b/server/src/main/java/com/vaadin/data/provider/ListDataProvider.java @@ -17,18 +17,12 @@ package com.vaadin.data.provider; import java.util.Collection; import java.util.Comparator; -import java.util.Locale; import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; -import com.vaadin.data.ValueProvider; -import com.vaadin.server.SerializableBiPredicate; import com.vaadin.server.SerializableComparator; import com.vaadin.server.SerializablePredicate; -import com.vaadin.server.SerializableSupplier; -import com.vaadin.shared.data.sort.SortDirection; -import com.vaadin.ui.UI; /** * {@link DataProvider} wrapper for {@link Collection}s. @@ -38,17 +32,8 @@ import com.vaadin.ui.UI; * @since 8.0 */ public class ListDataProvider<T> - extends AbstractDataProvider<T, SerializablePredicate<T>> implements - ConfigurableFilterDataProvider<T, SerializablePredicate<T>, SerializablePredicate<T>> { - - private static final SerializableSupplier<Locale> CURRENT_LOCALE_SUPPLIER = () -> { - UI currentUi = UI.getCurrent(); - if (currentUi != null) { - return currentUi.getLocale(); - } else { - return Locale.getDefault(); - } - }; + extends AbstractDataProvider<T, SerializablePredicate<T>> + implements InMemoryDataProvider<T> { private SerializableComparator<T> sortOrder = null; @@ -98,11 +83,6 @@ public class ListDataProvider<T> } @Override - public boolean isInMemory() { - return true; - } - - @Override public int size(Query<T, SerializablePredicate<T>> query) { return (int) getFilteredStream(query).count(); } @@ -122,454 +102,25 @@ public class ListDataProvider<T> return stream; } - /** - * 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> - * 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 comparator - * a comparator to use, or <code>null</code> to clear any - * previously set sort order - */ + @Override + public SerializableComparator<T> getSortComparator() { + return sortOrder; + } + + @Override public void setSortComparator(SerializableComparator<T> comparator) { this.sortOrder = comparator; refreshAll(); } - /** - * 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> - * 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> - * 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 comparator - * a comparator to add, not <code>null</code> - */ - public void addSortComparator(SerializableComparator<T> comparator) { - Objects.requireNonNull(comparator, "Sort order to add cannot be null"); - - SerializableComparator<T> originalComparator = this.sortOrder; - if (originalComparator == null) { - setSortComparator(comparator); - } else { - setSortComparator((a, b) -> { - int result = originalComparator.compare(a, b); - if (result == 0) { - result = comparator.compare(a, b); - } - return result; - }); - } - } - - /** - * 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)); - } - - 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 + public SerializablePredicate<T> getFilter() { + return filter; } - /** - * Sets a filter to be applied to all queries. The filter replaces any - * filter that has been set or added previously. - * - * @see #setFilter(ValueProvider, SerializablePredicate) - * @see #setFilterByValue(ValueProvider, Object) - * @see #addFilter(SerializablePredicate) - * - * @param filter - * the filter to set, or <code>null</code> to remove any set - * filters - */ @Override public void setFilter(SerializablePredicate<T> filter) { this.filter = filter; refreshAll(); } - - /** - * Adds a filter to be applied to all queries. The filter will be used in - * addition to any filter that has been set or added previously. - * - * @see #addFilter(ValueProvider, SerializablePredicate) - * @see #addFilterByValue(ValueProvider, Object) - * @see #setFilter(SerializablePredicate) - * - * @param filter - * the filter to add, not <code>null</code> - */ - public void addFilter(SerializablePredicate<T> filter) { - Objects.requireNonNull(filter, "Filter cannot be null"); - - if (this.filter == null) { - setFilter(filter); - } else { - SerializablePredicate<T> oldFilter = this.filter; - setFilter(item -> oldFilter.test(item) && filter.test(item)); - } - } - - /** - * Removes any filter that has been set or added previously. - * - * @see #setFilter(SerializablePredicate) - */ - public void clearFilters() { - setFilter(null); - } - - /** - * Sets a filter for an item property. The filter replaces any filter that - * has been set or added previously. - * - * @see #setFilter(SerializablePredicate) - * @see #setFilterByValue(ValueProvider, Object) - * @see #addFilter(ValueProvider, SerializablePredicate) - * - * @param valueProvider - * value provider that gets the property value, not - * <code>null</code> - * @param valueFilter - * filter for testing the property value, not <code>null</code> - */ - public <V> void setFilter(ValueProvider<T, V> valueProvider, - SerializablePredicate<V> valueFilter) { - setFilter(createValueProviderFilter(valueProvider, valueFilter)); - } - - /** - * Adds a filter for an item property. The filter will be used in addition - * to any filter that has been set or added previously. - * - * @see #addFilter(SerializablePredicate) - * @see #addFilterByValue(ValueProvider, Object) - * @see #setFilter(ValueProvider, SerializablePredicate) - * - * @param valueProvider - * value provider that gets the property value, not - * <code>null</code> - * @param valueFilter - * filter for testing the property value, not <code>null</code> - */ - public <V> void addFilter(ValueProvider<T, V> valueProvider, - SerializablePredicate<V> valueFilter) { - Objects.requireNonNull(valueProvider, "Value provider cannot be null"); - Objects.requireNonNull(valueFilter, "Value filter cannot be null"); - - addFilter(createValueProviderFilter(valueProvider, valueFilter)); - } - - private static <T, V> SerializablePredicate<T> createValueProviderFilter( - ValueProvider<T, V> valueProvider, - SerializablePredicate<V> valueFilter) { - return item -> valueFilter.test(valueProvider.apply(item)); - } - - /** - * Sets a filter that requires an item property to have a specific value. - * The property value and the provided value are compared using - * {@link Object#equals(Object)}. The filter replaces any filter that has - * been set or added previously. - * - * @see #setFilter(SerializablePredicate) - * @see #setFilter(ValueProvider, SerializablePredicate) - * @see #addFilterByValue(ValueProvider, Object) - * - * @param valueProvider - * value provider that gets the property value, not - * <code>null</code> - * @param requiredValue - * the value that the property must have for the filter to pass - */ - public <V> void setFilterByValue(ValueProvider<T, V> valueProvider, - V requiredValue) { - setFilter(createEqualsFilter(valueProvider, requiredValue)); - } - - /** - * Adds a filter that requires an item property to have a specific value. - * The property value and the provided value are compared using - * {@link Object#equals(Object)}.The filter will be used in addition to any - * filter that has been set or added previously. - * - * @see #setFilterByValue(ValueProvider, Object) - * @see #addFilter(SerializablePredicate) - * @see #addFilter(ValueProvider, SerializablePredicate) - * - * @param valueProvider - * value provider that gets the property value, not - * <code>null</code> - * @param requiredValue - * the value that the property must have for the filter to pass - */ - public <V> void addFilterByValue(ValueProvider<T, V> valueProvider, - V requiredValue) { - addFilter(createEqualsFilter(valueProvider, requiredValue)); - } - - private static <T, V> SerializablePredicate<T> createEqualsFilter( - ValueProvider<T, V> valueProvider, V requiredValue) { - Objects.requireNonNull(valueProvider, "Value provider cannot be null"); - - return item -> Objects.equals(valueProvider.apply(item), requiredValue); - } - - /** - * Wraps this data provider to create a new data provider that is filtered - * by comparing an item to the filter value provided in the query. - * <p> - * The predicate receives the item as the first parameter and the query - * filter value as the second parameter, and should return <code>true</code> - * if the corresponding item should be included. The query filter value is - * never <code>null</code> – all items are included without running the - * predicate if the query doesn't define any filter. - * - * @param predicate - * a predicate to use for comparing the item to the query filter, - * not <code>null</code> - * - * @return a data provider that filters accordingly, not <code>null</code> - */ - public <Q> DataProvider<T, Q> filteringBy( - SerializableBiPredicate<T, Q> predicate) { - Objects.requireNonNull(predicate, "Predicate cannot be null"); - - return withConvertedFilter( - filterValue -> item -> predicate.test(item, filterValue)); - } - - /** - * Wraps this data provider to create a new data provider that is filtered - * by comparing an item property value to the filter value provided in the - * query. - * <p> - * The predicate receives the property value as the first parameter and the - * query filter value as the second parameter, and should return - * <code>true</code> if the corresponding item should be included. The query - * filter value is never <code>null</code> – all items are included without - * running either callback if the query doesn't define any filter. - * - * @param valueProvider - * a value provider that gets the property value, not - * <code>null</code> - * @param predicate - * a predicate to use for comparing the property value to the - * query filter, not <code>null</code> - * - * @return a data provider that filters accordingly, not <code>null</code> - */ - public <V, Q> DataProvider<T, Q> filteringBy( - ValueProvider<T, V> valueProvider, - SerializableBiPredicate<V, Q> predicate) { - Objects.requireNonNull(valueProvider, "Value provider cannot be null"); - Objects.requireNonNull(predicate, "Predicate cannot be null"); - - return filteringBy((item, filterValue) -> predicate - .test(valueProvider.apply(item), filterValue)); - } - - /** - * Wraps this data provider to create a new data provider that is filtered - * by testing whether the value of a property is equals to the filter value - * provided in the query. Equality is tested using - * {@link Objects#equals(Object, Object)}. - * - * @param valueProvider - * a value provider that gets the property value, not - * <code>null</code> - * - * @return a data provider that filters accordingly, not <code>null</code> - */ - public <V> DataProvider<T, V> filteringByEquals( - ValueProvider<T, V> valueProvider) { - return filteringBy(valueProvider, Objects::equals); - } - - private <V, Q> DataProvider<T, Q> filteringByIgnoreNull( - ValueProvider<T, V> valueProvider, - SerializableBiPredicate<V, Q> predicate) { - Objects.requireNonNull(predicate, "Predicate cannot be null"); - - return filteringBy(valueProvider, - (itemValue, queryFilter) -> itemValue != null - && predicate.test(itemValue, queryFilter)); - } - - /** - * Wraps this data provider to create a new data provider that is filtered - * by a string by checking whether the lower case representation of the - * filter value provided in the query is a substring of the lower case - * representation of an item property value. The filter never passes if the - * item property value is <code>null</code>. - * - * @param valueProvider - * a value provider that gets the string property value, not - * <code>null</code> - * @param locale - * the locale to use for converting the strings to lower case, - * not <code>null</code> - * @return a data provider that filters accordingly, not <code>null</code> - */ - public DataProvider<T, String> filteringBySubstring( - ValueProvider<T, String> valueProvider, Locale locale) { - Objects.requireNonNull(locale, "Locale cannot be null"); - return filteringByCaseInsensitiveString(valueProvider, String::contains, - () -> locale); - } - - /** - * Wraps this data provider to create a new data provider that is filtered - * by a string by checking whether the lower case representation of the - * filter value provided in the query is a substring of the lower case - * representation of an item property value. Conversion to lower case is - * done using the locale of the {@link UI#getCurrent() current UI} if - * available, or otherwise {@link Locale#getDefault() the default locale}. - * The filter never passes if the item property value is <code>null</code>. - * - * @param valueProvider - * a value provider that gets the string property value, not - * <code>null</code> - * @return a data provider that filters accordingly, not <code>null</code> - */ - public DataProvider<T, String> filteringBySubstring( - ValueProvider<T, String> valueProvider) { - return filteringByCaseInsensitiveString(valueProvider, String::contains, - CURRENT_LOCALE_SUPPLIER); - } - - /** - * Wraps this data provider to create a new data provider that is filtered - * by a string by checking whether the lower case representation of an item - * property value starts with the lower case representation of the filter - * value provided in the query. The filter never passes if the item property - * value is <code>null</code>. - * - * @param valueProvider - * a value provider that gets the string property value, not - * <code>null</code> - * @param locale - * the locale to use for converting the strings to lower case, - * not <code>null</code> - * @return a data provider that filters accordingly, not <code>null</code> - */ - public DataProvider<T, String> filteringByPrefix( - ValueProvider<T, String> valueProvider, Locale locale) { - return filteringByCaseInsensitiveString(valueProvider, - String::startsWith, () -> locale); - } - - /** - * Wraps this data provider to create a new data provider that is filtered - * by a string by checking whether the lower case representation of an item - * property value starts with the lower case representation of the filter - * value provided in the query. Conversion to lower case is done using the - * locale of the {@link UI#getCurrent() current UI} if available, or - * otherwise {@link Locale#getDefault() the default locale}. The filter - * never passes if the item property value is <code>null</code>. - * - * @param valueProvider - * a value provider that gets the string property value, not - * <code>null</code> - * @return a data provider that filters accordingly, not <code>null</code> - */ - public DataProvider<T, String> filteringByPrefix( - ValueProvider<T, String> valueProvider) { - return filteringByCaseInsensitiveString(valueProvider, - String::startsWith, CURRENT_LOCALE_SUPPLIER); - } - - private DataProvider<T, String> filteringByCaseInsensitiveString( - ValueProvider<T, String> valueProvider, - SerializableBiPredicate<String, String> predicate, - SerializableSupplier<Locale> localeSupplier) { - // Only assert since these are only passed from our own code - assert predicate != null; - assert localeSupplier != null; - - return filteringByIgnoreNull(valueProvider, - (itemString, filterString) -> { - Locale locale = localeSupplier.get(); - assert locale != null; - - return predicate.test(itemString.toLowerCase(locale), - filterString.toLowerCase(locale)); - }); - } } diff --git a/server/src/main/java/com/vaadin/data/provider/InMemoryHierarchicalDataProvider.java b/server/src/main/java/com/vaadin/data/provider/TreeDataProvider.java index 8f70dd8e8c..b30a286afa 100644 --- a/server/src/main/java/com/vaadin/data/provider/InMemoryHierarchicalDataProvider.java +++ b/server/src/main/java/com/vaadin/data/provider/TreeDataProvider.java @@ -20,17 +20,13 @@ import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; -import com.vaadin.data.HierarchyData; -import com.vaadin.data.ValueProvider; +import com.vaadin.data.TreeData; import com.vaadin.server.SerializableComparator; import com.vaadin.server.SerializableFunction; import com.vaadin.server.SerializablePredicate; -import com.vaadin.shared.data.sort.SortDirection; /** - * A {@link DataProvider} for in-memory hierarchical data. - * - * @see HierarchyData + * {@link HierarchicalDataProvider} wrapper for {@link TreeData}. * * @author Vaadin Ltd * @since 8.1 @@ -38,27 +34,27 @@ import com.vaadin.shared.data.sort.SortDirection; * @param <T> * data type */ -public class InMemoryHierarchicalDataProvider<T> extends - AbstractHierarchicalDataProvider<T, SerializablePredicate<T>> implements - ConfigurableFilterDataProvider<T, SerializablePredicate<T>, SerializablePredicate<T>> { +public class TreeDataProvider<T> + extends AbstractHierarchicalDataProvider<T, SerializablePredicate<T>> + implements InMemoryDataProvider<T> { - private final HierarchyData<T> hierarchyData; + private final TreeData<T> treeData; private SerializablePredicate<T> filter = null; private SerializableComparator<T> sortOrder = null; /** - * Constructs a new InMemoryHierarchicalDataProvider. + * Constructs a new TreeDataProvider. * <p> - * All changes made to the given HierarchyData object will also be visible - * through this data provider. + * All changes made to the given {@link TreeData} object will also be + * visible through this data provider. * - * @param hierarchyData - * the backing HierarchyData for this provider + * @param treeData + * the backing {@link TreeData} for this provider */ - public InMemoryHierarchicalDataProvider(HierarchyData<T> hierarchyData) { - this.hierarchyData = hierarchyData; + public TreeDataProvider(TreeData<T> treeData) { + this.treeData = treeData; } /** @@ -66,24 +62,19 @@ public class InMemoryHierarchicalDataProvider<T> extends * * @return the underlying data of this provider */ - public HierarchyData<T> getData() { - return hierarchyData; - } - - @Override - public boolean isInMemory() { - return true; + public TreeData<T> getTreeData() { + return treeData; } @Override public boolean hasChildren(T item) { - if (!hierarchyData.contains(item)) { + if (!treeData.contains(item)) { throw new IllegalArgumentException("Item " + item - + " could not be found in the backing HierarchyData. " + + " could not be found in the backing TreeData. " + "Did you forget to refresh this data provider after item removal?"); } - return !hierarchyData.getChildren(item).isEmpty(); + return !treeData.getChildren(item).isEmpty(); } @Override @@ -95,15 +86,15 @@ public class InMemoryHierarchicalDataProvider<T> extends @Override public Stream<T> fetchChildren( HierarchicalQuery<T, SerializablePredicate<T>> query) { - if (!hierarchyData.contains(query.getParent())) { + if (!treeData.contains(query.getParent())) { throw new IllegalArgumentException("The queried item " + query.getParent() - + " could not be found in the backing HierarchyData. " + + " could not be found in the backing TreeData. " + "Did you forget to refresh this data provider after item removal?"); } Stream<T> childStream = getFilteredStream( - hierarchyData.getChildren(query.getParent()).stream(), + treeData.getChildren(query.getParent()).stream(), query.getFilter()); Optional<Comparator<T>> comparing = Stream @@ -119,87 +110,27 @@ public class InMemoryHierarchicalDataProvider<T> extends } @Override + public SerializablePredicate<T> getFilter() { + return filter; + } + + @Override public void setFilter(SerializablePredicate<T> filter) { this.filter = filter; refreshAll(); } - /** - * Adds a filter to be applied to all queries. The filter will be used in - * addition to any filter that has been set or added previously. - * - * @see #addFilter(ValueProvider, SerializablePredicate) - * @see #addFilterByValue(ValueProvider, Object) - * @see #setFilter(SerializablePredicate) - * - * @param filter - * the filter to add, not <code>null</code> - */ - public void addFilter(SerializablePredicate<T> filter) { - Objects.requireNonNull(filter, "Filter cannot be null"); - - if (this.filter == null) { - setFilter(filter); - } else { - SerializablePredicate<T> oldFilter = this.filter; - setFilter(item -> oldFilter.test(item) && filter.test(item)); - } + @Override + public SerializableComparator<T> getSortComparator() { + return sortOrder; } - /** - * 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> - * 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 comparator - * a comparator to use, or <code>null</code> to clear any - * previously set sort order - */ + @Override public void setSortComparator(SerializableComparator<T> comparator) { sortOrder = comparator; refreshAll(); } - /** - * 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> - * 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 comparator - * a comparator to add, not <code>null</code> - */ - public void addSortComparator(SerializableComparator<T> comparator) { - Objects.requireNonNull(comparator, "Sort order to add cannot be null"); - SerializableComparator<T> originalComparator = sortOrder; - if (originalComparator == null) { - setSortComparator(comparator); - } else { - setSortComparator((a, b) -> { - int result = originalComparator.compare(a, b); - if (result == 0) { - result = comparator.compare(a, b); - } - return result; - }); - } - } - @Override public <C> DataProvider<T, C> withConvertedFilter( SerializableFunction<C, SerializablePredicate<T>> filterConverter) { diff --git a/server/src/main/java/com/vaadin/ui/Tree.java b/server/src/main/java/com/vaadin/ui/Tree.java index 97e1b94cec..9a765238c1 100644 --- a/server/src/main/java/com/vaadin/ui/Tree.java +++ b/server/src/main/java/com/vaadin/ui/Tree.java @@ -24,7 +24,7 @@ import java.util.Objects; import java.util.Set; import com.vaadin.data.Binder; -import com.vaadin.data.HasDataProvider; +import com.vaadin.data.HasHierarchicalDataProvider; import com.vaadin.data.SelectionModel; import com.vaadin.data.provider.DataGenerator; import com.vaadin.data.provider.DataProvider; @@ -57,7 +57,8 @@ import elemental.json.JsonObject; * @param <T> * the data type */ -public class Tree<T> extends Composite implements HasDataProvider<T> { +public class Tree<T> extends Composite + implements HasHierarchicalDataProvider<T> { @Deprecated private static final Method ITEM_CLICK_METHOD = ReflectTools @@ -422,11 +423,6 @@ public class Tree<T> extends Composite implements HasDataProvider<T> { return treeGrid.getSelectionModel(); } - @Override - public void setItems(Collection<T> items) { - treeGrid.setItems(items); - } - /** * Sets the item caption generator that is used to produce the strings shown * as the text for each item. By default, {@link String#valueOf(Object)} is diff --git a/server/src/main/java/com/vaadin/ui/TreeGrid.java b/server/src/main/java/com/vaadin/ui/TreeGrid.java index 720bffaf3e..1b051d1da6 100644 --- a/server/src/main/java/com/vaadin/ui/TreeGrid.java +++ b/server/src/main/java/com/vaadin/ui/TreeGrid.java @@ -21,19 +21,19 @@ import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.stream.Stream; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; -import com.vaadin.data.HierarchyData; +import com.vaadin.data.HasHierarchicalDataProvider; +import com.vaadin.data.TreeData; import com.vaadin.data.ValueProvider; import com.vaadin.data.provider.DataProvider; import com.vaadin.data.provider.HierarchicalDataCommunicator; import com.vaadin.data.provider.HierarchicalDataProvider; import com.vaadin.data.provider.HierarchicalQuery; -import com.vaadin.data.provider.InMemoryHierarchicalDataProvider; +import com.vaadin.data.provider.TreeDataProvider; import com.vaadin.event.CollapseEvent; import com.vaadin.event.CollapseEvent.CollapseListener; import com.vaadin.event.ExpandEvent; @@ -60,7 +60,8 @@ import com.vaadin.ui.renderers.Renderer; * @param <T> * the grid bean type */ -public class TreeGrid<T> extends Grid<T> { +public class TreeGrid<T> extends Grid<T> + implements HasHierarchicalDataProvider<T> { public TreeGrid() { super(new HierarchicalDataCommunicator<>()); @@ -125,117 +126,6 @@ public class TreeGrid<T> extends Grid<T> { CollapseListener.COLLAPSE_METHOD); } - /** - * Sets the data items of this component provided as a collection. - * <p> - * The provided items are wrapped into a - * {@link InMemoryHierarchicalDataProvider} backed by a flat - * {@link HierarchyData} structure. The data provider instance is used as a - * parameter for the {@link #setDataProvider(DataProvider)} method. It means - * that the items collection can be accessed later on via - * {@link InMemoryHierarchicalDataProvider#getData()}: - * - * <pre> - * <code> - * TreeGrid<String> treeGrid = new TreeGrid<>(); - * treeGrid.setItems(Arrays.asList("a","b")); - * ... - * - * HierarchyData<String> data = ((InMemoryHierarchicalDataProvider<String>)treeGrid.getDataProvider()).getData(); - * </code> - * </pre> - * <p> - * The returned HierarchyData instance may be used as-is to add, remove or - * modify items in the hierarchy. These modifications to the object are not - * automatically reflected back to the TreeGrid. Items modified should be - * refreshed with {@link HierarchicalDataProvider#refreshItem(Object)} and - * when adding or removing items - * {@link HierarchicalDataProvider#refreshAll()} should be called. - * - * @param items - * the data items to display, not null - */ - @Override - public void setItems(Collection<T> items) { - Objects.requireNonNull(items, "Given collection may not be null"); - setDataProvider(new InMemoryHierarchicalDataProvider<>( - new HierarchyData<T>().addItems(null, items))); - } - - /** - * Sets the data items of this component provided as a stream. - * <p> - * The provided items are wrapped into a - * {@link InMemoryHierarchicalDataProvider} backed by a flat - * {@link HierarchyData} structure. The data provider instance is used as a - * parameter for the {@link #setDataProvider(DataProvider)} method. It means - * that the items collection can be accessed later on via - * {@link InMemoryHierarchicalDataProvider#getData()}: - * - * <pre> - * <code> - * TreeGrid<String> treeGrid = new TreeGrid<>(); - * treeGrid.setItems(Stream.of("a","b")); - * ... - * - * HierarchyData<String> data = ((InMemoryHierarchicalDataProvider<String>)treeGrid.getDataProvider()).getData(); - * </code> - * </pre> - * <p> - * The returned HierarchyData instance may be used as-is to add, remove or - * modify items in the hierarchy. These modifications to the object are not - * automatically reflected back to the TreeGrid. Items modified should be - * refreshed with {@link HierarchicalDataProvider#refreshItem(Object)} and - * when adding or removing items - * {@link HierarchicalDataProvider#refreshAll()} should be called. - * - * @param items - * the data items to display, not null - */ - @Override - public void setItems(Stream<T> items) { - Objects.requireNonNull(items, "Given stream may not be null"); - setDataProvider(new InMemoryHierarchicalDataProvider<>( - new HierarchyData<T>().addItems(null, items))); - } - - /** - * Sets the data items of this listing. - * <p> - * The provided items are wrapped into a - * {@link InMemoryHierarchicalDataProvider} backed by a flat - * {@link HierarchyData} structure. The data provider instance is used as a - * parameter for the {@link #setDataProvider(DataProvider)} method. It means - * that the items collection can be accessed later on via - * {@link InMemoryHierarchicalDataProvider#getData()}: - * - * <pre> - * <code> - * TreeGrid<String> treeGrid = new TreeGrid<>(); - * treeGrid.setItems("a","b"); - * ... - * - * HierarchyData<String> data = ((InMemoryHierarchicalDataProvider<String>)treeGrid.getDataProvider()).getData(); - * </code> - * </pre> - * <p> - * The returned HierarchyData instance may be used as-is to add, remove or - * modify items in the hierarchy. These modifications to the object are not - * automatically reflected back to the TreeGrid. Items modified should be - * refreshed with {@link HierarchicalDataProvider#refreshItem(Object)} and - * when adding or removing items - * {@link HierarchicalDataProvider#refreshAll()} should be called. - * - * @param items - * the data items to display, not null - */ - @Override - public void setItems(@SuppressWarnings("unchecked") T... items) { - Objects.requireNonNull(items, "Given items may not be null"); - setDataProvider(new InMemoryHierarchicalDataProvider<>( - new HierarchyData<T>().addItems(null, items))); - } - @Override public void setDataProvider(DataProvider<T, ?> dataProvider) { if (!(dataProvider instanceof HierarchicalDataProvider)) { @@ -395,7 +285,7 @@ public class TreeGrid<T> extends Grid<T> { List<DeclarativeValueProvider<T>> providers) { getSelectionModel().deselectAll(); List<T> selectedItems = new ArrayList<>(); - HierarchyData<T> data = new HierarchyData<T>(); + TreeData<T> data = new TreeData<T>(); for (Element row : body.children()) { T item = deserializeDeclarativeRepresentation(row.attr("item")); @@ -416,7 +306,7 @@ public class TreeGrid<T> extends Grid<T> { } } - setDataProvider(new InMemoryHierarchicalDataProvider<>(data)); + setDataProvider(new TreeDataProvider<>(data)); selectedItems.forEach(getSelectionModel()::select); } |