From: Aleksi Hietanen Date: Tue, 16 May 2017 08:57:02 +0000 (+0300) Subject: Refactor common methods in in-memory data providers (#9308) X-Git-Tag: 8.1.0.alpha8~26 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=efa7f5a4d069556061ba4ceef4fb4d76dae84ef4;p=vaadin-framework.git 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. --- diff --git a/all/src/main/templates/release-notes.html b/all/src/main/templates/release-notes.html index 44b1690e7b..abfda6bf2e 100644 --- a/all/src/main/templates/release-notes.html +++ b/all/src/main/templates/release-notes.html @@ -102,7 +102,7 @@
  • The TreeGrid component replaces TreeTable component
  • The Tree component replaces old Tree component
  • -
  • The HierarchicalDataProvider interface replaces Container.Hierarchical and <InMemoryHierarchicalDataProvider replaces HierarchicalContainer
  • +
  • The HierarchicalDataProvider interface replaces Container.Hierarchical and <TreeDataProvider replaces HierarchicalContainer
  • The DragSourceExtension and DropTargetExtension extensions replace the old DnD features
  • OSGi bundle manifests of Vaadin Framework JARs no longer export /VAADIN, and there are new mechanisms for publishing static resources for OSGi
  • Tooltip styles for ContentMode.PREFORMATTED have been changed in all built-in themes to use the application font and allow long lines to wrap to multiple lines.
  • diff --git a/documentation/components/components-tree.asciidoc b/documentation/components/components-tree.asciidoc index dd957a0308..f81342c5ba 100644 --- a/documentation/components/components-tree.asciidoc +++ b/documentation/components/components-tree.asciidoc @@ -31,32 +31,32 @@ image::img/tree-basic.png[width=70%, scaledwidth=100%] [[components.tree.data]] == Binding to Data -[classname]#Tree# is used by binding it to a hierarchical data provider. The data provider can be based on in-memory or back end data. For in-memory data, the [classname]#InMemoryHierarchicalDataProvider# can be used, and for loading data from a back end, you need to implement three methods from the [interfacename]#HierarchicalDataProvider# interface. Usage of both data providers is described in +[classname]#Tree# is used by binding it to a hierarchical data provider. The data provider can be based on in-memory or back end data. For in-memory data, the [classname]#TreeDataProvider# can be used, and for loading data from a back end, you need to implement three methods from the [interfacename]#HierarchicalDataProvider# interface. Usage of both data providers is described in <>. -The [classname]#HierarchyData# class can be used to build the hierarchical data structure, -and it can then be passed on to [classname]#InMemoryHierarchicalDataProvider#. It is simply a hierarchical +The [classname]#TreeData# class can be used to build the hierarchical data structure, +and it can then be passed on to [classname]#TreeDataProvider#. It is simply a hierarchical collection, that the data provider uses to populate the [classname]#Tree#. The [methodname]#setItems# method in [classname]#Tree# can be used to set the root level items. Internally -an [classname]#InMemoryHierarchicalDataProvider# with [classname]#HierarchyData# is used. +an [classname]#TreeDataProvider# with [classname]#TreeData# is used. [source, java] ---- // An initial planet tree Tree tree = new Tree<>(); -HierarchyData hierarchyData = new HierarchyData<>(); +TreeData treeData = new TreeData<>(); // Couple of childless root items -hierarchyData.addItem(null,"Mercury"); -hierarchyData.addItem(null,"Venus"); +treeData.addItem(null,"Mercury"); +treeData.addItem(null,"Venus"); // Items with hierarchy -hierarchyData.addItem(null,"Earth"); -hierarchyData.addItem("Earth","The Moon"); +treeData.addItem(null,"Earth"); +treeData.addItem("Earth","The Moon"); -inMemoryDataProvider = new InMemoryHierarchicalDataProvider<>(hierarchyData); +inMemoryDataProvider = new TreeDataProvider<>(treeData); tree.setDataProvider(inMemoryDataProvider); tree.expand("Earth"); // Expand programmatically ---- @@ -67,9 +67,9 @@ the in-memory data in the tree, you may do it as follows: [source, java] ---- // Add Mars with satellites -hierarchyData.addItem(null, "Mars"); -hierarchyData.addItem("Mars", "Phobos"); -hierarchyData.addItem("Mars", "Deimos"); +treeData.addItem(null, "Mars"); +treeData.addItem("Mars", "Phobos"); +treeData.addItem("Mars", "Deimos"); inMemoryDataProvider.refreshAll(); ---- @@ -208,4 +208,4 @@ You could thereby define the item styling as follows: font-style: italic; } ---- -//// \ No newline at end of file +//// diff --git a/documentation/components/components-treegrid.asciidoc b/documentation/components/components-treegrid.asciidoc index d5659cb556..7f444602d9 100644 --- a/documentation/components/components-treegrid.asciidoc +++ b/documentation/components/components-treegrid.asciidoc @@ -30,7 +30,7 @@ image::img/tree-grid-basic.png[width=70%, scaledwidth=100%] [[components.treegrid.data]] == Binding to Data -[classname]#TreeGrid# is used by binding it to a hierarchical data provider. The data provider can be based on in-memory or back end data. For in-memory data, the [classname]#InMemoryHierarchicalDataProvider# can be used, and for loading data from a back end, you need to implement three methods from the [interfacename]#HierarchicalDataProvider# interface. Usage of both data providers is described in +[classname]#TreeGrid# is used by binding it to a hierarchical data provider. The data provider can be based on in-memory or back end data. For in-memory data, the [classname]#TreeDataProvider# can be used, and for loading data from a back end, you need to implement three methods from the [interfacename]#HierarchicalDataProvider# interface. Usage of both data providers is described in <>. Populating a [classname]#TreeGrid# with in-memory data can be done as follows @@ -39,7 +39,7 @@ Populating a [classname]#TreeGrid# with in-memory data can be done as follows ---- Project rootProject = getRootRroject(); -HierarchyData data = new HierarchyData<>(); +TreeData data = new TreeData<>(); // add a root level item with null parent data.addItem(null, rootProject); @@ -48,7 +48,7 @@ rootProject.flattened().forEach( project -> data.addItems(project, project.getSubProjects())); TreeGrid treeGrid = new TreeGrid<>(); -treeGrid.setDataProvider(new InMemoryHierarchicalDataProvider<>(data)); +treeGrid.setDataProvider(new TreeDataProvider<>(data)); // the first column gets the hierarchy indicator by default treeGrid.addColumn(Project::getName).setCaption("Project Name"); @@ -56,18 +56,18 @@ treeGrid.addColumn(Project::getHoursDone).setCaption("Hours Done"); treeGrid.addColumn(Project::getLastModified).setCaption("Last Modified"); ---- -The [classname]#HierarchyData# class can be used to build the hierarchical data structure, -and it can then be passed on to [classname]#InMemoryHierarchicalDataProvider#. It is simply a hierarchical +The [classname]#TreeData# class can be used to build the hierarchical data structure, +and it can then be passed on to [classname]#TreeDataProvider#. It is simply a hierarchical collection, that the data provider uses to populate the [classname]#TreeGrid#. The [methodname]#setItems# method in [classname]#TreeGrid# can be used to set the root level items. Internally -an [classname]#InMemoryHierarchicalDataProvider# with [classname]#HierarchyData# is used. If at any time you want to modify the in-memory data in the grid, you may do it as follows +an [classname]#TreeDataProvider# with [classname]#TreeData# is used. If at any time you want to modify the in-memory data in the grid, you may do it as follows [source, java] ---- -InMemoryHierarchicalDataProvider dataProvider = (InMemoryHierarchicalDataProvider) treeGrid.getDataProvider(); +TreeDataProvider dataProvider = (TreeDataProvider) treeGrid.getDataProvider(); -HierarchyData data = dataProvider.getData(); +TreeData data = dataProvider.getData(); // add new items data.addItem(null, newProject); data.addItems(newProject, newProject.getChildren()); diff --git a/documentation/datamodel/datamodel-hierarchical.asciidoc b/documentation/datamodel/datamodel-hierarchical.asciidoc index 735317c519..f405bc775f 100644 --- a/documentation/datamodel/datamodel-hierarchical.asciidoc +++ b/documentation/datamodel/datamodel-hierarchical.asciidoc @@ -11,21 +11,21 @@ IMPORTANT: The [interfacename]#HierarchicalDataProvider# is currently being deve The [classname]#Tree# and the [classname]#TreeGrid# components allow you to show data with hierarchical relationships between items. That data can be populated by on-demand from a back end by implementing the [interfacename]#HierarchicalDataProvider# interface. If you have the data available in-memory on the server, -you use the collection style API of [classname]#HierarchyData# and then pass it to a [classname]#InMemoryHierarchicalDataProvider#. This chapter introduces the hierarchical data providers and how they work. +you use the collection style API of [classname]#TreeData# and then pass it to a [classname]#TreeDataProvider#. This chapter introduces the hierarchical data providers and how they work. For using them with the components you should see <> and <> documentation. == In-memory Hierarchical Data -When the hierarchical data is available in the server side memory, you can use it to populate the [classname]#HierarchyData# that is the source of data for an [classname]#InMemoryHierarchicalDataProvider#. It contains collection style API to construct the hierarchical structure of your data, and also verifies that the hierarchy structure is valid. +When the hierarchical data is available in the server side memory, you can use it to populate the [classname]#TreeData# that is the source of data for an [classname]#TreeDataProvider#. It contains collection style API to construct the hierarchical structure of your data, and also verifies that the hierarchy structure is valid. -The following example populates a [classname]#HierarchyData# with two levels of data: +The following example populates a [classname]#TreeData# with two levels of data: [source, java] ---- Collection projects = service.getProjects(); -HierarchyData data = new HierarchyData<>(); +TreeData data = new TreeData<>(); // add root level items data.addItems(null, projects); @@ -33,16 +33,16 @@ data.addItems(null, projects); projects.forEach(project -> data.addItems(project, project.getChildren())); // construct the data provider for the hierarchical data we've built -InMemoryHierarchicalDataProvider dataProvider = new InMemoryHierarchicalDataProvider<>(data); +TreeDataProvider dataProvider = new TreeDataProvider<>(data); ---- === Updating data -When adding or removing items from the [classname]#HierarchyData#, you need to always notify the data provider that it should refresh its data. This can be done with the [methodname]#refreshAll# method in the data provider. +When adding or removing items from the [classname]#TreeData#, you need to always notify the data provider that it should refresh its data. This can be done with the [methodname]#refreshAll# method in the data provider. [source, java] ---- -HierarchyData data = dataProvider.getHierarchyData(); +TreeData data = dataProvider.getData(); data.addItem(null, newProject); data.addItems(newProject, newProject.getChildren()); @@ -55,14 +55,14 @@ dataProvider.refreshAll(); === Sorting and Filtering -For [classname]#InMemoryHierarchicalDataProvider#, both the sorting and filtering API is the same as in [classname]#ListDataProvider#. Sorting and filtering are applied separately for each hierarchy level, meaning e.g. that for a node that has not passed the filter there are no children shown. +For [classname]#TreeDataProvider#, both the sorting and filtering API is the same as in [classname]#ListDataProvider#. Sorting and filtering are applied separately for each hierarchy level, meaning e.g. that for a node that has not passed the filter there are no children shown. [source, java] ---- // setting sorting or filtering automatically refreshes the data dataProvider.setSortComparator((projectA, projectB) -> projectA.getHours().compareTo(projectB.getHours())); - + dataProvider.setFilter(project -> project.getHours() > 100); ---- @@ -72,7 +72,7 @@ The lazy loading hierarchical data, same concepts apply as with the non-hierarch To load hierarchical data on-demand from your back end, you should extend the [classname]#AbstractHierarchicalDataProvider# class. Then you just have to implement the following three methods: -* `boolean hasChildren(T item)` +* `boolean hasChildren(T item)` ** This tells the data provider whether the given item has children and should be expandable. Note that this method is called frequently and should not do any costly operations. * `int getChildCount(HierarchicalQuery query)` @@ -87,7 +87,7 @@ To load hierarchical data on-demand from your back end, you should extend the [c ** The parent node is available in the [classname]#HierarchicalQuery# via the [methodname]#getParent# method, which returns `null` for the root level. ** This method is called whenever the data should be displayed in the UI -Note that the [classname]#HierarchicalQuery# query object contains the relevant information regarding the sorting and filtering. +Note that the [classname]#HierarchicalQuery# query object contains the relevant information regarding the sorting and filtering. The following code snippet shows a simple example on how to building a lazy hierarchical data provider based on file system structure: 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); } diff --git a/server/src/test/java/com/vaadin/data/provider/InMemoryHierarchicalDataProviderTest.java b/server/src/test/java/com/vaadin/data/provider/InMemoryHierarchicalDataProviderTest.java deleted file mode 100644 index 99b7a04b19..0000000000 --- a/server/src/test/java/com/vaadin/data/provider/InMemoryHierarchicalDataProviderTest.java +++ /dev/null @@ -1,278 +0,0 @@ -package com.vaadin.data.provider; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.data.HierarchyData; -import com.vaadin.server.SerializablePredicate; - -public class InMemoryHierarchicalDataProviderTest extends - DataProviderTestBase> { - - private HierarchyData data; - private List flattenedData; - private List rootData; - - @Override - public void setUp() { - List randomBeans = StrBean.generateRandomBeans(20); - flattenedData = new ArrayList<>(); - rootData = new ArrayList<>(); - - data = new HierarchyData<>(); - data.addItems(null, randomBeans.subList(0, 5)); - data.addItems(randomBeans.get(0), randomBeans.subList(5, 10)); - data.addItems(randomBeans.get(5), randomBeans.subList(10, 15)); - data.addItems(null, randomBeans.subList(15, 20)); - - flattenedData.add(randomBeans.get(0)); - flattenedData.add(randomBeans.get(5)); - flattenedData.addAll(randomBeans.subList(10, 15)); - flattenedData.addAll(randomBeans.subList(6, 10)); - flattenedData.addAll(randomBeans.subList(1, 5)); - flattenedData.addAll(randomBeans.subList(15, 20)); - - rootData.addAll(randomBeans.subList(0, 5)); - rootData.addAll(randomBeans.subList(15, 20)); - - super.setUp(); - } - - @Test(expected = IllegalArgumentException.class) - public void hierarchyData_add_item_parent_not_in_hierarchy_throws() { - new HierarchyData<>().addItem(new StrBean("", 0, 0), - new StrBean("", 0, 0)); - } - - @Test(expected = NullPointerException.class) - public void hierarchyData_add_null_item_throws() { - new HierarchyData<>().addItem(null, null); - } - - @Test(expected = IllegalArgumentException.class) - public void hierarchyData_add_item_already_in_hierarchy_throws() { - StrBean bean = new StrBean("", 0, 0); - new HierarchyData<>().addItem(null, bean).addItem(null, bean); - } - - @Test - public void hierarchyData_remove_root_item() { - data.removeItem(null); - Assert.assertTrue(data.getChildren(null).isEmpty()); - } - - @Test - public void hierarchyData_clear() { - data.clear(); - Assert.assertTrue(data.getChildren(null).isEmpty()); - } - - @Test - public void hierarchyData_re_add_removed_item() { - StrBean item = rootData.get(0); - data.removeItem(item).addItem(null, item); - Assert.assertTrue(data.getChildren(null).contains(item)); - } - - @Test - public void setFilter() { - getDataProvider().setFilter(item -> item.getValue().equals("Xyz") - || item.getValue().equals("Baz")); - - Assert.assertEquals(10, sizeWithUnfilteredQuery()); - - getDataProvider().setFilter(item -> !item.getValue().equals("Foo") - && !item.getValue().equals("Xyz")); - - Assert.assertEquals( - "Previous filter should be replaced when setting a new one", 6, - sizeWithUnfilteredQuery()); - - getDataProvider().setFilter(null); - - Assert.assertEquals("Setting filter to null should remove all filters", - 20, sizeWithUnfilteredQuery()); - } - - @Test - public void addFilter() { - getDataProvider().addFilter(item -> item.getId() <= 10); - getDataProvider().addFilter(item -> item.getId() >= 5); - Assert.assertEquals(5, sizeWithUnfilteredQuery()); - } - - @Override - public void filteringListDataProvider_convertFilter() { - DataProvider strFilterDataProvider = getDataProvider() - .withConvertedFilter( - text -> strBean -> strBean.getValue().contains(text)); - Assert.assertEquals("Only one item should match 'Xyz'", 1, - strFilterDataProvider - .size(new HierarchicalQuery<>("Xyz", null))); - Assert.assertEquals("No item should match 'Zyx'", 0, - strFilterDataProvider - .size(new HierarchicalQuery<>("Zyx", null))); - Assert.assertEquals("Unexpected number of matches for 'Foo'", 3, - strFilterDataProvider - .size(new HierarchicalQuery<>("Foo", null))); - Assert.assertEquals("No items should've been filtered out", - rootData.size(), strFilterDataProvider - .size(new HierarchicalQuery<>(null, null))); - } - - @Override - public void filteringListDataProvider_defaultFilterType() { - Assert.assertEquals("Only one item should match 'Xyz'", 1, - getDataProvider().size(new HierarchicalQuery<>( - strBean -> strBean.getValue().contains("Xyz"), null))); - Assert.assertEquals("No item should match 'Zyx'", 0, - dataProvider.size(new HierarchicalQuery<>( - strBean -> strBean.getValue().contains("Zyx"), null))); - Assert.assertEquals("Unexpected number of matches for 'Foo'", 3, - getDataProvider() - .size(new HierarchicalQuery<>(fooFilter, null))); - } - - @Override - public void testDefaultSortWithSpecifiedPostSort() { - Comparator comp = Comparator.comparing(StrBean::getValue) - .thenComparing(Comparator.comparing(StrBean::getId).reversed()); - setSortOrder(QuerySortOrder.asc("value").thenDesc("id").build(), comp); - - List list = getDataProvider() - .fetch(createQuery(QuerySortOrder.asc("randomNumber").build(), - Comparator.comparing(StrBean::getRandomNumber), null, - null)) - .collect(Collectors.toList()); - - Assert.assertEquals("Sorted data and original data sizes don't match", - getDataProvider().fetch(new HierarchicalQuery<>(null, null)) - .count(), - list.size()); - - for (int i = 1; i < list.size(); ++i) { - StrBean prev = list.get(i - 1); - StrBean cur = list.get(i); - // Test specific sort - Assert.assertTrue( - "Failure: " + prev.getRandomNumber() + " > " - + cur.getRandomNumber(), - prev.getRandomNumber() <= cur.getRandomNumber()); - - if (prev.getRandomNumber() == cur.getRandomNumber()) { - // Test default sort - Assert.assertTrue( - prev.getValue().compareTo(cur.getValue()) <= 0); - if (prev.getValue().equals(cur.getValue())) { - Assert.assertTrue(prev.getId() > cur.getId()); - } - } - } - } - - @Override - public void testDefaultSortWithFunction() { - setSortOrder(QuerySortOrder.asc("value").build(), - Comparator.comparing(StrBean::getValue)); - - List list = getDataProvider() - .fetch(new HierarchicalQuery<>(null, null)) - .collect(Collectors.toList()); - - Assert.assertEquals("Sorted data and original data sizes don't match", - rootData.size(), list.size()); - - for (int i = 1; i < list.size(); ++i) { - StrBean prev = list.get(i - 1); - StrBean cur = list.get(i); - - // Test default sort - Assert.assertTrue(prev.getValue().compareTo(cur.getValue()) <= 0); - } - } - - @Override - public void testListContainsAllData() { - assertHierarchyCorrect(); - } - - @Override - public void testSortByComparatorListsDiffer() { - Comparator comp = Comparator.comparing(StrBean::getValue) - .thenComparing(StrBean::getRandomNumber) - .thenComparing(StrBean::getId); - - List list = getDataProvider().fetch( - createQuery(QuerySortOrder.asc("value").thenAsc("randomNumber") - .thenAsc("id").build(), comp, null, null)) - .collect(Collectors.toList()); - - Assert.assertNotEquals("First value should not match", rootData.get(0), - list.get(0)); - - Assert.assertEquals("Sorted data and original data sizes don't match", - rootData.size(), list.size()); - - rootData.sort(comp); - for (int i = 0; i < rootData.size(); ++i) { - Assert.assertEquals("Sorting result differed", rootData.get(i), - list.get(i)); - } - } - - @Override - protected InMemoryHierarchicalDataProvider createDataProvider() { - return new InMemoryHierarchicalDataProvider<>(data); - } - - @Override - protected void setSortOrder(List sortOrder, - Comparator comp) { - getDataProvider().setSortComparator(comp::compare); - } - - @Override - protected long sizeWithUnfilteredQuery() { - return getFlattenedDataFromProvider(new ArrayList<>(), null).size(); - } - - private void assertHierarchyCorrect() { - Assert.assertEquals(flattenedData, - getFlattenedData(new ArrayList<>(), null)); - Assert.assertEquals(flattenedData, - getFlattenedDataFromProvider(new ArrayList<>(), null)); - } - - private List getFlattenedData(List flattened, - StrBean item) { - if (item != null) { - flattened.add(item); - } - data.getChildren(item) - .forEach(child -> getFlattenedData(flattened, child)); - return flattened; - } - - private List getFlattenedDataFromProvider(List flattened, - StrBean item) { - if (item != null) { - flattened.add(item); - } - getDataProvider().fetchChildren(new HierarchicalQuery<>(null, item)) - .forEach(child -> getFlattenedDataFromProvider(flattened, - child)); - return flattened; - } - - private HierarchicalQuery> createQuery( - List sortOrder, Comparator comp, - SerializablePredicate filter, StrBean parent) { - return new HierarchicalQuery<>(0, Integer.MAX_VALUE, sortOrder, comp, - filter, parent); - } -} diff --git a/server/src/test/java/com/vaadin/data/provider/TreeDataProviderTest.java b/server/src/test/java/com/vaadin/data/provider/TreeDataProviderTest.java new file mode 100644 index 0000000000..3f9f1f6e94 --- /dev/null +++ b/server/src/test/java/com/vaadin/data/provider/TreeDataProviderTest.java @@ -0,0 +1,297 @@ +package com.vaadin.data.provider; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.data.TreeData; +import com.vaadin.server.SerializablePredicate; + +public class TreeDataProviderTest extends + DataProviderTestBase> { + + private TreeData data; + private List flattenedData; + private List rootData; + + @Override + public void setUp() { + List randomBeans = StrBean.generateRandomBeans(20); + flattenedData = new ArrayList<>(); + rootData = new ArrayList<>(); + + data = new TreeData<>(); + data.addItems(null, randomBeans.subList(0, 5)); + data.addItems(randomBeans.get(0), randomBeans.subList(5, 10)); + data.addItems(randomBeans.get(5), randomBeans.subList(10, 15)); + data.addItems(null, randomBeans.subList(15, 20)); + + flattenedData.add(randomBeans.get(0)); + flattenedData.add(randomBeans.get(5)); + flattenedData.addAll(randomBeans.subList(10, 15)); + flattenedData.addAll(randomBeans.subList(6, 10)); + flattenedData.addAll(randomBeans.subList(1, 5)); + flattenedData.addAll(randomBeans.subList(15, 20)); + + rootData.addAll(randomBeans.subList(0, 5)); + rootData.addAll(randomBeans.subList(15, 20)); + + super.setUp(); + } + + @Test(expected = IllegalArgumentException.class) + public void treeData_add_item_parent_not_in_hierarchy_throws() { + new TreeData<>().addItem(new StrBean("", 0, 0), + new StrBean("", 0, 0)); + } + + @Test(expected = NullPointerException.class) + public void treeData_add_null_item_throws() { + new TreeData<>().addItem(null, null); + } + + @Test(expected = IllegalArgumentException.class) + public void treeData_add_item_already_in_hierarchy_throws() { + StrBean bean = new StrBean("", 0, 0); + new TreeData<>().addItem(null, bean).addItem(null, bean); + } + + @Test + public void treeData_remove_root_item() { + data.removeItem(null); + Assert.assertTrue(data.getChildren(null).isEmpty()); + } + + @Test + public void treeData_clear() { + data.clear(); + Assert.assertTrue(data.getChildren(null).isEmpty()); + } + + @Test + public void treeData_re_add_removed_item() { + StrBean item = rootData.get(0); + data.removeItem(item).addItem(null, item); + Assert.assertTrue(data.getChildren(null).contains(item)); + } + + @Test + public void populate_treeData_with_child_item_provider() { + TreeData stringData = new TreeData<>(); + List rootItems = Arrays.asList("a", "b", "c"); + stringData.addItems(rootItems, item -> { + if (item.length() >= 3 || item.startsWith("c")) { + return Arrays.asList(); + } + return Arrays.asList(item + "/a", item + "/b", item + "/c"); + }); + Assert.assertEquals(stringData.getChildren("a"), + Arrays.asList("a/a", "a/b", "a/c")); + Assert.assertEquals(stringData.getChildren("b"), + Arrays.asList("b/a", "b/b", "b/c")); + Assert.assertEquals(stringData.getChildren("c"), Arrays.asList()); + Assert.assertEquals(stringData.getChildren("a/b"), Arrays.asList()); + } + + @Test + public void setFilter() { + getDataProvider().setFilter(item -> item.getValue().equals("Xyz") + || item.getValue().equals("Baz")); + + Assert.assertEquals(10, sizeWithUnfilteredQuery()); + + getDataProvider().setFilter(item -> !item.getValue().equals("Foo") + && !item.getValue().equals("Xyz")); + + Assert.assertEquals( + "Previous filter should be replaced when setting a new one", 6, + sizeWithUnfilteredQuery()); + + getDataProvider().setFilter(null); + + Assert.assertEquals("Setting filter to null should remove all filters", + 20, sizeWithUnfilteredQuery()); + } + + @Test + public void addFilter() { + getDataProvider().addFilter(item -> item.getId() <= 10); + getDataProvider().addFilter(item -> item.getId() >= 5); + Assert.assertEquals(5, sizeWithUnfilteredQuery()); + } + + @Override + public void filteringListDataProvider_convertFilter() { + DataProvider strFilterDataProvider = getDataProvider() + .withConvertedFilter( + text -> strBean -> strBean.getValue().contains(text)); + Assert.assertEquals("Only one item should match 'Xyz'", 1, + strFilterDataProvider + .size(new HierarchicalQuery<>("Xyz", null))); + Assert.assertEquals("No item should match 'Zyx'", 0, + strFilterDataProvider + .size(new HierarchicalQuery<>("Zyx", null))); + Assert.assertEquals("Unexpected number of matches for 'Foo'", 3, + strFilterDataProvider + .size(new HierarchicalQuery<>("Foo", null))); + Assert.assertEquals("No items should've been filtered out", + rootData.size(), strFilterDataProvider + .size(new HierarchicalQuery<>(null, null))); + } + + @Override + public void filteringListDataProvider_defaultFilterType() { + Assert.assertEquals("Only one item should match 'Xyz'", 1, + getDataProvider().size(new HierarchicalQuery<>( + strBean -> strBean.getValue().contains("Xyz"), null))); + Assert.assertEquals("No item should match 'Zyx'", 0, + dataProvider.size(new HierarchicalQuery<>( + strBean -> strBean.getValue().contains("Zyx"), null))); + Assert.assertEquals("Unexpected number of matches for 'Foo'", 3, + getDataProvider() + .size(new HierarchicalQuery<>(fooFilter, null))); + } + + @Override + public void testDefaultSortWithSpecifiedPostSort() { + Comparator comp = Comparator.comparing(StrBean::getValue) + .thenComparing(Comparator.comparing(StrBean::getId).reversed()); + setSortOrder(QuerySortOrder.asc("value").thenDesc("id").build(), comp); + + List list = getDataProvider() + .fetch(createQuery(QuerySortOrder.asc("randomNumber").build(), + Comparator.comparing(StrBean::getRandomNumber), null, + null)) + .collect(Collectors.toList()); + + Assert.assertEquals("Sorted data and original data sizes don't match", + getDataProvider().fetch(new HierarchicalQuery<>(null, null)) + .count(), + list.size()); + + for (int i = 1; i < list.size(); ++i) { + StrBean prev = list.get(i - 1); + StrBean cur = list.get(i); + // Test specific sort + Assert.assertTrue( + "Failure: " + prev.getRandomNumber() + " > " + + cur.getRandomNumber(), + prev.getRandomNumber() <= cur.getRandomNumber()); + + if (prev.getRandomNumber() == cur.getRandomNumber()) { + // Test default sort + Assert.assertTrue( + prev.getValue().compareTo(cur.getValue()) <= 0); + if (prev.getValue().equals(cur.getValue())) { + Assert.assertTrue(prev.getId() > cur.getId()); + } + } + } + } + + @Override + public void testDefaultSortWithFunction() { + setSortOrder(QuerySortOrder.asc("value").build(), + Comparator.comparing(StrBean::getValue)); + + List list = getDataProvider() + .fetch(new HierarchicalQuery<>(null, null)) + .collect(Collectors.toList()); + + Assert.assertEquals("Sorted data and original data sizes don't match", + rootData.size(), list.size()); + + for (int i = 1; i < list.size(); ++i) { + StrBean prev = list.get(i - 1); + StrBean cur = list.get(i); + + // Test default sort + Assert.assertTrue(prev.getValue().compareTo(cur.getValue()) <= 0); + } + } + + @Override + public void testListContainsAllData() { + assertHierarchyCorrect(); + } + + @Override + public void testSortByComparatorListsDiffer() { + Comparator comp = Comparator.comparing(StrBean::getValue) + .thenComparing(StrBean::getRandomNumber) + .thenComparing(StrBean::getId); + + List list = getDataProvider().fetch( + createQuery(QuerySortOrder.asc("value").thenAsc("randomNumber") + .thenAsc("id").build(), comp, null, null)) + .collect(Collectors.toList()); + + Assert.assertNotEquals("First value should not match", rootData.get(0), + list.get(0)); + + Assert.assertEquals("Sorted data and original data sizes don't match", + rootData.size(), list.size()); + + rootData.sort(comp); + for (int i = 0; i < rootData.size(); ++i) { + Assert.assertEquals("Sorting result differed", rootData.get(i), + list.get(i)); + } + } + + @Override + protected TreeDataProvider createDataProvider() { + return new TreeDataProvider<>(data); + } + + @Override + protected void setSortOrder(List sortOrder, + Comparator comp) { + getDataProvider().setSortComparator(comp::compare); + } + + @Override + protected long sizeWithUnfilteredQuery() { + return getFlattenedDataFromProvider(new ArrayList<>(), null).size(); + } + + private void assertHierarchyCorrect() { + Assert.assertEquals(flattenedData, + getFlattenedData(new ArrayList<>(), null)); + Assert.assertEquals(flattenedData, + getFlattenedDataFromProvider(new ArrayList<>(), null)); + } + + private List getFlattenedData(List flattened, + StrBean item) { + if (item != null) { + flattened.add(item); + } + data.getChildren(item) + .forEach(child -> getFlattenedData(flattened, child)); + return flattened; + } + + private List getFlattenedDataFromProvider(List flattened, + StrBean item) { + if (item != null) { + flattened.add(item); + } + getDataProvider().fetchChildren(new HierarchicalQuery<>(null, item)) + .forEach(child -> getFlattenedDataFromProvider(flattened, + child)); + return flattened; + } + + private HierarchicalQuery> createQuery( + List sortOrder, Comparator comp, + SerializablePredicate filter, StrBean parent) { + return new HierarchicalQuery<>(0, Integer.MAX_VALUE, sortOrder, comp, + filter, parent); + } +} diff --git a/server/src/test/java/com/vaadin/tests/components/TreeTest.java b/server/src/test/java/com/vaadin/tests/components/TreeTest.java index 13ef0acc49..9224934a4c 100644 --- a/server/src/test/java/com/vaadin/tests/components/TreeTest.java +++ b/server/src/test/java/com/vaadin/tests/components/TreeTest.java @@ -3,8 +3,8 @@ package com.vaadin.tests.components; import org.junit.Assert; import org.junit.Test; -import com.vaadin.data.HierarchyData; -import com.vaadin.data.provider.InMemoryHierarchicalDataProvider; +import com.vaadin.data.TreeData; +import com.vaadin.data.provider.TreeDataProvider; import com.vaadin.event.CollapseEvent; import com.vaadin.event.CollapseEvent.CollapseListener; import com.vaadin.event.ExpandEvent; @@ -52,12 +52,12 @@ public class TreeTest { @Test public void event_source_is_tree() { Tree tree = new Tree<>(); - HierarchyData hierarchyData = new HierarchyData<>(); - hierarchyData.addItem(null, "Foo"); - hierarchyData.addItem("Foo", "Bar"); - hierarchyData.addItem("Foo", "Baz"); + TreeData treeData = new TreeData<>(); + treeData.addItem(null, "Foo"); + treeData.addItem("Foo", "Bar"); + treeData.addItem("Foo", "Baz"); tree.setDataProvider( - new InMemoryHierarchicalDataProvider<>(hierarchyData)); + new TreeDataProvider<>(treeData)); TreeCollapseExpandListener listener = new TreeCollapseExpandListener( tree); diff --git a/server/src/test/java/com/vaadin/tests/components/treegrid/TreeGridTest.java b/server/src/test/java/com/vaadin/tests/components/treegrid/TreeGridTest.java index 2e335f865e..1544a4f69d 100644 --- a/server/src/test/java/com/vaadin/tests/components/treegrid/TreeGridTest.java +++ b/server/src/test/java/com/vaadin/tests/components/treegrid/TreeGridTest.java @@ -3,8 +3,8 @@ package com.vaadin.tests.components.treegrid; import org.junit.Assert; import org.junit.Test; -import com.vaadin.data.HierarchyData; -import com.vaadin.data.provider.InMemoryHierarchicalDataProvider; +import com.vaadin.data.TreeData; +import com.vaadin.data.provider.TreeDataProvider; import com.vaadin.ui.TreeGrid; import com.vaadin.ui.renderers.TextRenderer; @@ -24,12 +24,12 @@ public class TreeGridTest { @Test public void testExpandAndCollapseEvents() { - HierarchyData hierarchyData = new HierarchyData<>(); - hierarchyData.addItem(null, "Foo"); - hierarchyData.addItem("Foo", "Bar"); - hierarchyData.addItem("Foo", "Baz"); + TreeData treeData = new TreeData<>(); + treeData.addItem(null, "Foo"); + treeData.addItem("Foo", "Bar"); + treeData.addItem("Foo", "Baz"); treeGrid.setDataProvider( - new InMemoryHierarchicalDataProvider<>(hierarchyData)); + new TreeDataProvider<>(treeData)); treeGrid.addExpandListener(e -> expandEventFired = true); treeGrid.addCollapseListener(e -> collapseEventFired = true); diff --git a/server/src/test/java/com/vaadin/tests/server/ClassesSerializableTest.java b/server/src/test/java/com/vaadin/tests/server/ClassesSerializableTest.java index b4f9851867..d2d25b4777 100644 --- a/server/src/test/java/com/vaadin/tests/server/ClassesSerializableTest.java +++ b/server/src/test/java/com/vaadin/tests/server/ClassesSerializableTest.java @@ -76,6 +76,7 @@ public class ClassesSerializableTest { "com\\.vaadin\\.buildhelpers.*", // "com\\.vaadin\\.util\\.EncodeUtil.*", // "com\\.vaadin\\.util\\.ReflectTools.*", // + "com\\.vaadin\\.data\\.provider\\.InMemoryDataProviderHelpers", "com\\.vaadin\\.data\\.provider\\.HierarchyMapper\\$TreeLevelQuery", "com\\.vaadin\\.data\\.util\\.ReflectTools.*", // "com\\.vaadin\\.data\\.util\\.JsonUtil.*", // diff --git a/server/src/test/java/com/vaadin/tests/server/component/treegrid/TreeGridDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/treegrid/TreeGridDeclarativeTest.java index c8a51a1928..a13ba357ef 100644 --- a/server/src/test/java/com/vaadin/tests/server/component/treegrid/TreeGridDeclarativeTest.java +++ b/server/src/test/java/com/vaadin/tests/server/component/treegrid/TreeGridDeclarativeTest.java @@ -4,9 +4,9 @@ import java.lang.reflect.InvocationTargetException; import org.junit.Assert; -import com.vaadin.data.HierarchyData; +import com.vaadin.data.TreeData; import com.vaadin.data.provider.HierarchicalQuery; -import com.vaadin.data.provider.InMemoryHierarchicalDataProvider; +import com.vaadin.data.provider.TreeDataProvider; import com.vaadin.tests.data.bean.Person; import com.vaadin.tests.server.component.abstractlisting.AbstractListingDeclarativeTest; import com.vaadin.ui.TreeGrid; @@ -28,7 +28,7 @@ public class TreeGridDeclarativeTest Person person6 = createPerson("ca", "last-name"); Person person7 = createPerson("caa", "last-name"); - HierarchyData data = new HierarchyData<>(); + TreeData data = new TreeData<>(); data.addItems(null, person1, person4, person5); data.addItems(person1, person2, person3); data.addItem(person5, person6); @@ -38,7 +38,7 @@ public class TreeGridDeclarativeTest grid.addColumn(Person::getLastName).setId("id").setCaption("Id"); grid.setHierarchyColumn("id"); - grid.setDataProvider(new InMemoryHierarchicalDataProvider<>(data)); + grid.setDataProvider(new TreeDataProvider<>(data)); String design = String.format( "<%s hierarchy-column='id'>" diff --git a/uitest/src/main/java/com/vaadin/tests/components/tree/TreeBasicFeatures.java b/uitest/src/main/java/com/vaadin/tests/components/tree/TreeBasicFeatures.java index e0fa469fa8..a579c98a12 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/tree/TreeBasicFeatures.java +++ b/uitest/src/main/java/com/vaadin/tests/components/tree/TreeBasicFeatures.java @@ -5,8 +5,8 @@ import java.util.List; import com.vaadin.annotations.Theme; import com.vaadin.annotations.Widgetset; -import com.vaadin.data.HierarchyData; -import com.vaadin.data.provider.InMemoryHierarchicalDataProvider; +import com.vaadin.data.TreeData; +import com.vaadin.data.provider.TreeDataProvider; import com.vaadin.icons.VaadinIcons; import com.vaadin.server.ClassResource; import com.vaadin.server.ThemeResource; @@ -27,7 +27,7 @@ import com.vaadin.ui.VerticalLayout; public class TreeBasicFeatures extends AbstractTestUIWithLog { private Tree tree; - private InMemoryHierarchicalDataProvider inMemoryDataProvider; + private TreeDataProvider inMemoryDataProvider; private IconGenerator iconGenerator = i -> { switch (i.getDepth()) { case 0: @@ -126,7 +126,7 @@ public class TreeBasicFeatures extends AbstractTestUIWithLog { } private void setupDataProvider() { - HierarchyData data = new HierarchyData<>(); + TreeData data = new TreeData<>(); List ints = Arrays.asList(0, 1, 2); @@ -145,7 +145,7 @@ public class TreeBasicFeatures extends AbstractTestUIWithLog { }); }); - inMemoryDataProvider = new InMemoryHierarchicalDataProvider<>(data); + inMemoryDataProvider = new TreeDataProvider<>(data); } } diff --git a/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridBasicFeatures.java b/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridBasicFeatures.java index 8151aa8c88..13064c4c39 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridBasicFeatures.java +++ b/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridBasicFeatures.java @@ -8,11 +8,11 @@ import java.util.stream.Stream; import com.vaadin.annotations.Theme; import com.vaadin.annotations.Widgetset; -import com.vaadin.data.HierarchyData; +import com.vaadin.data.TreeData; import com.vaadin.data.provider.DataProvider; 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.server.SerializablePredicate; import com.vaadin.shared.Range; import com.vaadin.tests.components.AbstractComponentTest; @@ -24,7 +24,7 @@ import com.vaadin.ui.TreeGrid; public class TreeGridBasicFeatures extends AbstractComponentTest { private TreeGrid grid; - private InMemoryHierarchicalDataProvider inMemoryDataProvider; + private TreeDataProvider inMemoryDataProvider; private LazyHierarchicalDataProvider lazyDataProvider; private HierarchicalDataProvider loggingDataProvider; @@ -70,7 +70,7 @@ public class TreeGridBasicFeatures extends AbstractComponentTest { } private void initializeDataProviders() { - HierarchyData data = new HierarchyData<>(); + TreeData data = new TreeData<>(); List ints = Arrays.asList(0, 1, 2); @@ -89,9 +89,9 @@ public class TreeGridBasicFeatures extends AbstractComponentTest { }); }); - inMemoryDataProvider = new InMemoryHierarchicalDataProvider<>(data); + inMemoryDataProvider = new TreeDataProvider<>(data); lazyDataProvider = new LazyHierarchicalDataProvider(3, 2); - loggingDataProvider = new InMemoryHierarchicalDataProvider( + loggingDataProvider = new TreeDataProvider( data) { @Override @@ -118,7 +118,7 @@ public class TreeGridBasicFeatures extends AbstractComponentTest { @SuppressWarnings("rawtypes") LinkedHashMap options = new LinkedHashMap<>(); options.put("LazyHierarchicalDataProvider", lazyDataProvider); - options.put("InMemoryHierarchicalDataProvider", inMemoryDataProvider); + options.put("TreeDataProvider", inMemoryDataProvider); options.put("LoggingDataProvider", loggingDataProvider); createSelectAction("Set data provider", CATEGORY_FEATURES, options, diff --git a/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridChangingHierarchy.java b/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridChangingHierarchy.java index c7943524e4..d630e36292 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridChangingHierarchy.java +++ b/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridChangingHierarchy.java @@ -2,10 +2,10 @@ package com.vaadin.tests.components.treegrid; import java.util.stream.Stream; -import com.vaadin.data.HierarchyData; +import com.vaadin.data.TreeData; import com.vaadin.data.ValueProvider; import com.vaadin.data.provider.HierarchicalQuery; -import com.vaadin.data.provider.InMemoryHierarchicalDataProvider; +import com.vaadin.data.provider.TreeDataProvider; import com.vaadin.server.SerializablePredicate; import com.vaadin.server.VaadinRequest; import com.vaadin.tests.components.AbstractTestUI; @@ -15,18 +15,18 @@ import com.vaadin.ui.TreeGrid; public class TreeGridChangingHierarchy extends AbstractTestUI { private static class TestDataProvider - extends InMemoryHierarchicalDataProvider { + extends TreeDataProvider { - private HierarchyData hierarchyData; + private TreeData treeData; - public TestDataProvider(HierarchyData hierarchyData) { - super(hierarchyData); - this.hierarchyData = hierarchyData; + public TestDataProvider(TreeData treeData) { + super(treeData); + this.treeData = treeData; } @Override public boolean hasChildren(String item) { - if (!hierarchyData.contains(item)) { + if (!treeData.contains(item)) { return false; } return super.hasChildren(item); @@ -35,7 +35,7 @@ public class TreeGridChangingHierarchy extends AbstractTestUI { @Override public Stream fetchChildren( HierarchicalQuery> query) { - if (!hierarchyData.contains(query.getParent())) { + if (!treeData.contains(query.getParent())) { return Stream.empty(); } return super.fetchChildren(query); @@ -44,7 +44,7 @@ public class TreeGridChangingHierarchy extends AbstractTestUI { @Override protected void setup(VaadinRequest request) { - HierarchyData data = new HierarchyData<>(); + TreeData data = new TreeData<>(); data.addItems(null, "a", "b", "c").addItem("b", "b/a"); TreeGrid grid = new TreeGrid<>(); diff --git a/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridHugeTree.java b/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridHugeTree.java index 7cfb926849..f56052400e 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridHugeTree.java +++ b/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridHugeTree.java @@ -2,8 +2,8 @@ package com.vaadin.tests.components.treegrid; import com.vaadin.annotations.Theme; import com.vaadin.annotations.Widgetset; -import com.vaadin.data.HierarchyData; -import com.vaadin.data.provider.InMemoryHierarchicalDataProvider; +import com.vaadin.data.TreeData; +import com.vaadin.data.provider.TreeDataProvider; import com.vaadin.server.VaadinRequest; import com.vaadin.tests.components.AbstractTestUI; import com.vaadin.ui.Button; @@ -15,10 +15,10 @@ public class TreeGridHugeTree extends AbstractTestUI { private TreeGrid treeGrid; - private InMemoryHierarchicalDataProvider inMemoryDataProvider; + private TreeDataProvider inMemoryDataProvider; private void initializeDataProvider() { - HierarchyData data = new HierarchyData<>(); + TreeData data = new TreeData<>(); for (int i = 0; i < 3; i++) { String granddad = "Granddad " + i; data.addItem(null, granddad); @@ -31,7 +31,7 @@ public class TreeGridHugeTree } } } - inMemoryDataProvider = new InMemoryHierarchicalDataProvider<>(data); + inMemoryDataProvider = new TreeDataProvider<>(data); } @Override diff --git a/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridHugeTreeNavigation.java b/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridHugeTreeNavigation.java index fc550a9570..02bfd65a28 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridHugeTreeNavigation.java +++ b/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridHugeTreeNavigation.java @@ -2,8 +2,8 @@ package com.vaadin.tests.components.treegrid; import com.vaadin.annotations.Theme; import com.vaadin.annotations.Widgetset; -import com.vaadin.data.HierarchyData; -import com.vaadin.data.provider.InMemoryHierarchicalDataProvider; +import com.vaadin.data.TreeData; +import com.vaadin.data.provider.TreeDataProvider; import com.vaadin.tests.components.AbstractComponentTest; import com.vaadin.ui.TreeGrid; @@ -12,7 +12,7 @@ import com.vaadin.ui.TreeGrid; public class TreeGridHugeTreeNavigation extends AbstractComponentTest { private TreeGrid treeGrid; - private InMemoryHierarchicalDataProvider inMemoryDataProvider; + private TreeDataProvider inMemoryDataProvider; @Override public TreeGrid getComponent() { @@ -40,7 +40,7 @@ public class TreeGridHugeTreeNavigation extends AbstractComponentTest } private void initializeDataProvider() { - HierarchyData data = new HierarchyData<>(); + TreeData data = new TreeData<>(); for (int i = 0; i < 3; i++) { String granddad = "Granddad " + i; data.addItem(null, granddad); @@ -53,7 +53,7 @@ public class TreeGridHugeTreeNavigation extends AbstractComponentTest } } } - inMemoryDataProvider = new InMemoryHierarchicalDataProvider<>(data); + inMemoryDataProvider = new TreeDataProvider<>(data); } } diff --git a/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridInitialExpand.java b/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridInitialExpand.java index db5b8c54b6..4c4b95c3d1 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridInitialExpand.java +++ b/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridInitialExpand.java @@ -1,7 +1,7 @@ package com.vaadin.tests.components.treegrid; -import com.vaadin.data.HierarchyData; -import com.vaadin.data.provider.InMemoryHierarchicalDataProvider; +import com.vaadin.data.TreeData; +import com.vaadin.data.provider.TreeDataProvider; import com.vaadin.server.VaadinRequest; import com.vaadin.tests.components.AbstractTestUI; import com.vaadin.ui.TreeGrid; @@ -13,13 +13,13 @@ public class TreeGridInitialExpand extends AbstractTestUI { TreeGrid treeGrid = new TreeGrid<>(); treeGrid.setCaption("Test"); treeGrid.addColumn(String::toString).setCaption("String"); - HierarchyData data = new HierarchyData<>(); + TreeData data = new TreeData<>(); data.addItem(null, "parent1"); data.addItem("parent1", "parent1-child1"); data.addItem("parent1", "parent1-child2"); data.addItem(null, "parent2"); data.addItem("parent2", "parent2-child2"); - treeGrid.setDataProvider(new InMemoryHierarchicalDataProvider<>(data)); + treeGrid.setDataProvider(new TreeDataProvider<>(data)); treeGrid.setHeightByRows(5); treeGrid.expand("parent1"); treeGrid.expand("parent2"); diff --git a/uitest/src/main/java/com/vaadin/tests/performance/TreeGridMemory.java b/uitest/src/main/java/com/vaadin/tests/performance/TreeGridMemory.java index 531cd8e283..3f4047e457 100644 --- a/uitest/src/main/java/com/vaadin/tests/performance/TreeGridMemory.java +++ b/uitest/src/main/java/com/vaadin/tests/performance/TreeGridMemory.java @@ -7,8 +7,8 @@ import java.util.Optional; import javax.servlet.annotation.WebServlet; import com.vaadin.annotations.VaadinServletConfiguration; -import com.vaadin.data.HierarchyData; -import com.vaadin.data.provider.InMemoryHierarchicalDataProvider; +import com.vaadin.data.TreeData; +import com.vaadin.data.provider.TreeDataProvider; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinServlet; import com.vaadin.tests.data.bean.Address; @@ -54,22 +54,22 @@ public class TreeGridMemory extends AbstractBeansMemoryTest> { @Override protected void setInMemoryContainer(TreeGrid treeGrid, List data) { - HierarchyData hierarchyData = new HierarchyData<>(); + TreeData treeData = new TreeData<>(); treeGrid.setDataProvider( - new InMemoryHierarchicalDataProvider<>(hierarchyData)); + new TreeDataProvider<>(treeData)); List toExpand = new ArrayList<>(); if (data.size() != 0 && data.size() % 2 == 0) { // treat list as if it were a balanced binary tree - hierarchyData.addItem(null, data.get(0)); + treeData.addItem(null, data.get(0)); int n = 0; while (2 * n + 2 < data.size()) { - hierarchyData.addItems(data.get(n), + treeData.addItems(data.get(n), data.subList(2 * n + 1, 2 * n + 3)); toExpand.add(data.get(n)); n++; } } else { - hierarchyData.addItems(null, data); + treeData.addItems(null, data); } if (initiallyExpanded) { treeGrid.expand(toExpand); diff --git a/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridBasicFeaturesTest.java b/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridBasicFeaturesTest.java index 18b320abba..a766490017 100644 --- a/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridBasicFeaturesTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridBasicFeaturesTest.java @@ -32,7 +32,7 @@ public class TreeGridBasicFeaturesTest extends MultiBrowserTest { @Parameters public static Collection getDataProviders() { return Arrays.asList("LazyHierarchicalDataProvider", - "InMemoryHierarchicalDataProvider"); + "TreeDataProvider"); } @Before diff --git a/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridClientSortTest.java b/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridClientSortTest.java index 2000d1388b..4e56e8445e 100644 --- a/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridClientSortTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridClientSortTest.java @@ -19,7 +19,7 @@ public class TreeGridClientSortTest extends SingleBrowserTest { openTestURL(); TreeGridElement grid = $(TreeGridElement.class).first(); selectMenuPath("Component", "Features", "Set data provider", - "InMemoryHierarchicalDataProvider"); + "TreeDataProvider"); grid.getHeaderCell(0, 0).doubleClick(); grid.expandWithClick(0); grid.expandWithClick(1);