Skip to content

Commit 2cec594

Browse files
committed
Snippet to reproduce exception when calling getExpandedTreePaths()
This snippet shows how to cause an AssertionFailedException when calling getExpandedTreePaths() on a virtual tree. When clicking the "refresh" button for the first time, everything works fine. On the second time, the following error is logged: > !MESSAGE Invalid index value 2 >= 2 On the third time, an AssertionFailedException is thrown. See: #2491
1 parent edc926d commit 2cec594

File tree

1 file changed

+182
-0
lines changed

1 file changed

+182
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 DSA GmbH and others.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* DSA GmbH - initial API and implementation
13+
*******************************************************************************/
14+
15+
package org.eclipse.jface.snippets.viewers;
16+
17+
import java.net.MalformedURLException;
18+
import java.net.URI;
19+
20+
import org.eclipse.core.runtime.Platform;
21+
import org.eclipse.jface.resource.ImageDescriptor;
22+
import org.eclipse.jface.resource.JFaceResources;
23+
import org.eclipse.jface.resource.ResourceManager;
24+
import org.eclipse.jface.viewers.ColumnLabelProvider;
25+
import org.eclipse.jface.viewers.ILazyTreeContentProvider;
26+
import org.eclipse.jface.viewers.TreeNode;
27+
import org.eclipse.jface.viewers.TreePath;
28+
import org.eclipse.jface.viewers.TreeViewer;
29+
import org.eclipse.swt.SWT;
30+
import org.eclipse.swt.events.SelectionListener;
31+
import org.eclipse.swt.graphics.Image;
32+
import org.eclipse.swt.layout.BorderData;
33+
import org.eclipse.swt.layout.BorderLayout;
34+
import org.eclipse.swt.widgets.Shell;
35+
import org.eclipse.swt.widgets.ToolBar;
36+
import org.eclipse.swt.widgets.ToolItem;
37+
38+
/**
39+
* A simple example to illustrate how to preserve the expand/collapse state when
40+
* using a virtual viewer.
41+
*/
42+
public class Snippet070VirtualViewerWithExpandedState {
43+
public static class SimpleNode extends TreeNode {
44+
public SimpleNode(Object value) {
45+
super(value);
46+
}
47+
48+
public int getChildCount() {
49+
SimpleNode[] children = getChildren();
50+
return children == null ? 0 : children.length;
51+
}
52+
53+
public void setParentChildren(SimpleNode... children) {
54+
for (TreeNode child : children) {
55+
child.setParent(this);
56+
}
57+
setChildren(children);
58+
}
59+
60+
@Override
61+
public SimpleNode[] getChildren() {
62+
return (SimpleNode[]) super.getChildren();
63+
}
64+
65+
@Override
66+
public String toString() {
67+
return "[%s], %s".formatted(getClass().getSimpleName(), getValue());
68+
}
69+
}
70+
71+
public static class LazyContentProvider implements ILazyTreeContentProvider {
72+
private final TreeViewer viewer;
73+
74+
public LazyContentProvider(TreeViewer viewer) {
75+
this.viewer = viewer;
76+
}
77+
@Override
78+
public void updateElement(Object parent, int index) {
79+
SimpleNode[] children = ((SimpleNode) parent).getChildren();
80+
if (children == null) {
81+
// viewer.setHasChildren(parent, false);
82+
} else if (children.length > index) {
83+
SimpleNode child = children[index];
84+
viewer.replace(parent, index, child);
85+
viewer.setChildCount(child, child.getChildCount());
86+
} else {
87+
Platform.getLog(getClass()).warn("Invalid index value %d >= %d".formatted(index, children.length));
88+
}
89+
}
90+
91+
@Override
92+
public void updateChildCount(Object element, int currentChildCount) {
93+
int newChildCount = ((SimpleNode) element).getChildCount();
94+
if (currentChildCount != newChildCount) {
95+
viewer.setChildCount(element, newChildCount);
96+
}
97+
}
98+
99+
@Override
100+
public Object getParent(Object element) {
101+
return ((SimpleNode) element).getParent();
102+
}
103+
}
104+
105+
static SimpleNode root;
106+
static SimpleNode node0;
107+
static SimpleNode node1;
108+
static SimpleNode node2;
109+
static SimpleNode node3;
110+
static SimpleNode node4;
111+
static SimpleNode node5;
112+
static SimpleNode node6;
113+
static SimpleNode node7;
114+
static SimpleNode node8;
115+
public static boolean flip = false;
116+
117+
public static void flip() {
118+
flip = !flip;
119+
if (flip) {
120+
node0.setParentChildren(node1, node2, node8);
121+
} else {
122+
node0.setParentChildren(node2, node8);
123+
}
124+
}
125+
126+
public static void main(String[] args) throws MalformedURLException {
127+
root = new SimpleNode("Invisible");
128+
node0 = new SimpleNode(0);
129+
node1 = new SimpleNode(1);
130+
node2 = new SimpleNode(2);
131+
node3 = new SimpleNode(3);
132+
node4 = new SimpleNode(4);
133+
node5 = new SimpleNode(5);
134+
node6 = new SimpleNode(6);
135+
node7 = new SimpleNode(7);
136+
node8 = new SimpleNode(8);
137+
138+
root.setParentChildren(node0);
139+
node0.setParentChildren(node2, node8);
140+
node2.setParentChildren(node3, node4, node5);
141+
node5.setParentChildren(node6, node7);
142+
143+
Shell shell = new Shell();
144+
shell.setSize(500, 155);
145+
shell.setLayout(new BorderLayout());
146+
147+
TreeViewer viewer = new TreeViewer(shell, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER | SWT.VIRTUAL);
148+
viewer.setContentProvider(new LazyContentProvider(viewer));
149+
viewer.setLabelProvider(new ColumnLabelProvider());
150+
viewer.getTree().setLayoutData(new BorderData(SWT.CENTER));
151+
viewer.setUseHashlookup(true);
152+
viewer.setInput(root);
153+
viewer.expandAll();
154+
155+
ToolBar toolBar = new ToolBar(shell, SWT.NONE);
156+
toolBar.setLayoutData(new BorderData(SWT.TOP));
157+
158+
ResourceManager resourceManager = JFaceResources.managerFor(shell);
159+
ImageDescriptor imageDescriptor = ImageDescriptor
160+
.createFromURL(URI.create("platform:/plugin/org.eclipse.pde.ui/icons/elcl16/refresh.png").toURL());
161+
Image image = resourceManager.create(imageDescriptor);
162+
ToolItem toolItem = new ToolItem(toolBar, SWT.PUSH);
163+
toolItem.setImage(image);
164+
toolItem.addSelectionListener(SelectionListener.widgetSelectedAdapter(event -> {
165+
flip();
166+
167+
TreePath[] treePaths = viewer.getExpandedTreePaths();
168+
viewer.setInput(root);
169+
viewer.setExpandedTreePaths(treePaths);
170+
171+
// This refresh is important, as it recalculates all tree items. Because the
172+
// viewer doesn't fit in the shell, some of those items remain virtual, which
173+
// then cause an exception, the next time this listener is executed.
174+
viewer.refresh();
175+
}));
176+
177+
shell.open();
178+
while (!shell.isDisposed()) {
179+
shell.getDisplay().readAndDispatch();
180+
}
181+
}
182+
}

0 commit comments

Comments
 (0)