Skip to content

Commit 54028a5

Browse files
HeikoKlarefedejeanne
authored andcommitted
Restore recursive behavior of AbstractTreeViwer#internalExpandToLevel()
The method AbstractTreeViewer#internalExpandToLevel() used be a recursively called method (by implementation and specification). As a protected method, it is part of the API. With a recent change, the method was changed to delegate to a different method, removing it's recursive property. Consumers that have created subtypes of the AbstractTreeViewer and rely the recursive behavior of that method by overwriting it face a regression by that change. This change restores the existing, recursive behavior of that method while still keeping the reuse of the implementation across other use cases. A test case ensuring the contractual recursive execution is added.
1 parent d4aa37b commit 54028a5

File tree

2 files changed

+74
-30
lines changed

2 files changed

+74
-30
lines changed

bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/AbstractTreeViewer.java

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import java.util.Iterator;
2929
import java.util.LinkedList;
3030
import java.util.List;
31-
import java.util.function.Function;
3231

3332
import org.eclipse.core.runtime.Assert;
3433
import org.eclipse.core.runtime.IStatus;
@@ -1833,23 +1832,22 @@ protected Widget internalGetWidgetToSelect(Object elementOrTreePath) {
18331832
}
18341833

18351834
/**
1836-
* Recursively, conditionally expands the subtree rooted at the given widget to
1837-
* the given level. Takes the {@code shouldChildrenExpand} predicate that
1838-
* defines for a given widget if it shall be expanded.
1835+
* Recursively expands the subtree rooted at the given widget to the given level
1836+
* based on the {@code childExpansionFunction} function being executed for a
1837+
* child to be (potentially conditionally) expanded.
18391838
* <p>
18401839
* Note that the default implementation of this method does not call
18411840
* {@code setRedraw}.
18421841
* </p>
18431842
*
1844-
* @param widget the widget
1845-
* @param level non-negative level, or {@code ALL_LEVELS} to
1846-
* expand all levels of the tree
1847-
* @param shouldChildrenExpand predicate that defines for a given widget if it
1848-
* should be expanded.
1849-
* @since 3.32
1843+
* @param widget the widget
1844+
* @param level non-negative level, or {@code ALL_LEVELS} to
1845+
* expand all levels of the tree
1846+
* @param childrenExpansionFunction function to be called on a child to be
1847+
* expanded
18501848
*/
1851-
private void internalConditionalExpandToLevel(Widget widget, int level,
1852-
Function<Widget, Boolean> shouldChildrenExpand) {
1849+
private void internalCustomizedExpandToLevel(Widget widget, int level,
1850+
CustomChildrenExpansionFunction childrenExpansionFunction) {
18531851
if (level == ALL_LEVELS || level > 0) {
18541852
Object data = widget.getData();
18551853
if (widget instanceof Item it && data != null && !isExpandable(it, null, data)) {
@@ -1861,19 +1859,18 @@ private void internalConditionalExpandToLevel(Widget widget, int level,
18611859
setExpanded(it, true);
18621860
}
18631861
if (level == ALL_LEVELS || level > 1) {
1864-
Item[] children = getChildren(widget);
1865-
if (children != null && shouldChildrenExpand.apply(widget).booleanValue()) {
1866-
int newLevel = (level == ALL_LEVELS ? ALL_LEVELS
1867-
: level - 1);
1868-
for (Item element : children) {
1869-
internalConditionalExpandToLevel(element, newLevel, shouldChildrenExpand);
1870-
}
1871-
}
1862+
int newLevel = (level == ALL_LEVELS ? ALL_LEVELS : level - 1);
1863+
childrenExpansionFunction.expandChildren(widget, newLevel);
18721864
}
18731865
// XXX expanding here fails on linux
18741866
}
18751867
}
18761868

1869+
@FunctionalInterface
1870+
private interface CustomChildrenExpansionFunction {
1871+
void expandChildren(Widget parent, int previousLevel);
1872+
}
1873+
18771874
/**
18781875
* Recursively expands the subtree rooted at the given widget to the given
18791876
* level.
@@ -1887,7 +1884,14 @@ private void internalConditionalExpandToLevel(Widget widget, int level,
18871884
* levels of the tree
18881885
*/
18891886
protected void internalExpandToLevel(Widget widget, int level) {
1890-
internalConditionalExpandToLevel(widget, level, w -> Boolean.TRUE);
1887+
internalCustomizedExpandToLevel(widget, level, (parent, newLevel) -> {
1888+
Item[] children = getChildren(parent);
1889+
if (children != null) {
1890+
for (Item child : children) {
1891+
internalExpandToLevel(child, newLevel);
1892+
}
1893+
}
1894+
});
18911895
}
18921896

18931897
/**
@@ -2532,14 +2536,22 @@ public void treeCollapsed(TreeExpansionEvent event) {
25322536
@Override
25332537
public void treeExpanded(TreeExpansionEvent e) {
25342538
Widget item = doFindItem(e.getElement());
2535-
2536-
internalConditionalExpandToLevel(item, autoExpandOnSingleChildLevels,
2537-
w -> Boolean.valueOf(doesWidgetHaveExactlyOneChild(w)));
2539+
internalCustomizedExpandToLevel(item, autoExpandOnSingleChildLevels, singleChildExpansionFunction);
25382540
}
25392541
};
25402542
addTreeListener(autoExpandOnSingleChildListener);
25412543
}
25422544

2545+
private CustomChildrenExpansionFunction singleChildExpansionFunction = (widget, newLevel) -> {
2546+
Item[] children = getChildren(widget);
2547+
boolean hasExactlyOneChild = children != null && children.length == 1;
2548+
if (hasExactlyOneChild) {
2549+
for (Item child : children) {
2550+
internalCustomizedExpandToLevel(child, newLevel, this.singleChildExpansionFunction);
2551+
}
2552+
}
2553+
};
2554+
25432555
private void removeAutoExpandOnSingleChildListener() {
25442556
if (autoExpandOnSingleChildListener != null) {
25452557
removeTreeListener(autoExpandOnSingleChildListener);
@@ -2702,8 +2714,7 @@ public void setExpandedStateWithAutoExpandOnSingleChild(Object elementOrTreePath
27022714
Widget item = internalGetWidgetToSelect(elementOrTreePath);
27032715

27042716
if (autoExpandOnSingleChildLevels != NO_EXPAND && expanded) {
2705-
internalConditionalExpandToLevel(item, autoExpandOnSingleChildLevels,
2706-
w -> Boolean.valueOf(doesWidgetHaveExactlyOneChild(w)));
2717+
internalCustomizedExpandToLevel(item, autoExpandOnSingleChildLevels, singleChildExpansionFunction);
27072718
}
27082719
}
27092720

@@ -3535,8 +3546,4 @@ ISelection getUpdatedSelection(ISelection selection) {
35353546
return selection;
35363547
}
35373548

3538-
private boolean doesWidgetHaveExactlyOneChild(Widget w) {
3539-
return getChildren(w).length == 1;
3540-
}
3541-
35423549
}

tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/viewers/TreeViewerTest.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,21 @@
1616
import static org.junit.Assert.assertFalse;
1717
import static org.junit.Assert.assertTrue;
1818

19+
import java.util.ArrayList;
20+
import java.util.Arrays;
21+
import java.util.List;
22+
import java.util.Queue;
23+
import java.util.concurrent.ConcurrentLinkedQueue;
24+
25+
import org.eclipse.jface.viewers.AbstractTreeViewer;
1926
import org.eclipse.jface.viewers.StructuredViewer;
2027
import org.eclipse.jface.viewers.TreeViewer;
2128
import org.eclipse.swt.SWT;
2229
import org.eclipse.swt.widgets.Composite;
2330
import org.eclipse.swt.widgets.Event;
2431
import org.eclipse.swt.widgets.Tree;
2532
import org.eclipse.swt.widgets.TreeItem;
33+
import org.eclipse.swt.widgets.Widget;
2634
import org.junit.Test;
2735

2836
public class TreeViewerTest extends AbstractTreeViewerTest {
@@ -77,4 +85,33 @@ public void testAutoExpandOnSingleChildThroughEvent() {
7785
fTreeViewer.getExpandedState(trivialPathRoot.getFirstChild().getFirstChild()));
7886
}
7987

88+
@Test
89+
public void testInternalExpandToLevelRecursive() {
90+
// internalExpandToLevel is recursive by contract, so we track all processed
91+
// elements in a subtype to validate contractual recursive execution
92+
List<Object> recursiveExpandedElements = new ArrayList<>();
93+
TreeViewer viewer = new TreeViewer(fShell) {
94+
@Override
95+
protected void internalExpandToLevel(Widget widget, int level) {
96+
if (widget != this.getTree()) {
97+
recursiveExpandedElements.add(widget.getData());
98+
}
99+
super.internalExpandToLevel(widget, level);
100+
}
101+
};
102+
TestElement rootElement = TestElement.createModel(2, 5);
103+
viewer.setContentProvider(new TestModelContentProvider());
104+
viewer.setInput(rootElement);
105+
106+
viewer.expandToLevel(AbstractTreeViewer.ALL_LEVELS);
107+
108+
Queue<TestElement> elements = new ConcurrentLinkedQueue<>(Arrays.asList(rootElement.getChildren()));
109+
while (!elements.isEmpty()) {
110+
TestElement currentElement = elements.poll();
111+
assertTrue("expansion for child was not processed: " + currentElement,
112+
recursiveExpandedElements.contains(currentElement));
113+
elements.addAll(Arrays.asList(currentElement.getChildren()));
114+
}
115+
}
116+
80117
}

0 commit comments

Comments
 (0)