diff --git a/all/src/main/templates/release-notes.html b/all/src/main/templates/release-notes.html index 44b1690e7b8..abfda6bf2eb 100644 --- a/all/src/main/templates/release-notes.html +++ b/all/src/main/templates/release-notes.html @@ -102,7 +102,7 @@

Incompatible or Behavior-altering Changes in @version@

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 dd957a03088..f81342c5ba2 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 d5659cb556a..7f444602d9e 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 735317c519b..f405bc775fa 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 00000000000..e43b9d6b77f --- /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/TreeData.java similarity index 85% rename from server/src/main/java/com/vaadin/data/HierarchyData.java rename to server/src/main/java/com/vaadin/data/TreeData.java index 0cf680db2fa..def69745ef8 100644 --- a/server/src/main/java/com/vaadin/data/HierarchyData.java +++ b/server/src/main/java/com/vaadin/data/TreeData.java @@ -34,7 +34,7 @@ * @param * data type */ -public class HierarchyData implements Serializable { +public class TreeData implements Serializable { private static class HierarchyWrapper implements Serializable { private T item; @@ -86,7 +86,7 @@ public void removeChild(T child) { * Creates an initially empty hierarchical data representation to which * items can be added or removed. */ - public HierarchyData() { + public TreeData() { itemToWrapperMap = new LinkedHashMap<>(); itemToWrapperMap.put(null, new HierarchyWrapper<>(null, null)); } @@ -109,7 +109,7 @@ public HierarchyData() { * @throws NullPointerException * if item is null */ - public HierarchyData addItem(T parent, T item) { + public TreeData addItem(T parent, T item) { Objects.requireNonNull(item, "Item cannot be null"); if (parent != null && !contains(parent)) { throw new IllegalArgumentException( @@ -144,7 +144,7 @@ public HierarchyData addItem(T parent, T item) { * @throws NullPointerException * if any of the items are null */ - public HierarchyData addItems(T parent, + public TreeData addItems(T parent, @SuppressWarnings("unchecked") T... items) { Arrays.asList(items).stream().forEach(item -> addItem(parent, item)); return this; @@ -170,7 +170,7 @@ public HierarchyData addItems(T parent, * @throws NullPointerException * if any of the items are null */ - public HierarchyData addItems(T parent, Collection items) { + public TreeData addItems(T parent, Collection items) { items.stream().forEach(item -> addItem(parent, item)); return this; } @@ -195,11 +195,33 @@ public HierarchyData addItems(T parent, Collection items) { * @throws NullPointerException * if any of the items are null */ - public HierarchyData addItems(T parent, Stream items) { + 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. @@ -211,7 +233,7 @@ public HierarchyData addItems(T parent, Stream items) { * @throws IllegalArgumentException * if the item does not exist in this structure */ - public HierarchyData removeItem(T item) { + public TreeData removeItem(T item) { if (!contains(item)) { throw new IllegalArgumentException( "Item '" + item + "' not in the hierarchy"); @@ -232,7 +254,7 @@ public HierarchyData removeItem(T item) { * * @return this */ - public HierarchyData clear() { + public TreeData clear() { removeItem(null); return this; } @@ -275,4 +297,13 @@ private void putItem(T item, T parent) { } 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 5d3bd81a053..4a8ae62772b 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.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 00000000000..bfb62b8dc6d --- /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 00000000000..556404911c1 --- /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/ListDataProvider.java b/server/src/main/java/com/vaadin/data/provider/ListDataProvider.java index fba2f94d9d1..eef0b0388e8 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 @@ 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 @@ * @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 Stream fetch(Query> query) { 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 @@ private Stream getFilteredStream( 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/InMemoryHierarchicalDataProvider.java b/server/src/main/java/com/vaadin/data/provider/TreeDataProvider.java similarity index 52% rename from server/src/main/java/com/vaadin/data/provider/InMemoryHierarchicalDataProvider.java rename to server/src/main/java/com/vaadin/data/provider/TreeDataProvider.java index 8f70dd8e8c2..b30a286afa8 100644 --- a/server/src/main/java/com/vaadin/data/provider/InMemoryHierarchicalDataProvider.java +++ b/server/src/main/java/com/vaadin/data/provider/TreeDataProvider.java @@ -20,17 +20,13 @@ import java.util.Optional; import java.util.stream.Stream; -import com.vaadin.data.HierarchyData; -import com.vaadin.data.ValueProvider; +import com.vaadin.data.TreeData; import com.vaadin.server.SerializableComparator; import com.vaadin.server.SerializableFunction; import com.vaadin.server.SerializablePredicate; -import com.vaadin.shared.data.sort.SortDirection; /** - * A {@link DataProvider} for in-memory hierarchical data. - * - * @see HierarchyData + * {@link HierarchicalDataProvider} wrapper for {@link TreeData}. * * @author Vaadin Ltd * @since 8.1 @@ -38,27 +34,27 @@ * @param * data type */ -public class InMemoryHierarchicalDataProvider extends - AbstractHierarchicalDataProvider> implements - ConfigurableFilterDataProvider, SerializablePredicate> { +public class TreeDataProvider + extends AbstractHierarchicalDataProvider> + implements InMemoryDataProvider { - private final HierarchyData hierarchyData; + private final TreeData treeData; private SerializablePredicate filter = null; private SerializableComparator sortOrder = null; /** - * Constructs a new InMemoryHierarchicalDataProvider. + * Constructs a new TreeDataProvider. *

    - * All changes made to the given HierarchyData object will also be visible - * through this data provider. + * All changes made to the given {@link TreeData} object will also be + * visible through this data provider. * - * @param hierarchyData - * the backing HierarchyData for this provider + * @param treeData + * the backing {@link TreeData} for this provider */ - public InMemoryHierarchicalDataProvider(HierarchyData hierarchyData) { - this.hierarchyData = hierarchyData; + public TreeDataProvider(TreeData treeData) { + this.treeData = treeData; } /** @@ -66,24 +62,19 @@ public InMemoryHierarchicalDataProvider(HierarchyData hierarchyData) { * * @return the underlying data of this provider */ - public HierarchyData getData() { - return hierarchyData; - } - - @Override - public boolean isInMemory() { - return true; + public TreeData getTreeData() { + return treeData; } @Override public boolean hasChildren(T item) { - if (!hierarchyData.contains(item)) { + if (!treeData.contains(item)) { throw new IllegalArgumentException("Item " + item - + " could not be found in the backing HierarchyData. " + + " could not be found in the backing TreeData. " + "Did you forget to refresh this data provider after item removal?"); } - return !hierarchyData.getChildren(item).isEmpty(); + return !treeData.getChildren(item).isEmpty(); } @Override @@ -95,15 +86,15 @@ public int getChildCount( @Override public Stream fetchChildren( HierarchicalQuery> query) { - if (!hierarchyData.contains(query.getParent())) { + if (!treeData.contains(query.getParent())) { throw new IllegalArgumentException("The queried item " + query.getParent() - + " could not be found in the backing HierarchyData. " + + " could not be found in the backing TreeData. " + "Did you forget to refresh this data provider after item removal?"); } Stream childStream = getFilteredStream( - hierarchyData.getChildren(query.getParent()).stream(), + treeData.getChildren(query.getParent()).stream(), query.getFilter()); Optional> comparing = Stream @@ -118,88 +109,28 @@ public Stream fetchChildren( return childStream.skip(query.getOffset()).limit(query.getLimit()); } + @Override + public SerializablePredicate getFilter() { + return filter; + } + @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)); - } + @Override + public SerializableComparator getSortComparator() { + return sortOrder; } - /** - * Sets the comparator to use as the default sorting for this data provider. - * This overrides the sorting set by any other method that manipulates the - * default sorting of this data provider. - *

    - * 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 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) { diff --git a/server/src/main/java/com/vaadin/ui/Tree.java b/server/src/main/java/com/vaadin/ui/Tree.java index 97e1b94cecd..9a765238c12 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.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 @@ * @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 SelectionModel getSelectionModel() { 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 720bffaf3ee..1b051d1da6a 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.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 @@ * @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 Registration addCollapseListener(CollapseListener listener) { 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 @@ protected void readData(Element body, 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 @@ protected void readData(Element body, } } - 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/TreeDataProviderTest.java similarity index 85% rename from server/src/test/java/com/vaadin/data/provider/InMemoryHierarchicalDataProviderTest.java rename to server/src/test/java/com/vaadin/data/provider/TreeDataProviderTest.java index 99b7a04b191..3f9f1f6e949 100644 --- a/server/src/test/java/com/vaadin/data/provider/InMemoryHierarchicalDataProviderTest.java +++ b/server/src/test/java/com/vaadin/data/provider/TreeDataProviderTest.java @@ -1,6 +1,7 @@ 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; @@ -8,13 +9,13 @@ import org.junit.Assert; import org.junit.Test; -import com.vaadin.data.HierarchyData; +import com.vaadin.data.TreeData; import com.vaadin.server.SerializablePredicate; -public class InMemoryHierarchicalDataProviderTest extends - DataProviderTestBase> { +public class TreeDataProviderTest extends + DataProviderTestBase> { - private HierarchyData data; + private TreeData data; private List flattenedData; private List rootData; @@ -24,7 +25,7 @@ public void setUp() { flattenedData = new ArrayList<>(); rootData = new ArrayList<>(); - data = new HierarchyData<>(); + 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)); @@ -44,41 +45,59 @@ public void setUp() { } @Test(expected = IllegalArgumentException.class) - public void hierarchyData_add_item_parent_not_in_hierarchy_throws() { - new HierarchyData<>().addItem(new StrBean("", 0, 0), + 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 hierarchyData_add_null_item_throws() { - new HierarchyData<>().addItem(null, null); + public void treeData_add_null_item_throws() { + new TreeData<>().addItem(null, null); } @Test(expected = IllegalArgumentException.class) - public void hierarchyData_add_item_already_in_hierarchy_throws() { + public void treeData_add_item_already_in_hierarchy_throws() { StrBean bean = new StrBean("", 0, 0); - new HierarchyData<>().addItem(null, bean).addItem(null, bean); + new TreeData<>().addItem(null, bean).addItem(null, bean); } @Test - public void hierarchyData_remove_root_item() { + public void treeData_remove_root_item() { data.removeItem(null); Assert.assertTrue(data.getChildren(null).isEmpty()); } @Test - public void hierarchyData_clear() { + public void treeData_clear() { data.clear(); Assert.assertTrue(data.getChildren(null).isEmpty()); } @Test - public void hierarchyData_re_add_removed_item() { + 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") @@ -226,8 +245,8 @@ public void testSortByComparatorListsDiffer() { } @Override - protected InMemoryHierarchicalDataProvider createDataProvider() { - return new InMemoryHierarchicalDataProvider<>(data); + protected TreeDataProvider createDataProvider() { + return new TreeDataProvider<>(data); } @Override 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 13ef0acc497..9224934a4cb 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 @@ 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 boolean isCollapsed() { @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 2e335f865ea..1544a4f69dd 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 @@ 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 void testChangeRendererOfHierarchyColumn() { @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 b4f98518670..d2d25b47772 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 c8a51a1928e..a13ba357eff 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 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 void dataSerialization() throws InstantiationException, 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 void dataSerialization() throws InstantiationException, 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 e0fa469fa8d..a579c98a12c 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 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 @@ 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 @@ private void createIconMenu(MenuItem iconMenu) { } private void setupDataProvider() { - HierarchyData data = new HierarchyData<>(); + TreeData data = new TreeData<>(); List ints = Arrays.asList(0, 1, 2); @@ -145,7 +145,7 @@ private void setupDataProvider() { }); }); - 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 8151aa8c887..13064c4c399 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 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 @@ public class TreeGridBasicFeatures extends AbstractComponentTest { private TreeGrid grid; - private InMemoryHierarchicalDataProvider inMemoryDataProvider; + private TreeDataProvider inMemoryDataProvider; private LazyHierarchicalDataProvider lazyDataProvider; private HierarchicalDataProvider loggingDataProvider; @@ -70,7 +70,7 @@ protected void createActions() { } private void initializeDataProviders() { - HierarchyData data = new HierarchyData<>(); + TreeData data = new TreeData<>(); List ints = Arrays.asList(0, 1, 2); @@ -89,9 +89,9 @@ private void initializeDataProviders() { }); }); - 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 @@ private void createDataProviderSelect() { @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 c7943524e49..d630e362926 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 @@ 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 @@ 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 boolean hasChildren(String item) { @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 Stream fetchChildren( @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 7cfb9268497..f56052400e4 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 @@ 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 @@ private void initializeDataProvider() { } } } - 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 fc550a95703..02bfd65a289 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 @@ 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 @@ public class TreeGridHugeTreeNavigation extends AbstractComponentTest { private TreeGrid treeGrid; - private InMemoryHierarchicalDataProvider inMemoryDataProvider; + private TreeDataProvider inMemoryDataProvider; @Override public TreeGrid getComponent() { @@ -40,7 +40,7 @@ protected void initializeComponents() { } 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 @@ private void initializeDataProvider() { } } } - 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 db5b8c54b60..4c4b95c3d18 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 @@ protected void setup(VaadinRequest request) { 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 531cd8e283c..3f4047e4572 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 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 @@ protected TreeGrid createComponent() { @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 18b320abba2..a766490017b 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 void setDataProvider(String dataProviderString) { @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 2000d1388bf..4e56e8445ef 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 void client_sorting_with_collapse_and_expand() { 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);