JDK-8096243 : [ListView] Removing multiple items including the selected item leads to inconsistent and wrong selection state
  • Type: Bug
  • Component: javafx
  • Sub-Component: controls
  • Affected Version: 8u20
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2014-09-24
  • Updated: 2020-06-11
  • Resolved: 2014-10-20
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8
8u40Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Description
To reproduce:

- add items "a","b","c","d"
- select item "b"
- remove items "b","c" using removeAll method

expected:

- getSelectedItem() returns "a", getSelectedIndex() returns 0
- getSelectedItems().get(0) returns "a", getSelectedIndices().get(0) returns 0

actual:

- getSelectedItem() returns "b", getSelectedIndex() returns 1
- getSelectedItems().get(0) returns null, getSelectedIndices().get(0) returns -1

Test case to reproduce:

    @Test
    public void testListViewSelectionChanges() throws Exception {
        new JFXPanel();
        ListView<String> stringListView = new ListView<>();
        stringListView.getItems().addAll("a","b","c","d");
        stringListView.getSelectionModel().select("b");

        stringListView.getItems().removeAll("b","c");

        Assert.assertEquals(0, stringListView.getSelectionModel().getSelectedIndex());
        Assert.assertEquals(Integer.valueOf(0), stringListView.getSelectionModel().getSelectedIndices().get(0));
        Assert.assertEquals("a", stringListView.getSelectionModel().getSelectedItem());
        Assert.assertEquals("a", stringListView.getSelectionModel().getSelectedItems().get(0));
    }
Comments
Changeset: http://hg.openjdk.java.net/openjfx/8u-dev/rt/rev/7319b471069b Unit tests: javafx.scene.control.ListViewTest.test_rt_38787_remove_b() javafx.scene.control.ListViewTest.test_rt_38787_remove_b_c() javafx.scene.control.ListViewTest.test_rt_38787_remove_c_d() javafx.scene.control.ListViewTest.test_rt_38787_remove_a() javafx.scene.control.ListViewTest.test_rt_38787_remove_z() javafx.scene.control.TableViewTest.test_rt_38787_remove_b() javafx.scene.control.TableViewTest.test_rt_38787_remove_b_c() javafx.scene.control.TableViewTest.test_rt_38787_remove_c_d() javafx.scene.control.TableViewTest.test_rt_38787_remove_a() javafx.scene.control.TableViewTest.test_rt_38787_remove_z() javafx.scene.control.TreeTableViewTest.test_rt_38787_remove_b() javafx.scene.control.TreeTableViewTest.test_rt_38787_remove_b_c() javafx.scene.control.TreeTableViewTest.test_rt_38787_remove_c_d() javafx.scene.control.TreeTableViewTest.test_rt_38787_remove_a() javafx.scene.control.TreeViewTest.test_rt_38787_remove_b() javafx.scene.control.TreeViewTest.test_rt_38787_remove_b_c() javafx.scene.control.TreeViewTest.test_rt_38787_remove_c_d() javafx.scene.control.TreeViewTest.test_rt_38787_remove_a()
20-10-2014

@Jonathan I didn't mean to say the code block causes the problem. That is not the case. It is however merely addressing one symptom in a very odd manner and not the underlying cause (the flimsical code in updateSelection) We stumbled over this when we were puzzled why selectedItem changes twice and change listeners are triggered twice if you remove the selected item, while selectedIndex only changes once. @Test public void testRemoveSelectedItem() throws Exception { new JFXPanel(); ObservableList<String> list = FXCollections.observableArrayList("first", "second", "third", "fourth", "fifth"); ListView<String> listView = new ListView<>(list); listView.getSelectionModel().selectedItemProperty().addListener((observable) -> System.out.println("selectedItem: " + listView.getSelectionModel().getSelectedItem())); listView.getSelectionModel().selectedIndexProperty().addListener((observable) -> System.out.println("selectedIndex: " + listView.getSelectionModel().getSelectedIndex())); System.out.println("select third"); listView.getSelectionModel().select("third"); System.out.println("remove third"); list.remove("third"); } Output: select third selectedItem: third selectedIndex: 2 remove third selectedItem: fourth selectedItem: second selectedIndex: 1 We noticed the selected item changing to "fourth" is due to the fix mentioned. And looking closer at it, it became apparent the fix is really shoe-horned in without looking at the bigger picture.
17-10-2014

I should learn to batch up my comments until I'm done ;-) Scratch my 'scratch that' comment - all is well in the world and it was a false alarm in the unit test case. I'll push the fix next week once the milestone freeze stops.
16-10-2014

Scratch that - I wrote more tests (for ListView) and still can get failures, so I'm not quite there yet.
16-10-2014

Attaching a partial patch that resolves this issue (without touching the previously alluded to code - hence my previous comment asking for more information). It brings in the provided test case (although updated slightly), but I need to replicate that for TreeView, TableView, and TreeTableView.
16-10-2014

@Andreas Can you clarify what makes you say that the code in your first comment is the reason for this bug report? If I comment out that entire 'else if' block I still see the test fail. I'm not saying you're wrong, I'm just asking as I'm only now beginning to look at this issue and it sounds like you may have looked more deeply than I have.
16-10-2014

I looked at this on 2.2.0-bs1, 8.0.0-b54, 8.0.0b97, 8.0.0b132, and 8u20b23 and all throw the same Assertion. Don't think this is a regression.
24-09-2014

With 9.24.2014 latest we get: bash-3.2$ java -Xbootclasspath/a:${JAVAFX_HOME}/rt/lib/ext/jfxrt.jar:../import/junit-4.8.2/junit-4.8.2.jar:. org.junit.runner.JUnitCore ListViewTest JUnit version 4.8.2 .E Time: 1.676 There was 1 failure: 1) testListViewSelectionChanges(ListViewTest) java.lang.AssertionError: expected:<0> but was:<1> at org.junit.Assert.fail(Assert.java:91) at org.junit.Assert.failNotEquals(Assert.java:645) at org.junit.Assert.assertEquals(Assert.java:126) at org.junit.Assert.assertEquals(Assert.java:470) at org.junit.Assert.assertEquals(Assert.java:454) at ListViewTest.testListViewSelectionChanges(ListViewTest.java:18) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) at org.junit.runners.ParentRunner.run(ParentRunner.java:236) at org.junit.runners.Suite.runChild(Suite.java:128) at org.junit.runners.Suite.runChild(Suite.java:24) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) at org.junit.runners.ParentRunner.run(ParentRunner.java:236) at org.junit.runner.JUnitCore.run(JUnitCore.java:157) at org.junit.runner.JUnitCore.run(JUnitCore.java:136) at org.junit.runner.JUnitCore.run(JUnitCore.java:117) at org.junit.runner.JUnitCore.runMain(JUnitCore.java:98) at org.junit.runner.JUnitCore.runMainAndExit(JUnitCore.java:53) at org.junit.runner.JUnitCore.main(JUnitCore.java:45) FAILURES!!! Tests run: 1, Failures: 1
24-09-2014

Need to determine whether this is a regression.
24-09-2014

This is in part caused by an incomplete fix of RT-28637 which only works in case a single item is removed. The respective lines 1236 to 1246 in ListView read: else if (c.wasRemoved() && c.getRemovedSize() == 1 && ! c.wasAdded() && selectedItem != null && selectedItem.equals(c.getRemoved().get(0))) { // Bug fix for RT-28637 if (getSelectedIndex() < getItemCount()) { T newSelectedItem = getModelItem(selectedIndex); if (! selectedItem.equals(newSelectedItem)) { setSelectedItem(newSelectedItem); } } } This horrible workaround fix has the additional drawback that selection is sometimes changed twice, since updateSelection() is still called afterwards and might shift the selection again. It would be much better to rewrite the code in updateSelection() properly.
24-09-2014