Skip to content

Commit

Permalink
Refactor common methods in in-memory data providers (#9308)
Browse files Browse the repository at this point in the history
* Refactor common methods of InMemoryHierarchicalDataProvider and ListDataProvider to a single interface
* Rename HierarchyData and InMemoryHierarchicalDataProvider, introduce HasHierarchicalDataProvider
* Additionally adds a helper method for recursive constructing
TreeData with a child item provider.
  • Loading branch information
ahie authored and pleku committed May 16, 2017
1 parent eb743d9 commit efa7f5a
Show file tree
Hide file tree
Showing 27 changed files with 1,070 additions and 808 deletions.
2 changes: 1 addition & 1 deletion all/src/main/templates/release-notes.html
Expand Up @@ -102,7 +102,7 @@ <h2 id="incompatible">Incompatible or Behavior-altering Changes in @version@</h2


<li>The <tt>TreeGrid</tt> component replaces <tt>TreeTable</tt> component</li> <li>The <tt>TreeGrid</tt> component replaces <tt>TreeTable</tt> component</li>
<li>The <tt>Tree</tt> component replaces old <tt>Tree</tt> component</li> <li>The <tt>Tree</tt> component replaces old <tt>Tree</tt> component</li>
<li>The <tt>HierarchicalDataProvider</tt> interface replaces <tt>Container.Hierarchical</tt> and <<tt>InMemoryHierarchicalDataProvider</tt> replaces <tt>HierarchicalContainer</tt></li> <li>The <tt>HierarchicalDataProvider</tt> interface replaces <tt>Container.Hierarchical</tt> and <<tt>TreeDataProvider</tt> replaces <tt>HierarchicalContainer</tt></li>
<li>The <tt>DragSourceExtension</tt> and <tt>DropTargetExtension</tt> extensions replace the old DnD features</li> <li>The <tt>DragSourceExtension</tt> and <tt>DropTargetExtension</tt> extensions replace the old DnD features</li>
<li>OSGi bundle manifests of Vaadin Framework JARs no longer export <tt>/VAADIN</tt>, and there are new mechanisms for publishing static resources for OSGi</li> <li>OSGi bundle manifests of Vaadin Framework JARs no longer export <tt>/VAADIN</tt>, and there are new mechanisms for publishing static resources for OSGi</li>
<li>Tooltip styles for <tt>ContentMode.PREFORMATTED</tt> have been changed in all built-in themes to use the application font and allow long lines to wrap to multiple lines.</li> <li>Tooltip styles for <tt>ContentMode.PREFORMATTED</tt> have been changed in all built-in themes to use the application font and allow long lines to wrap to multiple lines.</li>
Expand Down
28 changes: 14 additions & 14 deletions documentation/components/components-tree.asciidoc
Expand Up @@ -31,32 +31,32 @@ image::img/tree-basic.png[width=70%, scaledwidth=100%]
[[components.tree.data]] [[components.tree.data]]
== Binding to 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
<<dummy/../../../framework/datamodel/datamodel-hierarchical.asciidoc#datamodel.hierarchical,"Hierarchical Data">>. <<dummy/../../../framework/datamodel/datamodel-hierarchical.asciidoc#datamodel.hierarchical,"Hierarchical Data">>.




The [classname]#HierarchyData# class can be used to build the hierarchical data structure, The [classname]#TreeData# 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 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#. 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 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] [source, java]
---- ----
// An initial planet tree // An initial planet tree
Tree<String> tree = new Tree<>(); Tree<String> tree = new Tree<>();
HierarchyData<String> hierarchyData = new HierarchyData<>(); TreeData<String> treeData = new TreeData<>();
// Couple of childless root items // Couple of childless root items
hierarchyData.addItem(null,"Mercury"); treeData.addItem(null,"Mercury");
hierarchyData.addItem(null,"Venus"); treeData.addItem(null,"Venus");
// Items with hierarchy // Items with hierarchy
hierarchyData.addItem(null,"Earth"); treeData.addItem(null,"Earth");
hierarchyData.addItem("Earth","The Moon"); treeData.addItem("Earth","The Moon");
inMemoryDataProvider = new InMemoryHierarchicalDataProvider<>(hierarchyData); inMemoryDataProvider = new TreeDataProvider<>(treeData);
tree.setDataProvider(inMemoryDataProvider); tree.setDataProvider(inMemoryDataProvider);
tree.expand("Earth"); // Expand programmatically tree.expand("Earth"); // Expand programmatically
---- ----
Expand All @@ -67,9 +67,9 @@ the in-memory data in the tree, you may do it as follows:
[source, java] [source, java]
---- ----
// Add Mars with satellites // Add Mars with satellites
hierarchyData.addItem(null, "Mars"); treeData.addItem(null, "Mars");
hierarchyData.addItem("Mars", "Phobos"); treeData.addItem("Mars", "Phobos");
hierarchyData.addItem("Mars", "Deimos"); treeData.addItem("Mars", "Deimos");
inMemoryDataProvider.refreshAll(); inMemoryDataProvider.refreshAll();
---- ----
Expand Down Expand Up @@ -208,4 +208,4 @@ You could thereby define the item styling as follows:
font-style: italic; font-style: italic;
} }
---- ----
//// ////
16 changes: 8 additions & 8 deletions documentation/components/components-treegrid.asciidoc
Expand Up @@ -30,7 +30,7 @@ image::img/tree-grid-basic.png[width=70%, scaledwidth=100%]
[[components.treegrid.data]] [[components.treegrid.data]]
== Binding to 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
<<dummy/../../../framework/datamodel/datamodel-hierarchical.asciidoc#datamodel.hierarchical,"Hierarchical Data">>. <<dummy/../../../framework/datamodel/datamodel-hierarchical.asciidoc#datamodel.hierarchical,"Hierarchical Data">>.


Populating a [classname]#TreeGrid# with in-memory data can be done as follows Populating a [classname]#TreeGrid# with in-memory data can be done as follows
Expand All @@ -39,7 +39,7 @@ Populating a [classname]#TreeGrid# with in-memory data can be done as follows
---- ----
Project rootProject = getRootRroject(); Project rootProject = getRootRroject();
HierarchyData<Project> data = new HierarchyData<>(); TreeData<Project> data = new TreeData<>();
// add a root level item with null parent // add a root level item with null parent
data.addItem(null, rootProject); data.addItem(null, rootProject);
Expand All @@ -48,26 +48,26 @@ rootProject.flattened().forEach(
project -> data.addItems(project, project.getSubProjects())); project -> data.addItems(project, project.getSubProjects()));
TreeGrid<Project> treeGrid = new TreeGrid<>(); TreeGrid<Project> treeGrid = new TreeGrid<>();
treeGrid.setDataProvider(new InMemoryHierarchicalDataProvider<>(data)); treeGrid.setDataProvider(new TreeDataProvider<>(data));
// the first column gets the hierarchy indicator by default // the first column gets the hierarchy indicator by default
treeGrid.addColumn(Project::getName).setCaption("Project Name"); treeGrid.addColumn(Project::getName).setCaption("Project Name");
treeGrid.addColumn(Project::getHoursDone).setCaption("Hours Done"); treeGrid.addColumn(Project::getHoursDone).setCaption("Hours Done");
treeGrid.addColumn(Project::getLastModified).setCaption("Last Modified"); treeGrid.addColumn(Project::getLastModified).setCaption("Last Modified");
---- ----


The [classname]#HierarchyData# class can be used to build the hierarchical data structure, The [classname]#TreeData# 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 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#. 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 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] [source, java]
---- ----
InMemoryHierarchicalDataProvider<Project> dataProvider = (InMemoryHierarchicalDataProvider<Project>) treeGrid.getDataProvider(); TreeDataProvider<Project> dataProvider = (TreeDataProvider<Project>) treeGrid.getDataProvider();
HierarchyData<Project> data = dataProvider.getData(); TreeData<Project> data = dataProvider.getData();
// add new items // add new items
data.addItem(null, newProject); data.addItem(null, newProject);
data.addItems(newProject, newProject.getChildren()); data.addItems(newProject, newProject.getChildren());
Expand Down
22 changes: 11 additions & 11 deletions documentation/datamodel/datamodel-hierarchical.asciidoc
Expand Up @@ -11,38 +11,38 @@ 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. 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, 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 <<dummy/../../../framework/components/components-tree.asciidoc#components.tree,"Tree">> For using them with the components you should see <<dummy/../../../framework/components/components-tree.asciidoc#components.tree,"Tree">>
and <<dummy/../../../framework/components/components-treegrid.asciidoc#components.treegrid,"TreeGrid">> documentation. and <<dummy/../../../framework/components/components-treegrid.asciidoc#components.treegrid,"TreeGrid">> documentation.


== In-memory Hierarchical Data == 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] [source, java]
---- ----
Collection<Project> projects = service.getProjects(); Collection<Project> projects = service.getProjects();
HierarchyData<Project> data = new HierarchyData<>(); TreeData<Project> data = new TreeData<>();
// add root level items // add root level items
data.addItems(null, projects); data.addItems(null, projects);
// add children for the root level items // add children for the root level items
projects.forEach(project -> data.addItems(project, project.getChildren())); projects.forEach(project -> data.addItems(project, project.getChildren()));
// construct the data provider for the hierarchical data we've built // construct the data provider for the hierarchical data we've built
InMemoryHierarchicalDataProvider<Project> dataProvider = new InMemoryHierarchicalDataProvider<>(data); TreeDataProvider<Project> dataProvider = new TreeDataProvider<>(data);
---- ----


=== Updating 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] [source, java]
---- ----
HierarchyData<Project> data = dataProvider.getHierarchyData(); TreeData<Project> data = dataProvider.getData();
data.addItem(null, newProject); data.addItem(null, newProject);
data.addItems(newProject, newProject.getChildren()); data.addItems(newProject, newProject.getChildren());
Expand All @@ -55,14 +55,14 @@ dataProvider.refreshAll();


=== Sorting and Filtering === 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] [source, java]
---- ----
// setting sorting or filtering automatically refreshes the data // setting sorting or filtering automatically refreshes the data
dataProvider.setSortComparator((projectA, projectB) -> dataProvider.setSortComparator((projectA, projectB) ->
projectA.getHours().compareTo(projectB.getHours())); projectA.getHours().compareTo(projectB.getHours()));
dataProvider.setFilter(project -> project.getHours() > 100); dataProvider.setFilter(project -> project.getHours() > 100);
---- ----


Expand All @@ -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: 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. ** 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<T, F> query)` * `int getChildCount(HierarchicalQuery<T, F> query)`
Expand All @@ -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. ** 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 ** 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: The following code snippet shows a simple example on how to building a lazy hierarchical data provider based on file system structure:


Expand Down

0 comments on commit efa7f5a

Please sign in to comment.