| Summary: | [Viewers] Virtual tree does not populate data on TreeItem.getData | ||
|---|---|---|---|
| Product: | [Eclipse Project] Platform | Reporter: | Chris Lee <eclipse> |
| Component: | SWT | Assignee: | Platform-SWT-Inbox <platform-swt-inbox> |
| Status: | RESOLVED INVALID | QA Contact: | |
| Severity: | normal | ||
| Priority: | P3 | CC: | bokowski, eclipse.felipe, hsoliwal, prakash |
| Version: | 3.4 | ||
| Target Milestone: | --- | ||
| Hardware: | PC | ||
| OS: | Windows XP | ||
| Whiteboard: | |||
As a note, this was rather difficult to debug, since the debugger will automatically call toString on objects when inspecting them, making the bug not reproducible in that case... Why do you expect getData() to return data != null ? Are you calling setData() ? Can you write a SWT only snippet that reproduces the problem ? I suppose your content provider (SetData Listener) is calling setData() when the item is initialized (toString() causes the item to be initialized). I'm using a JFace TreeViewer and ILazyTreeContentProvider, which seems like it is supposed to manage the setData stuff. Perhaps this bug belongs to JFace rather than SWT (as I would need to include JFace to write a snippet)? Moving to UI I'm not sure if it is correct for you to rely on getData() returning something when the item is not initialized. The issue actually arose as we started migrating some trees from non-virtual to virtual. If accessing the Tree and the TreeItem data through TreeViewer.getTree () is not appropriate for virtual trees, is there an alternative (besides pasting "item.getText()" everywhere)...? (In reply to comment #5) > If accessing the Tree and the TreeItem data through TreeViewer.getTree () is > not appropriate for virtual trees, is there an alternative (besides pasting > "item.getText()" everywhere)...? No - if you expect the SetData listener to be fired (which will then cause setData to be called by JFace), you need to call a method that materializes the tree item. I think we're doing the same thing in JFace - calling getText() when we want an item to be materialized, and calling setText() when we want an item to not cause a SetData callback anymore. The only alternative I see would be new API in SWT, for explicitly causing a SetData callback if necessary. If this API was added, it would be useful to also have API for querying whether an item is materialized (cached), and for setting its cached flag to true without having to call setText(""). Moving back to SWT for consideration of this new API. Meanwhile, Chris, could you describe why you need to call getData() on the items yourself? It is not clear to me why you are doing this, maybe the solution for you would be to not have to call getData()? Boris, as you said, you can materialized the item by calling setText() or any other setter for that matter. If you want to cache to thrown away you can use TreeItem#clear/Tree#clear/Tree#clearAll. Knowing if an item is materialized is a bit more trick, but basically you need to set a flag when the SetDataa comes in and clear the flag when clear is called (of course, clear can be called by someone else and you won't see it). I still don't know what the problem is. Cris, what are trying to do ? Here are some details on why we're currently using these functions:
In the tree that this is an issue for, we're storing certain objects as data - let's call them 'GPath' objects.
There is also a user workflow where we want to programmatically select a GPath in the tree view, expanding nodes along the way. If a particular GPath doesn't exist, we want to get as close as possible.
Based on a leaf GPath node, we're able to tell what all the branches are along the way - for example in the tree below, we know that the branch nodes for '6' are (in order) '1' and '5'. However, we're unable to tell what index those branches are from the parent (ie: unable to tell that '5' is the second child of '1'). It's also the case that '6' might not exist, and in that case, we want to select '5'.
1
'- 2
' '-3
' '-4
'- 5
' '-6
To address this, we made a function that essentially
(a) iterates over the required branches (in the example, the iteration would go through '1', '5', '6'), and
(b) checks the data in the tree to see if the next branch is at that level, then
(c) continues searching for the next level.
Here's a simplified version of the function in our extension of TreeViewer:
private TreeItem findNodeFromHierarchy (GPath hierarchy)
{
TreeItem[] items = getTree ().getItems ();
TreeItem node = items[0]; // we always have a single root
outerloop: for (GPath cursor : hierarchy.getPaths ()) // see comment(a)
{
for (TreeItem item : node.getItems ())
{
item.toString (); // temp workaround for this bug
if (item.getData ().equals (cursor)) // see comment(b)
{
node = item;
if (cursor != hierarchy)
setExpandedState (node.getData (), true);
// Continue as long as we keep finding the next node.
continue outerloop; // see comment(c)
}
}
break;
}
}
The problem is that without the workaround (item.toString()), the data check (to see if this is the node we should expand to continue the search) fails.
As a note, we have a comment at the top of the function (legacy from when this tree was non-virtual) that says "we must expand as we traverse or else the tree will return null items". I assume this means that even when using a non-virtual tree, if we do not call setExpandedState, item.getData returns null? (I'm not the original author of this function, so I'm only guessing here).
This suggests a that setExpandedState (or something it delegates to) for virtual trees would be a good place to have the setData call?
(In reply to comment #8) > There is also a user workflow where we want to programmatically select a GPath > in the tree view, expanding nodes along the way. Chris, why are you not using AbstractTreeViewer.expandToLevel(new TreePath(new Object[]{n1,n5,n6}), 0) ? If your tree content provider implements getParent(Object), you could even just call expandToLevel(n6, 0). (In reply to comment #9) > (In reply to comment #8) > > There is also a user workflow where we want to programmatically select a GPath > > in the tree view, expanding nodes along the way. > Chris, why are you not using AbstractTreeViewer.expandToLevel(new TreePath(new > Object[]{n1,n5,n6}), 0) ? > If your tree content provider implements getParent(Object), you could even just > call expandToLevel(n6, 0). Honestly, I had assumed we'd tried this before without success, but if I change our code to use either version of expandToLevel as suggested, it works great. I'll mark this bug as invalid. |
Build Identifier: M20080911-1700 The TreeItem.getData() is returning null unless TreeItem.toString() is called first. The following function should be enough to demonstrate. public void test (Tree tree) { TreeItem[] items = tree.getItems (); TreeItem root = items[0]; for (TreeItem item : root.getItems ()) // properly updates child count { System.out.println (item.getData ()); // BUG - returns null System.out.println (item); // populates the data, outputs item System.out.println (item.getData ()); // now returns correct data } } Reproducible: Always Steps to Reproduce: 1. Create a TreeViewer with SWT.VIRTUAL flag and hook it up with an ILazyTreeContentProvider 2. Call test function as described in this bug.