Skip to content

Commit f987b18

Browse files
Marius HanlJeanette Winzenburg
Marius Hanl
authored and
Jeanette Winzenburg
committed
8089398: [ChoiceBox, ComboBox] throws NPE on setting value on null selectionModel
Reviewed-by: fastegal, aghaisas
1 parent a272c4f commit f987b18

File tree

5 files changed

+138
-14
lines changed

5 files changed

+138
-14
lines changed

modules/javafx.controls/src/main/java/javafx/scene/control/ChoiceBox.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,12 @@ public ChoiceBox(ObservableList<T> items) {
151151
// selection model to indicate that this is the selected item
152152
valueProperty().addListener((ov, t, t1) -> {
153153
if (getItems() == null) return;
154+
SingleSelectionModel<T> sm = getSelectionModel();
155+
if (sm == null) return;
156+
154157
int index = getItems().indexOf(t1);
155158
if (index > -1) {
156-
getSelectionModel().select(index);
159+
sm.select(index);
157160
}
158161
});
159162
}

modules/javafx.controls/src/main/java/javafx/scene/control/ComboBox.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,9 @@ public ComboBox(ObservableList<T> items) {
240240
// selection model to indicate that this is the selected item
241241
valueProperty().addListener((ov, t, t1) -> {
242242
if (getItems() == null) return;
243-
244243
SelectionModel<T> sm = getSelectionModel();
244+
if (sm == null) return;
245+
245246
int index = getItems().indexOf(t1);
246247

247248
if (index == -1) {
@@ -278,7 +279,10 @@ public ComboBox(ObservableList<T> items) {
278279
if (!isEditable()) {
279280
// check if value is in items list
280281
if (getItems() != null && !getItems().contains(getValue())) {
281-
getSelectionModel().clearSelection();
282+
SingleSelectionModel<T> selectionModel = getSelectionModel();
283+
if (selectionModel != null) {
284+
selectionModel.clearSelection();
285+
}
282286
}
283287
}
284288
});

modules/javafx.controls/src/main/java/javafx/scene/control/skin/ComboBoxListViewSkin.java

+24-8
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import javafx.scene.control.ListView;
5454
import javafx.scene.control.SelectionMode;
5555
import javafx.scene.control.SelectionModel;
56+
import javafx.scene.control.SingleSelectionModel;
5657
import javafx.scene.control.TextField;
5758
import javafx.scene.input.*;
5859
import javafx.util.Callback;
@@ -321,9 +322,13 @@ public final void setHideOnClick(boolean value) {
321322
if (listViewSelectionDirty) {
322323
try {
323324
listSelectionLock = true;
324-
T item = comboBox.getSelectionModel().getSelectedItem();
325-
listView.getSelectionModel().clearSelection();
326-
listView.getSelectionModel().select(item);
325+
SingleSelectionModel<T> selectionModel = comboBox.getSelectionModel();
326+
327+
if (selectionModel != null) {
328+
T item = selectionModel.getSelectedItem();
329+
listView.getSelectionModel().clearSelection();
330+
listView.getSelectionModel().select(item);
331+
}
327332
} finally {
328333
listSelectionLock = false;
329334
listViewSelectionDirty = false;
@@ -396,6 +401,11 @@ private void updateListViewItems() {
396401
}
397402

398403
private void updateValue() {
404+
SingleSelectionModel<T> comboBoxSM = comboBox.getSelectionModel();
405+
if (comboBoxSM == null) {
406+
return;
407+
}
408+
399409
T newValue = comboBox.getValue();
400410

401411
SelectionModel<T> listViewSM = listView.getSelectionModel();
@@ -413,7 +423,7 @@ private void updateValue() {
413423
listViewSM.clearSelection();
414424
listSelectionLock = false;
415425
} else {
416-
int index = comboBox.getSelectionModel().getSelectedIndex();
426+
int index = comboBoxSM.getSelectedIndex();
417427
if (index >= 0 && index < comboBoxItems.size()) {
418428
T itemsObj = comboBoxItems.get(index);
419429
if ((itemsObj != null && itemsObj.equals(newValue)) || (itemsObj == null && newValue == null)) {
@@ -554,15 +564,21 @@ private ListView<T> createListView() {
554564

555565
_listView.getSelectionModel().selectedIndexProperty().addListener(o -> {
556566
if (listSelectionLock) return;
567+
SingleSelectionModel<T> selectionModel = comboBox.getSelectionModel();
568+
if (selectionModel == null) return;
569+
557570
int index = listView.getSelectionModel().getSelectedIndex();
558-
comboBox.getSelectionModel().select(index);
571+
selectionModel.select(index);
559572
updateDisplayNode();
560573
comboBox.notifyAccessibleAttributeChanged(AccessibleAttribute.TEXT);
561574
});
562575

563-
comboBox.getSelectionModel().selectedItemProperty().addListener(o -> {
564-
listViewSelectionDirty = true;
565-
});
576+
SingleSelectionModel<T> selectionModel = comboBox.getSelectionModel();
577+
if (selectionModel != null) {
578+
selectionModel.selectedItemProperty().addListener(o -> {
579+
listViewSelectionDirty = true;
580+
});
581+
}
566582

567583
_listView.addEventFilter(MouseEvent.MOUSE_RELEASED, t -> {
568584
// RT-18672: Without checking if the user is clicking in the

modules/javafx.controls/src/test/java/test/javafx/scene/control/ChoiceBoxTest.java

+27-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
2626
package test.javafx.scene.control;
2727

2828
import javafx.scene.control.Separator;
29+
import org.junit.After;
2930
import test.com.sun.javafx.pgstub.StubToolkit;
3031
import com.sun.javafx.tk.Toolkit;
3132

@@ -56,8 +57,6 @@
5657
import javafx.scene.control.SingleSelectionModel;
5758
import javafx.scene.layout.StackPane;
5859
import javafx.stage.Stage;
59-
import javafx.event.Event;
60-
import javafx.event.EventHandler;
6160

6261
import org.junit.Before;
6362
import org.junit.Ignore;
@@ -70,10 +69,22 @@ public class ChoiceBoxTest {
7069
private Stage stage;
7170

7271
@Before public void setup() {
72+
Thread.currentThread().setUncaughtExceptionHandler((thread, throwable) -> {
73+
if (throwable instanceof RuntimeException) {
74+
throw (RuntimeException) throwable;
75+
} else {
76+
Thread.currentThread().getThreadGroup().uncaughtException(thread, throwable);
77+
}
78+
});
79+
7380
//This step is not needed (Just to make sure StubToolkit is loaded into VM)
7481
tk = (StubToolkit)Toolkit.getToolkit();
7582
}
7683

84+
@After public void cleanUp() {
85+
Thread.currentThread().setUncaughtExceptionHandler(null);
86+
}
87+
7788
protected void startApp(Parent root) {
7889
scene = new Scene(root,800,600);
7990
stage = new Stage();
@@ -154,6 +165,19 @@ protected void startApp(Parent root) {
154165
assertNull(box.getSelectionModel());
155166
}
156167

168+
@Test public void testNullSelectionModelDoesNotThrowNPEOnValueChange() {
169+
ObservableList<String> items = FXCollections.observableArrayList("ITEM1", "ITEM2");
170+
171+
box.setSkin(new ChoiceBoxSkin<>(box));
172+
box.setItems(items);
173+
box.setSelectionModel(null);
174+
175+
box.setValue(items.get(1));
176+
177+
String text = ChoiceBoxSkinNodesShim.getChoiceBoxSelectedText((ChoiceBoxSkin<?>) box.getSkin());
178+
assertEquals(items.get(1), text);
179+
}
180+
157181
@Test public void selectionModelCanBeBound() {
158182
SingleSelectionModel<String> sm = ChoiceBoxShim.<String>get_ChoiceBoxSelectionModel(box);
159183
ObjectProperty<SingleSelectionModel<String>> other = new SimpleObjectProperty<SingleSelectionModel<String>>(sm);

modules/javafx.controls/src/test/java/test/javafx/scene/control/ComboBoxTest.java

+77
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,83 @@ public Node getDisplayNode() {
285285
assertNull(comboBox.getSelectionModel());
286286
}
287287

288+
@Test public void testNullSelectionModelDoesNotThrowNPEInSkinOnValueChange() {
289+
ObservableList<String> items = FXCollections.observableArrayList("ITEM1", "ITEM2");
290+
291+
ListCell<String> buttonCell = new ListCell<>() {
292+
@Override
293+
protected void updateItem(String item, boolean empty) {
294+
super.updateItem(item, empty);
295+
setText(item);
296+
}
297+
};
298+
comboBox.setButtonCell(buttonCell);
299+
comboBox.setItems(items);
300+
comboBox.setSelectionModel(null);
301+
302+
// Should not throw an NPE.
303+
comboBox.setValue(items.get(1));
304+
305+
assertEquals(items.get(1), comboBox.getValue());
306+
assertEquals(items.get(1), comboBox.getButtonCell().getText());
307+
}
308+
309+
@Test public void testNullSelectionModelDoesNotThrowNPEInSkinOnLayout() {
310+
ObservableList<String> items = FXCollections.observableArrayList("ITEM1", "ITEM2");
311+
312+
comboBox.setItems(items);
313+
314+
comboBox.setValue(items.get(1));
315+
comboBox.setSelectionModel(null);
316+
317+
// Should not throw an NPE.
318+
comboBox.layout();
319+
}
320+
321+
@Test public void testNullSelectionModelDoesNotThrowNPEOnEditableChange() {
322+
ObservableList<String> items = FXCollections.observableArrayList("ITEM1", "ITEM2");
323+
324+
comboBox.setEditable(true);
325+
comboBox.setItems(items);
326+
comboBox.setSelectionModel(null);
327+
328+
// Should not throw an NPE.
329+
comboBox.setEditable(false);
330+
}
331+
332+
@Test public void testNullSelectionModelDoesNotThrowNPEOnValueChange() {
333+
ObservableList<String> items = FXCollections.observableArrayList("ITEM1", "ITEM2");
334+
335+
ComboBox<String> comboBox = new ComboBox<>();
336+
comboBox.setItems(items);
337+
comboBox.setSelectionModel(null);
338+
339+
// Should not throw an NPE.
340+
comboBox.setValue(items.get(1));
341+
342+
assertEquals(items.get(1), comboBox.getValue());
343+
}
344+
345+
@Test public void testNullSelectionModelDoesNotThrowNPEOnListViewSelect() {
346+
ObservableList<String> items = FXCollections.observableArrayList("ITEM1", "ITEM2");
347+
348+
comboBox.setItems(items);
349+
comboBox.setSelectionModel(null);
350+
ListView<String> listView = (ListView<String>) ((ComboBoxListViewSkin<String>) comboBox.getSkin())
351+
.getPopupContent();
352+
353+
// Should not throw an NPE.
354+
listView.getSelectionModel().select(1);
355+
}
356+
357+
@Test public void testNullSelectionModelDoesNotThrowNPEOnSkinCreation() {
358+
ComboBox<String> comboBox = new ComboBox<>();
359+
comboBox.setSelectionModel(null);
360+
361+
// Should not throw an NPE.
362+
comboBox.setSkin(new ComboBoxListViewSkin<>(comboBox));
363+
}
364+
288365
@Test public void selectionModelCanBeBound() {
289366
SingleSelectionModel<String> sm = ComboBoxShim.<String>get_ComboBoxSelectionModel(comboBox);
290367
ObjectProperty<SingleSelectionModel<String>> other = new SimpleObjectProperty<SingleSelectionModel<String>>(sm);

0 commit comments

Comments
 (0)