From efa7f5a4d069556061ba4ceef4fb4d76dae84ef4 Mon Sep 17 00:00:00 2001 From: Aleksi Hietanen Date: Tue, 16 May 2017 11:57:02 +0300 Subject: 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. --- .../vaadin/data/HasHierarchicalDataProvider.java | 207 +++++++++ .../main/java/com/vaadin/data/HierarchyData.java | 278 ------------ server/src/main/java/com/vaadin/data/TreeData.java | 309 ++++++++++++++ .../provider/HierarchicalDataCommunicator.java | 8 +- .../vaadin/data/provider/InMemoryDataProvider.java | 451 ++++++++++++++++++++ .../data/provider/InMemoryDataProviderHelpers.java | 185 ++++++++ .../provider/InMemoryHierarchicalDataProvider.java | 248 ----------- .../com/vaadin/data/provider/ListDataProvider.java | 471 +-------------------- .../com/vaadin/data/provider/TreeDataProvider.java | 179 ++++++++ server/src/main/java/com/vaadin/ui/Tree.java | 10 +- server/src/main/java/com/vaadin/ui/TreeGrid.java | 124 +----- 11 files changed, 1356 insertions(+), 1114 deletions(-) create mode 100644 server/src/main/java/com/vaadin/data/HasHierarchicalDataProvider.java delete mode 100644 server/src/main/java/com/vaadin/data/HierarchyData.java create mode 100644 server/src/main/java/com/vaadin/data/TreeData.java create mode 100644 server/src/main/java/com/vaadin/data/provider/InMemoryDataProvider.java create mode 100644 server/src/main/java/com/vaadin/data/provider/InMemoryDataProviderHelpers.java delete mode 100644 server/src/main/java/com/vaadin/data/provider/InMemoryHierarchicalDataProvider.java create mode 100644 server/src/main/java/com/vaadin/data/provider/TreeDataProvider.java (limited to 'server/src/main/java') 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 + * the item data type + */ +public interface HasHierarchicalDataProvider extends HasDataProvider { + + /** + * 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 getTreeData() { + if (getDataProvider() instanceof TreeDataProvider) { + return ((TreeDataProvider) 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. + *

+ * 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()}: + * + *

+     * 
+     * Collection grandParents = getGrandParents();
+     * HasHierarchicalDataProvider treeGrid = new TreeGrid<>();
+     * treeGrid.setItems(grandParents, Person::getChildren);
+     * ...
+     *
+     * TreeData data = treeGrid.getTreeData();
+     * 
+     * 
+ *

+ * 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 rootItems, + ValueProvider> 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().addItems(rootItems, childItemProvider))); + } + + /** + * Sets the data items of this component provided as a collection. + *

+ * 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()}: + * + *

+     * 
+     * HasHierarchicalDataProvider treeGrid = new TreeGrid<>();
+     * treeGrid.setItems(Arrays.asList("a","b"));
+     * ...
+     *
+     * TreeData data = treeGrid.getTreeData();
+     * 
+     * 
+ *

+ * 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 items) { + Objects.requireNonNull(items, "Given collection may not be null"); + setDataProvider(new TreeDataProvider<>( + new TreeData().addItems(null, items))); + } + + /** + * Sets the data items of this component provided as a stream. + *

+ * 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()}: + * + *

+     * 
+     * HasHierarchicalDataProvider treeGrid = new TreeGrid<>();
+     * treeGrid.setItems(Stream.of("a","b"));
+     * ...
+     *
+     * TreeData data = treeGrid.getTreeData();
+     * 
+     * 
+ *

+ * 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 items) { + Objects.requireNonNull(items, "Given stream may not be null"); + setItems(items.collect(Collectors.toList())); + } + + /** + * Sets the data items of this listing. + *

+ * 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()}: + * + *

+     * 
+     * TreeGrid treeGrid = new TreeGrid<>();
+     * treeGrid.setItems("a","b");
+     * ...
+     *
+     * TreeData data = treeGrid.getTreeData();
+     * 
+     * 
+ *

+ * 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/HierarchyData.java deleted file mode 100644 index 0cf680db2f..0000000000 --- a/server/src/main/java/com/vaadin/data/HierarchyData.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * 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.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Stream; - -/** - * Class for representing hierarchical data. - * - * @author Vaadin Ltd - * @since 8.1 - * - * @param - * data type - */ -public class HierarchyData implements Serializable { - - private static class HierarchyWrapper implements Serializable { - private T item; - private T parent; - private List children; - - public HierarchyWrapper(T item, T parent) { - this.item = item; - this.parent = parent; - children = new ArrayList<>(); - } - - public T getItem() { - return item; - } - - public void setItem(T item) { - this.item = item; - } - - public T getParent() { - return parent; - } - - public void setParent(T parent) { - this.parent = parent; - } - - public List getChildren() { - return children; - } - - public void setChildren(List children) { - this.children = children; - } - - public void addChild(T child) { - children.add(child); - } - - public void removeChild(T child) { - children.remove(child); - } - } - - private final Map> itemToWrapperMap; - - /** - * Creates an initially empty hierarchical data representation to which - * items can be added or removed. - */ - public HierarchyData() { - itemToWrapperMap = new LinkedHashMap<>(); - itemToWrapperMap.put(null, new HierarchyWrapper<>(null, null)); - } - - /** - * Adds a data item as a child of {@code parent}. Call with {@code null} as - * parent to add a root level item. The given parent item must already exist - * in this structure, and an item can only be added to this structure once. - * - * @param parent - * the parent item for which the items are added as children - * @param item - * the item to add - * @return this - * - * @throws IllegalArgumentException - * if parent is not null and not already added to this structure - * @throws IllegalArgumentException - * if the item has already been added to this structure - * @throws NullPointerException - * if item is null - */ - public HierarchyData addItem(T parent, T item) { - Objects.requireNonNull(item, "Item cannot be null"); - if (parent != null && !contains(parent)) { - throw new IllegalArgumentException( - "Parent needs to be added before children. " - + "To add root items, call with parent as null"); - } - if (contains(item)) { - throw new IllegalArgumentException( - "Cannot add the same item multiple times: " + item); - } - putItem(item, parent); - return this; - } - - /** - * Adds a list of data items as children of {@code parent}. Call with - * {@code null} as parent to add root level items. The given parent item - * must already exist in this structure, and an item can only be added to - * this structure once. - * - * @param parent - * the parent item for which the items are added as children - * @param items - * the list of items to add - * @return this - * - * @throws IllegalArgumentException - * if parent is not null and not already added to this structure - * @throws IllegalArgumentException - * if any of the given items have already been added to this - * structure - * @throws NullPointerException - * if any of the items are null - */ - public HierarchyData addItems(T parent, - @SuppressWarnings("unchecked") T... items) { - Arrays.asList(items).stream().forEach(item -> addItem(parent, item)); - return this; - } - - /** - * Adds a list of data items as children of {@code parent}. Call with - * {@code null} as parent to add root level items. The given parent item - * must already exist in this structure, and an item can only be added to - * this structure once. - * - * @param parent - * the parent item for which the items are added as children - * @param items - * the collection of items to add - * @return this - * - * @throws IllegalArgumentException - * if parent is not null and not already added to this structure - * @throws IllegalArgumentException - * if any of the given items have already been added to this - * structure - * @throws NullPointerException - * if any of the items are null - */ - public HierarchyData addItems(T parent, Collection items) { - items.stream().forEach(item -> addItem(parent, item)); - return this; - } - - /** - * Adds data items contained in a stream as children of {@code parent}. Call - * with {@code null} as parent to add root level items. The given parent - * item must already exist in this structure, and an item can only be added - * to this structure once. - * - * @param parent - * the parent item for which the items are added as children - * @param items - * stream of items to add - * @return this - * - * @throws IllegalArgumentException - * if parent is not null and not already added to this structure - * @throws IllegalArgumentException - * if any of the given items have already been added to this - * structure - * @throws NullPointerException - * if any of the items are null - */ - public HierarchyData addItems(T parent, Stream items) { - items.forEach(item -> addItem(parent, item)); - return this; - } - - /** - * Remove a given item from this structure. Additionally, this will - * recursively remove any descendants of the item. - * - * @param item - * the item to remove, or null to clear all data - * @return this - * - * @throws IllegalArgumentException - * if the item does not exist in this structure - */ - public HierarchyData removeItem(T item) { - if (!contains(item)) { - throw new IllegalArgumentException( - "Item '" + item + "' not in the hierarchy"); - } - new ArrayList<>(getChildren(item)).forEach(child -> removeItem(child)); - itemToWrapperMap.get(itemToWrapperMap.get(item).getParent()) - .removeChild(item); - if (item != null) { - // remove non root item from backing map - itemToWrapperMap.remove(item); - } - return this; - } - - /** - * Clear all items from this structure. Shorthand for calling - * {@link #removeItem(Object)} with null. - * - * @return this - */ - public HierarchyData clear() { - removeItem(null); - return this; - } - - /** - * Get the immediate child items for the given item. - * - * @param item - * the item for which to retrieve child items for, null to - * retrieve all root items - * @return a list of child items for the given item - * - * @throws IllegalArgumentException - * if the item does not exist in this structure - */ - public List getChildren(T item) { - if (!contains(item)) { - throw new IllegalArgumentException( - "Item '" + item + "' not in the hierarchy"); - } - return itemToWrapperMap.get(item).getChildren(); - } - - /** - * Check whether the given item is in this hierarchy. - * - * @param item - * the item to check - * @return {@code true} if the item is in this hierarchy, {@code false} if - * not - */ - public boolean contains(T item) { - return itemToWrapperMap.containsKey(item); - } - - private void putItem(T item, T parent) { - HierarchyWrapper wrappedItem = new HierarchyWrapper<>(item, parent); - if (itemToWrapperMap.containsKey(parent)) { - itemToWrapperMap.get(parent).addChild(item); - } - itemToWrapperMap.put(item, wrappedItem); - } -} diff --git a/server/src/main/java/com/vaadin/data/TreeData.java b/server/src/main/java/com/vaadin/data/TreeData.java new file mode 100644 index 0000000000..def69745ef --- /dev/null +++ b/server/src/main/java/com/vaadin/data/TreeData.java @@ -0,0 +1,309 @@ +/* + * 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.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + +/** + * Class for representing hierarchical data. + * + * @author Vaadin Ltd + * @since 8.1 + * + * @param + * data type + */ +public class TreeData implements Serializable { + + private static class HierarchyWrapper implements Serializable { + private T item; + private T parent; + private List children; + + public HierarchyWrapper(T item, T parent) { + this.item = item; + this.parent = parent; + children = new ArrayList<>(); + } + + public T getItem() { + return item; + } + + public void setItem(T item) { + this.item = item; + } + + public T getParent() { + return parent; + } + + public void setParent(T parent) { + this.parent = parent; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + + public void addChild(T child) { + children.add(child); + } + + public void removeChild(T child) { + children.remove(child); + } + } + + private final Map> itemToWrapperMap; + + /** + * Creates an initially empty hierarchical data representation to which + * items can be added or removed. + */ + public TreeData() { + itemToWrapperMap = new LinkedHashMap<>(); + itemToWrapperMap.put(null, new HierarchyWrapper<>(null, null)); + } + + /** + * Adds a data item as a child of {@code parent}. Call with {@code null} as + * parent to add a root level item. The given parent item must already exist + * in this structure, and an item can only be added to this structure once. + * + * @param parent + * the parent item for which the items are added as children + * @param item + * the item to add + * @return this + * + * @throws IllegalArgumentException + * if parent is not null and not already added to this structure + * @throws IllegalArgumentException + * if the item has already been added to this structure + * @throws NullPointerException + * if item is null + */ + public TreeData addItem(T parent, T item) { + Objects.requireNonNull(item, "Item cannot be null"); + if (parent != null && !contains(parent)) { + throw new IllegalArgumentException( + "Parent needs to be added before children. " + + "To add root items, call with parent as null"); + } + if (contains(item)) { + throw new IllegalArgumentException( + "Cannot add the same item multiple times: " + item); + } + putItem(item, parent); + return this; + } + + /** + * Adds a list of data items as children of {@code parent}. Call with + * {@code null} as parent to add root level items. The given parent item + * must already exist in this structure, and an item can only be added to + * this structure once. + * + * @param parent + * the parent item for which the items are added as children + * @param items + * the list of items to add + * @return this + * + * @throws IllegalArgumentException + * if parent is not null and not already added to this structure + * @throws IllegalArgumentException + * if any of the given items have already been added to this + * structure + * @throws NullPointerException + * if any of the items are null + */ + public TreeData addItems(T parent, + @SuppressWarnings("unchecked") T... items) { + Arrays.asList(items).stream().forEach(item -> addItem(parent, item)); + return this; + } + + /** + * Adds a list of data items as children of {@code parent}. Call with + * {@code null} as parent to add root level items. The given parent item + * must already exist in this structure, and an item can only be added to + * this structure once. + * + * @param parent + * the parent item for which the items are added as children + * @param items + * the collection of items to add + * @return this + * + * @throws IllegalArgumentException + * if parent is not null and not already added to this structure + * @throws IllegalArgumentException + * if any of the given items have already been added to this + * structure + * @throws NullPointerException + * if any of the items are null + */ + public TreeData addItems(T parent, Collection items) { + items.stream().forEach(item -> addItem(parent, item)); + return this; + } + + /** + * Adds data items contained in a stream as children of {@code parent}. Call + * with {@code null} as parent to add root level items. The given parent + * item must already exist in this structure, and an item can only be added + * to this structure once. + * + * @param parent + * the parent item for which the items are added as children + * @param items + * stream of items to add + * @return this + * + * @throws IllegalArgumentException + * if parent is not null and not already added to this structure + * @throws IllegalArgumentException + * if any of the given items have already been added to this + * structure + * @throws NullPointerException + * if any of the items are null + */ + public TreeData addItems(T parent, Stream 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 addItems(Collection rootItems, + ValueProvider> childItemProvider) { + rootItems.forEach(item -> { + addItem(null, item); + Collection 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. + * + * @param item + * the item to remove, or null to clear all data + * @return this + * + * @throws IllegalArgumentException + * if the item does not exist in this structure + */ + public TreeData removeItem(T item) { + if (!contains(item)) { + throw new IllegalArgumentException( + "Item '" + item + "' not in the hierarchy"); + } + new ArrayList<>(getChildren(item)).forEach(child -> removeItem(child)); + itemToWrapperMap.get(itemToWrapperMap.get(item).getParent()) + .removeChild(item); + if (item != null) { + // remove non root item from backing map + itemToWrapperMap.remove(item); + } + return this; + } + + /** + * Clear all items from this structure. Shorthand for calling + * {@link #removeItem(Object)} with null. + * + * @return this + */ + public TreeData clear() { + removeItem(null); + return this; + } + + /** + * Get the immediate child items for the given item. + * + * @param item + * the item for which to retrieve child items for, null to + * retrieve all root items + * @return a list of child items for the given item + * + * @throws IllegalArgumentException + * if the item does not exist in this structure + */ + public List getChildren(T item) { + if (!contains(item)) { + throw new IllegalArgumentException( + "Item '" + item + "' not in the hierarchy"); + } + return itemToWrapperMap.get(item).getChildren(); + } + + /** + * Check whether the given item is in this hierarchy. + * + * @param item + * the item to check + * @return {@code true} if the item is in this hierarchy, {@code false} if + * not + */ + public boolean contains(T item) { + return itemToWrapperMap.containsKey(item); + } + + private void putItem(T item, T parent) { + HierarchyWrapper wrappedItem = new HierarchyWrapper<>(item, parent); + if (itemToWrapperMap.containsKey(parent)) { + itemToWrapperMap.get(parent).addChild(item); + } + itemToWrapperMap.put(item, wrappedItem); + } + + private void addItemsRecursively(Collection items, + ValueProvider> childItemProvider) { + items.forEach(item -> { + Collection 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 extends DataCommunicator { /** * 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 + * data type + */ +public interface InMemoryDataProvider extends + ConfigurableFilterDataProvider, SerializablePredicate> { + + @Override + public default boolean isInMemory() { + return true; + } + + /** + * Gets the current filter of this data provider. + * + * @return the filter of this data provider + */ + public SerializablePredicate 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 null to remove any set + * filters + */ + @Override + public void setFilter(SerializablePredicate 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 + * null + * @param valueFilter + * filter for testing the property value, not null + */ + public default void setFilter(ValueProvider valueProvider, + SerializablePredicate 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 null + */ + public default void addFilter(SerializablePredicate filter) { + Objects.requireNonNull(filter, "Filter cannot be null"); + + if (getFilter() == null) { + setFilter(filter); + } else { + SerializablePredicate 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 + * null + * @param valueFilter + * filter for testing the property value, not null + */ + public default void addFilter(ValueProvider valueProvider, + SerializablePredicate 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 + * null + * @param requiredValue + * the value that the property must have for the filter to pass + */ + public default void setFilterByValue(ValueProvider 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 + * null + * @param requiredValue + * the value that the property must have for the filter to pass + */ + public default void addFilterByValue(ValueProvider 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 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. + *

+ * 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 null to clear any + * previously set sort order + */ + public void setSortComparator(SerializableComparator 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. + *

+ * 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 null + */ + public default void addSortComparator( + SerializableComparator comparator) { + Objects.requireNonNull(comparator, "Comparator to add cannot be null"); + SerializableComparator 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. + *

+ * 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 + * null + * @param sortDirection + * the sort direction to use, not null + */ + public default > void setSortOrder( + ValueProvider 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. + *

+ * 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 + * null + * @param sortDirection + * the sort direction to use, not null + */ + public default > void addSortOrder( + ValueProvider 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. + *

+ * The predicate receives the item as the first parameter and the query + * filter value as the second parameter, and should return true + * if the corresponding item should be included. The query filter value is + * never null – 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 null + * + * @return a data provider that filters accordingly, not null + */ + public default DataProvider filteringBy( + SerializableBiPredicate 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. + *

+ * The predicate receives the property value as the first parameter and the + * query filter value as the second parameter, and should return + * true if the corresponding item should be included. The query + * filter value is never null – 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 + * null + * @param predicate + * a predicate to use for comparing the property value to the + * query filter, not null + * + * @return a data provider that filters accordingly, not null + */ + public default DataProvider filteringBy( + ValueProvider valueProvider, + SerializableBiPredicate 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 + * null + * + * @return a data provider that filters accordingly, not null + */ + public default DataProvider filteringByEquals( + ValueProvider 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 null. + * + * @param valueProvider + * a value provider that gets the string property value, not + * null + * @param locale + * the locale to use for converting the strings to lower case, + * not null + * @return a data provider that filters accordingly, not null + */ + public default DataProvider filteringBySubstring( + ValueProvider 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 null. + * + * @param valueProvider + * a value provider that gets the string property value, not + * null + * @return a data provider that filters accordingly, not null + */ + public default DataProvider filteringBySubstring( + ValueProvider 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 null. + * + * @param valueProvider + * a value provider that gets the string property value, not + * null + * @param locale + * the locale to use for converting the strings to lower case, + * not null + * @return a data provider that filters accordingly, not null + */ + public default DataProvider filteringByPrefix( + ValueProvider 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 null. + * + * @param valueProvider + * a value provider that gets the string property value, not + * null + * @return a data provider that filters accordingly, not null + */ + public default DataProvider filteringByPrefix( + ValueProvider 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. + *

+ * 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 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 DataProvider filteringByIgnoreNull( + InMemoryDataProvider dataProvider, + ValueProvider valueProvider, + SerializableBiPredicate 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 DataProvider filteringByCaseInsensitiveString( + InMemoryDataProvider dataProvider, + ValueProvider valueProvider, + SerializableBiPredicate predicate, + SerializableSupplier 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 , T> SerializableComparator propertyComparator( + ValueProvider valueProvider, SortDirection sortDirection) { + Objects.requireNonNull(valueProvider, "Value provider cannot be null"); + Objects.requireNonNull(sortDirection, "Sort direction cannot be null"); + + Comparator 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 > Comparator getNaturalSortComparator( + SortDirection sortDirection) { + Comparator 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 SerializablePredicate createValueProviderFilter( + ValueProvider valueProvider, + SerializablePredicate 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 SerializablePredicate createEqualsFilter( + ValueProvider 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/InMemoryHierarchicalDataProvider.java b/server/src/main/java/com/vaadin/data/provider/InMemoryHierarchicalDataProvider.java deleted file mode 100644 index 8f70dd8e8c..0000000000 --- a/server/src/main/java/com/vaadin/data/provider/InMemoryHierarchicalDataProvider.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * 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.Objects; -import java.util.Optional; -import java.util.stream.Stream; - -import com.vaadin.data.HierarchyData; -import com.vaadin.data.ValueProvider; -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 - * - * @author Vaadin Ltd - * @since 8.1 - * - * @param - * data type - */ -public class InMemoryHierarchicalDataProvider extends - AbstractHierarchicalDataProvider> implements - ConfigurableFilterDataProvider, SerializablePredicate> { - - private final HierarchyData hierarchyData; - - private SerializablePredicate filter = null; - - private SerializableComparator sortOrder = null; - - /** - * Constructs a new InMemoryHierarchicalDataProvider. - *

- * All changes made to the given HierarchyData object will also be visible - * through this data provider. - * - * @param hierarchyData - * the backing HierarchyData for this provider - */ - public InMemoryHierarchicalDataProvider(HierarchyData hierarchyData) { - this.hierarchyData = hierarchyData; - } - - /** - * Return the underlying hierarchical data of this provider. - * - * @return the underlying data of this provider - */ - public HierarchyData getData() { - return hierarchyData; - } - - @Override - public boolean isInMemory() { - return true; - } - - @Override - public boolean hasChildren(T item) { - if (!hierarchyData.contains(item)) { - throw new IllegalArgumentException("Item " + item - + " could not be found in the backing HierarchyData. " - + "Did you forget to refresh this data provider after item removal?"); - } - - return !hierarchyData.getChildren(item).isEmpty(); - } - - @Override - public int getChildCount( - HierarchicalQuery> query) { - return (int) fetchChildren(query).count(); - } - - @Override - public Stream fetchChildren( - HierarchicalQuery> query) { - if (!hierarchyData.contains(query.getParent())) { - throw new IllegalArgumentException("The queried item " - + query.getParent() - + " could not be found in the backing HierarchyData. " - + "Did you forget to refresh this data provider after item removal?"); - } - - Stream childStream = getFilteredStream( - hierarchyData.getChildren(query.getParent()).stream(), - query.getFilter()); - - Optional> comparing = Stream - .of(query.getInMemorySorting(), sortOrder) - .filter(c -> c != null) - .reduce((c1, c2) -> c1.thenComparing(c2)); - - if (comparing.isPresent()) { - childStream = childStream.sorted(comparing.get()); - } - - return childStream.skip(query.getOffset()).limit(query.getLimit()); - } - - @Override - public void setFilter(SerializablePredicate 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 null - */ - public void addFilter(SerializablePredicate filter) { - Objects.requireNonNull(filter, "Filter cannot be null"); - - if (this.filter == null) { - setFilter(filter); - } else { - SerializablePredicate oldFilter = this.filter; - setFilter(item -> oldFilter.test(item) && filter.test(item)); - } - } - - /** - * 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. - *

- * 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 null to clear any - * previously set sort order - */ - public void setSortComparator(SerializableComparator 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. - *

- * 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 null - */ - public void addSortComparator(SerializableComparator comparator) { - Objects.requireNonNull(comparator, "Sort order to add cannot be null"); - SerializableComparator 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 DataProvider withConvertedFilter( - SerializableFunction> filterConverter) { - Objects.requireNonNull(filterConverter, - "Filter converter can't be null"); - return new DataProviderWrapper>(this) { - - @Override - protected SerializablePredicate getFilter(Query query) { - return query.getFilter().map(filterConverter).orElse(null); - } - - @Override - public int size(Query t) { - if (t instanceof HierarchicalQuery) { - return dataProvider.size(new HierarchicalQuery<>( - t.getOffset(), t.getLimit(), t.getSortOrders(), - t.getInMemorySorting(), getFilter(t), - ((HierarchicalQuery) t).getParent())); - } - throw new IllegalArgumentException( - "Hierarchical data provider doesn't support non-hierarchical queries"); - } - - @Override - public Stream fetch(Query t) { - if (t instanceof HierarchicalQuery) { - return dataProvider.fetch(new HierarchicalQuery<>( - t.getOffset(), t.getLimit(), t.getSortOrders(), - t.getInMemorySorting(), getFilter(t), - ((HierarchicalQuery) t).getParent())); - } - throw new IllegalArgumentException( - "Hierarchical data provider doesn't support non-hierarchical queries"); - } - }; - } - - private Stream getFilteredStream(Stream stream, - Optional> queryFilter) { - if (filter != null) { - stream = stream.filter(filter); - } - return queryFilter.map(stream::filter).orElse(stream); - } -} 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 - extends AbstractDataProvider> implements - ConfigurableFilterDataProvider, SerializablePredicate> { - - private static final SerializableSupplier CURRENT_LOCALE_SUPPLIER = () -> { - UI currentUi = UI.getCurrent(); - if (currentUi != null) { - return currentUi.getLocale(); - } else { - return Locale.getDefault(); - } - }; + extends AbstractDataProvider> + implements InMemoryDataProvider { private SerializableComparator sortOrder = null; @@ -97,11 +82,6 @@ public class ListDataProvider return stream.skip(query.getOffset()).limit(query.getLimit()); } - @Override - public boolean isInMemory() { - return true; - } - @Override public int size(Query> query) { return (int) getFilteredStream(query).count(); @@ -122,454 +102,25 @@ public class ListDataProvider 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. - *

- * 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 null to clear any - * previously set sort order - */ + @Override + public SerializableComparator getSortComparator() { + return sortOrder; + } + + @Override public void setSortComparator(SerializableComparator 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. - *

- * 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 - * null - * @param sortDirection - * the sort direction to use, not null - */ - public > void setSortOrder( - ValueProvider 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. - *

- * 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 null - */ - public void addSortComparator(SerializableComparator comparator) { - Objects.requireNonNull(comparator, "Sort order to add cannot be null"); - - SerializableComparator 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. - *

- * 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 - * null - * @param sortDirection - * the sort direction to use, not null - */ - public > void addSortOrder( - ValueProvider valueProvider, SortDirection sortDirection) { - addSortComparator(propertyComparator(valueProvider, sortDirection)); - } - - private static , T> SerializableComparator propertyComparator( - ValueProvider valueProvider, SortDirection sortDirection) { - Objects.requireNonNull(valueProvider, "Value provider cannot be null"); - Objects.requireNonNull(sortDirection, "Sort direction cannot be null"); - - Comparator comparator = getNaturalSortComparator(sortDirection); - - return (a, b) -> comparator.compare(valueProvider.apply(a), - valueProvider.apply(b)); - } - - private static > Comparator getNaturalSortComparator( - SortDirection sortDirection) { - Comparator comparator = Comparator.naturalOrder(); - if (sortDirection == SortDirection.DESCENDING) { - comparator = comparator.reversed(); - } - return comparator; + @Override + public SerializablePredicate 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 null to remove any set - * filters - */ @Override public void setFilter(SerializablePredicate 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 null - */ - public void addFilter(SerializablePredicate filter) { - Objects.requireNonNull(filter, "Filter cannot be null"); - - if (this.filter == null) { - setFilter(filter); - } else { - SerializablePredicate 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 - * null - * @param valueFilter - * filter for testing the property value, not null - */ - public void setFilter(ValueProvider valueProvider, - SerializablePredicate 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 - * null - * @param valueFilter - * filter for testing the property value, not null - */ - public void addFilter(ValueProvider valueProvider, - SerializablePredicate valueFilter) { - Objects.requireNonNull(valueProvider, "Value provider cannot be null"); - Objects.requireNonNull(valueFilter, "Value filter cannot be null"); - - addFilter(createValueProviderFilter(valueProvider, valueFilter)); - } - - private static SerializablePredicate createValueProviderFilter( - ValueProvider valueProvider, - SerializablePredicate 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 - * null - * @param requiredValue - * the value that the property must have for the filter to pass - */ - public void setFilterByValue(ValueProvider 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 - * null - * @param requiredValue - * the value that the property must have for the filter to pass - */ - public void addFilterByValue(ValueProvider valueProvider, - V requiredValue) { - addFilter(createEqualsFilter(valueProvider, requiredValue)); - } - - private static SerializablePredicate createEqualsFilter( - ValueProvider 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. - *

- * The predicate receives the item as the first parameter and the query - * filter value as the second parameter, and should return true - * if the corresponding item should be included. The query filter value is - * never null – 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 null - * - * @return a data provider that filters accordingly, not null - */ - public DataProvider filteringBy( - SerializableBiPredicate 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. - *

- * The predicate receives the property value as the first parameter and the - * query filter value as the second parameter, and should return - * true if the corresponding item should be included. The query - * filter value is never null – 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 - * null - * @param predicate - * a predicate to use for comparing the property value to the - * query filter, not null - * - * @return a data provider that filters accordingly, not null - */ - public DataProvider filteringBy( - ValueProvider valueProvider, - SerializableBiPredicate 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 - * null - * - * @return a data provider that filters accordingly, not null - */ - public DataProvider filteringByEquals( - ValueProvider valueProvider) { - return filteringBy(valueProvider, Objects::equals); - } - - private DataProvider filteringByIgnoreNull( - ValueProvider valueProvider, - SerializableBiPredicate 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 null. - * - * @param valueProvider - * a value provider that gets the string property value, not - * null - * @param locale - * the locale to use for converting the strings to lower case, - * not null - * @return a data provider that filters accordingly, not null - */ - public DataProvider filteringBySubstring( - ValueProvider 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 null. - * - * @param valueProvider - * a value provider that gets the string property value, not - * null - * @return a data provider that filters accordingly, not null - */ - public DataProvider filteringBySubstring( - ValueProvider 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 null. - * - * @param valueProvider - * a value provider that gets the string property value, not - * null - * @param locale - * the locale to use for converting the strings to lower case, - * not null - * @return a data provider that filters accordingly, not null - */ - public DataProvider filteringByPrefix( - ValueProvider 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 null. - * - * @param valueProvider - * a value provider that gets the string property value, not - * null - * @return a data provider that filters accordingly, not null - */ - public DataProvider filteringByPrefix( - ValueProvider valueProvider) { - return filteringByCaseInsensitiveString(valueProvider, - String::startsWith, CURRENT_LOCALE_SUPPLIER); - } - - private DataProvider filteringByCaseInsensitiveString( - ValueProvider valueProvider, - SerializableBiPredicate predicate, - SerializableSupplier 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/TreeDataProvider.java b/server/src/main/java/com/vaadin/data/provider/TreeDataProvider.java new file mode 100644 index 0000000000..b30a286afa --- /dev/null +++ b/server/src/main/java/com/vaadin/data/provider/TreeDataProvider.java @@ -0,0 +1,179 @@ +/* + * 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.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +import com.vaadin.data.TreeData; +import com.vaadin.server.SerializableComparator; +import com.vaadin.server.SerializableFunction; +import com.vaadin.server.SerializablePredicate; + +/** + * {@link HierarchicalDataProvider} wrapper for {@link TreeData}. + * + * @author Vaadin Ltd + * @since 8.1 + * + * @param + * data type + */ +public class TreeDataProvider + extends AbstractHierarchicalDataProvider> + implements InMemoryDataProvider { + + private final TreeData treeData; + + private SerializablePredicate filter = null; + + private SerializableComparator sortOrder = null; + + /** + * Constructs a new TreeDataProvider. + *

+ * All changes made to the given {@link TreeData} object will also be + * visible through this data provider. + * + * @param treeData + * the backing {@link TreeData} for this provider + */ + public TreeDataProvider(TreeData treeData) { + this.treeData = treeData; + } + + /** + * Return the underlying hierarchical data of this provider. + * + * @return the underlying data of this provider + */ + public TreeData getTreeData() { + return treeData; + } + + @Override + public boolean hasChildren(T item) { + if (!treeData.contains(item)) { + throw new IllegalArgumentException("Item " + item + + " could not be found in the backing TreeData. " + + "Did you forget to refresh this data provider after item removal?"); + } + + return !treeData.getChildren(item).isEmpty(); + } + + @Override + public int getChildCount( + HierarchicalQuery> query) { + return (int) fetchChildren(query).count(); + } + + @Override + public Stream fetchChildren( + HierarchicalQuery> query) { + if (!treeData.contains(query.getParent())) { + throw new IllegalArgumentException("The queried item " + + query.getParent() + + " could not be found in the backing TreeData. " + + "Did you forget to refresh this data provider after item removal?"); + } + + Stream childStream = getFilteredStream( + treeData.getChildren(query.getParent()).stream(), + query.getFilter()); + + Optional> comparing = Stream + .of(query.getInMemorySorting(), sortOrder) + .filter(c -> c != null) + .reduce((c1, c2) -> c1.thenComparing(c2)); + + if (comparing.isPresent()) { + childStream = childStream.sorted(comparing.get()); + } + + return childStream.skip(query.getOffset()).limit(query.getLimit()); + } + + @Override + public SerializablePredicate getFilter() { + return filter; + } + + @Override + public void setFilter(SerializablePredicate filter) { + this.filter = filter; + refreshAll(); + } + + @Override + public SerializableComparator getSortComparator() { + return sortOrder; + } + + @Override + public void setSortComparator(SerializableComparator comparator) { + sortOrder = comparator; + refreshAll(); + } + + @Override + public DataProvider withConvertedFilter( + SerializableFunction> filterConverter) { + Objects.requireNonNull(filterConverter, + "Filter converter can't be null"); + return new DataProviderWrapper>(this) { + + @Override + protected SerializablePredicate getFilter(Query query) { + return query.getFilter().map(filterConverter).orElse(null); + } + + @Override + public int size(Query t) { + if (t instanceof HierarchicalQuery) { + return dataProvider.size(new HierarchicalQuery<>( + t.getOffset(), t.getLimit(), t.getSortOrders(), + t.getInMemorySorting(), getFilter(t), + ((HierarchicalQuery) t).getParent())); + } + throw new IllegalArgumentException( + "Hierarchical data provider doesn't support non-hierarchical queries"); + } + + @Override + public Stream fetch(Query t) { + if (t instanceof HierarchicalQuery) { + return dataProvider.fetch(new HierarchicalQuery<>( + t.getOffset(), t.getLimit(), t.getSortOrders(), + t.getInMemorySorting(), getFilter(t), + ((HierarchicalQuery) t).getParent())); + } + throw new IllegalArgumentException( + "Hierarchical data provider doesn't support non-hierarchical queries"); + } + }; + } + + private Stream getFilteredStream(Stream stream, + Optional> queryFilter) { + if (filter != null) { + stream = stream.filter(filter); + } + return queryFilter.map(stream::filter).orElse(stream); + } +} 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 * the data type */ -public class Tree extends Composite implements HasDataProvider { +public class Tree extends Composite + implements HasHierarchicalDataProvider { @Deprecated private static final Method ITEM_CLICK_METHOD = ReflectTools @@ -422,11 +423,6 @@ public class Tree extends Composite implements HasDataProvider { return treeGrid.getSelectionModel(); } - @Override - public void setItems(Collection 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 * the grid bean type */ -public class TreeGrid extends Grid { +public class TreeGrid extends Grid + implements HasHierarchicalDataProvider { public TreeGrid() { super(new HierarchicalDataCommunicator<>()); @@ -125,117 +126,6 @@ public class TreeGrid extends Grid { CollapseListener.COLLAPSE_METHOD); } - /** - * Sets the data items of this component provided as a collection. - *

- * 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()}: - * - *

-     * 
-     * TreeGrid treeGrid = new TreeGrid<>();
-     * treeGrid.setItems(Arrays.asList("a","b"));
-     * ...
-     *
-     * HierarchyData data = ((InMemoryHierarchicalDataProvider)treeGrid.getDataProvider()).getData();
-     * 
-     * 
- *

- * 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 items) { - Objects.requireNonNull(items, "Given collection may not be null"); - setDataProvider(new InMemoryHierarchicalDataProvider<>( - new HierarchyData().addItems(null, items))); - } - - /** - * Sets the data items of this component provided as a stream. - *

- * 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()}: - * - *

-     * 
-     * TreeGrid treeGrid = new TreeGrid<>();
-     * treeGrid.setItems(Stream.of("a","b"));
-     * ...
-     *
-     * HierarchyData data = ((InMemoryHierarchicalDataProvider)treeGrid.getDataProvider()).getData();
-     * 
-     * 
- *

- * 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 items) { - Objects.requireNonNull(items, "Given stream may not be null"); - setDataProvider(new InMemoryHierarchicalDataProvider<>( - new HierarchyData().addItems(null, items))); - } - - /** - * Sets the data items of this listing. - *

- * 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()}: - * - *

-     * 
-     * TreeGrid treeGrid = new TreeGrid<>();
-     * treeGrid.setItems("a","b");
-     * ...
-     *
-     * HierarchyData data = ((InMemoryHierarchicalDataProvider)treeGrid.getDataProvider()).getData();
-     * 
-     * 
- *

- * 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().addItems(null, items))); - } - @Override public void setDataProvider(DataProvider dataProvider) { if (!(dataProvider instanceof HierarchicalDataProvider)) { @@ -395,7 +285,7 @@ public class TreeGrid extends Grid { List> providers) { getSelectionModel().deselectAll(); List selectedItems = new ArrayList<>(); - HierarchyData data = new HierarchyData(); + TreeData data = new TreeData(); for (Element row : body.children()) { T item = deserializeDeclarativeRepresentation(row.attr("item")); @@ -416,7 +306,7 @@ public class TreeGrid extends Grid { } } - setDataProvider(new InMemoryHierarchicalDataProvider<>(data)); + setDataProvider(new TreeDataProvider<>(data)); selectedItems.forEach(getSelectionModel()::select); } -- cgit v1.2.3