1 /*
   2  * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package test.javafx.scene.control;
  27 
  28 import static test.com.sun.javafx.scene.control.infrastructure.ControlTestUtils.assertStyleClassContains;
  29 import static javafx.scene.control.TableColumn.SortType.ASCENDING;
  30 import static javafx.scene.control.TableColumn.SortType.DESCENDING;
  31 import static org.junit.Assert.*;
  32 
  33 import java.util.*;
  34 import java.util.concurrent.atomic.AtomicLong;
  35 import java.util.function.Supplier;
  36 
  37 import com.sun.javafx.scene.control.ReadOnlyUnbackedObservableList;
  38 import com.sun.javafx.scene.control.SelectedCellsMap;
  39 import com.sun.javafx.scene.control.TableColumnBaseHelper;
  40 import com.sun.javafx.scene.control.behavior.TableCellBehavior;
  41 import javafx.beans.InvalidationListener;
  42 import test.com.sun.javafx.scene.control.infrastructure.ControlTestUtils;
  43 import test.com.sun.javafx.scene.control.infrastructure.KeyEventFirer;
  44 import test.com.sun.javafx.scene.control.infrastructure.KeyModifier;
  45 import test.com.sun.javafx.scene.control.infrastructure.MouseEventFirer;
  46 import test.com.sun.javafx.scene.control.infrastructure.StageLoader;
  47 import javafx.application.Platform;
  48 import javafx.beans.binding.Bindings;
  49 import javafx.beans.binding.ObjectBinding;
  50 import javafx.beans.property.*;
  51 import javafx.collections.FXCollections;
  52 import javafx.collections.ListChangeListener;
  53 import javafx.collections.ObservableList;
  54 import javafx.collections.transformation.SortedList;
  55 import javafx.event.EventHandler;
  56 import javafx.geometry.Orientation;
  57 import javafx.scene.Group;
  58 import javafx.scene.Scene;
  59 import javafx.scene.control.cell.*;
  60 import javafx.scene.control.skin.TableCellSkin;
  61 import javafx.scene.control.skin.TableColumnHeader;
  62 import javafx.scene.control.skin.TableColumnHeaderShim;
  63 import javafx.scene.control.skin.TableHeaderRow;
  64 import javafx.scene.control.skin.VirtualFlow;
  65 import com.sun.javafx.scene.control.VirtualScrollBar;
  66 import javafx.scene.image.ImageView;
  67 import javafx.scene.input.KeyCode;
  68 import javafx.scene.layout.StackPane;
  69 import javafx.scene.layout.VBox;
  70 import javafx.scene.paint.Color;
  71 import javafx.scene.shape.Rectangle;
  72 import javafx.util.Callback;
  73 
  74 import com.sun.javafx.tk.Toolkit;
  75 import org.junit.Before;
  76 import org.junit.Ignore;
  77 import org.junit.Test;
  78 
  79 import com.sun.javafx.scene.control.TableColumnComparatorBase.TableColumnComparator;
  80 import javafx.scene.control.Button;
  81 import javafx.scene.control.Cell;
  82 import javafx.scene.control.CheckBox;
  83 import javafx.scene.control.ControlShim;
  84 import javafx.scene.control.FocusModel;
  85 import javafx.scene.control.IndexedCell;
  86 import javafx.scene.control.MultipleSelectionModel;
  87 import javafx.scene.control.MultipleSelectionModelBaseShim;
  88 import javafx.scene.control.ScrollBar;
  89 import javafx.scene.control.SelectionMode;
  90 import javafx.scene.control.SplitPane;
  91 import javafx.scene.control.TableCell;
  92 import javafx.scene.control.TableCellShim;
  93 import javafx.scene.control.TableColumn;
  94 import javafx.scene.control.TablePosition;
  95 import javafx.scene.control.TableRow;
  96 import javafx.scene.control.TableRowShim;
  97 import javafx.scene.control.TableSelectionModel;
  98 import javafx.scene.control.TableView;
  99 import javafx.scene.control.TableViewShim;
 100 import javafx.scene.control.TextField;
 101 import javafx.scene.control.TreeTableColumn;
 102 import javafx.scene.control.TreeTableView;
 103 import test.com.sun.javafx.scene.control.infrastructure.VirtualFlowTestUtils;
 104 import test.com.sun.javafx.scene.control.test.Person;
 105 import test.com.sun.javafx.scene.control.test.RT_22463_Person;
 106 
 107 import javafx.scene.control.skin.TableHeaderRowShim;
 108 import static org.junit.Assert.assertEquals;
 109 import test.com.sun.javafx.scene.control.infrastructure.TableColumnHeaderUtil;
 110 
 111 public class TableViewTest {
 112     private TableView<String> table;
 113     private TableView.TableViewSelectionModel sm;
 114     private TableView.TableViewFocusModel<String> fm;
 115 
 116     private ObservableList<Person> personTestData;
 117 
 118     @Before public void setup() {
 119         table = new TableView<>();
 120         sm = table.getSelectionModel();
 121         fm = table.getFocusModel();
 122 
 123         personTestData = FXCollections.observableArrayList(
 124                 new Person("Jacob", "Smith", "jacob.smith@example.com"),
 125                 new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
 126                 new Person("Ethan", "Williams", "ethan.williams@example.com"),
 127                 new Person("Emma", "Jones", "emma.jones@example.com"),
 128                 new Person("Michael", "Brown", "michael.brown@example.com"));
 129     }
 130 
 131 
 132     /*********************************************************************
 133      * Tests for the constructors                                        *
 134      ********************************************************************/
 135 
 136     @Test public void noArgConstructorSetsTheStyleClass() {
 137         assertStyleClassContains(table, "table-view");
 138     }
 139 
 140     @Test public void noArgConstructorSetsNonNullSelectionModel() {
 141         assertNotNull(sm);
 142     }
 143 
 144     @Test public void noArgConstructorSetsNonNullItems() {
 145         assertNotNull(table.getItems());
 146     }
 147 
 148     @Test public void noArgConstructorSetsNonNullSortPolicy() {
 149         assertNotNull(table.getSortPolicy());
 150     }
 151 
 152     @Test public void noArgConstructorSetsNullComparator() {
 153         assertNull(table.getComparator());
 154     }
 155 
 156     @Test public void noArgConstructorSetsNullOnSort() {
 157         assertNull(table.getOnSort());
 158     }
 159 
 160     @Test public void noArgConstructor_selectedItemIsNull() {
 161         assertNull(sm.getSelectedItem());
 162     }
 163 
 164     @Test public void noArgConstructor_selectedIndexIsNegativeOne() {
 165         assertEquals(-1, sm.getSelectedIndex());
 166     }
 167 
 168     @Test public void singleArgConstructorSetsTheStyleClass() {
 169         final TableView<String> b2 = new TableView<>(FXCollections.observableArrayList("Hi"));
 170         assertStyleClassContains(b2, "table-view");
 171     }
 172 
 173     @Test public void singleArgConstructorSetsNonNullSelectionModel() {
 174         final TableView<String> b2 = new TableView<>(FXCollections.observableArrayList("Hi"));
 175         assertNotNull(b2.getSelectionModel());
 176     }
 177 
 178     @Test public void singleArgConstructorAllowsNullItems() {
 179         final TableView<String> b2 = new TableView<>(null);
 180         assertNull(b2.getItems());
 181     }
 182 
 183     @Test public void singleArgConstructorTakesItems() {
 184         ObservableList<String> items = FXCollections.observableArrayList("Hi");
 185         final TableView<String> b2 = new TableView<>(items);
 186         assertSame(items, b2.getItems());
 187     }
 188 
 189     @Test public void singleArgConstructor_selectedItemIsNull() {
 190         final TableView<String> b2 = new TableView<>(FXCollections.observableArrayList("Hi"));
 191         assertNull(b2.getSelectionModel().getSelectedItem());
 192     }
 193 
 194     @Test public void singleArgConstructor_selectedIndexIsNegativeOne() {
 195         final TableView<String> b2 = new TableView<>(FXCollections.observableArrayList("Hi"));
 196         assertEquals(-1, b2.getSelectionModel().getSelectedIndex());
 197     }
 198 
 199     /*********************************************************************
 200      * Tests for selection model                                         *
 201      ********************************************************************/
 202 
 203     @Test public void selectionModelCanBeNull() {
 204         table.setSelectionModel(null);
 205         assertNull(table.getSelectionModel());
 206     }
 207 
 208     @Test public void selectionModelCanBeBound() {
 209         TableView.TableViewSelectionModel<String> sm = TableViewShim.<String>get_TableViewArrayListSelectionModel(table);
 210         ObjectProperty<TableView.TableViewSelectionModel<String>> other = new SimpleObjectProperty<TableView.TableViewSelectionModel<String>>(sm);
 211         table.selectionModelProperty().bind(other);
 212         assertSame(sm, sm);
 213     }
 214 
 215     @Test public void selectionModelCanBeChanged() {
 216         TableView.TableViewSelectionModel<String> sm = TableViewShim.<String>get_TableViewArrayListSelectionModel(table);
 217         table.setSelectionModel(sm);
 218         assertSame(sm, sm);
 219     }
 220 
 221     @Test public void canSetSelectedItemToAnItemEvenWhenThereAreNoItems() {
 222         final String randomString = new String("I AM A CRAZY RANDOM STRING");
 223         sm.select(randomString);
 224         assertEquals(-1, sm.getSelectedIndex());
 225         assertSame(randomString, sm.getSelectedItem());
 226     }
 227 
 228     @Test public void canSetSelectedItemToAnItemNotInTheDataModel() {
 229         table.getItems().addAll("Apple", "Orange", "Banana");
 230         final String randomString = new String("I AM A CRAZY RANDOM STRING");
 231         sm.select(randomString);
 232         assertSame(randomString, sm.getSelectedItem());
 233         assertEquals(-1, sm.getSelectedIndex());
 234     }
 235 
 236     @Test public void settingTheSelectedItemToAnItemInItemsResultsInTheCorrectSelectedIndex() {
 237         table.getItems().addAll("Apple", "Orange", "Banana");
 238         sm.select("Orange");
 239         assertEquals(1, sm.getSelectedIndex());
 240         assertSame("Orange", sm.getSelectedItem());
 241     }
 242 
 243     @Test public void settingTheSelectedItemToANonexistantItemAndThenSettingItemsWhichContainsItResultsInCorrectSelectedIndex() {
 244         sm.select("Orange");
 245         table.getItems().addAll("Apple", "Orange", "Banana");
 246         assertEquals(1, sm.getSelectedIndex());
 247         assertSame("Orange", sm.getSelectedItem());
 248     }
 249 
 250     @Test public void ensureSelectionClearsWhenAllItemsAreRemoved_selectIndex0() {
 251         table.getItems().addAll("Apple", "Orange", "Banana");
 252         sm.select(0);
 253         table.getItems().clear();
 254         assertEquals(-1, sm.getSelectedIndex());
 255     }
 256 
 257     @Test public void ensureSelectionClearsWhenAllItemsAreRemoved_selectIndex2() {
 258         table.getItems().addAll("Apple", "Orange", "Banana");
 259         sm.select(2);
 260         table.getItems().clear();
 261         assertEquals(-1, sm.getSelectedIndex());
 262     }
 263 
 264     @Test public void ensureSelectedItemRemainsAccurateWhenItemsAreCleared() {
 265         table.getItems().addAll("Apple", "Orange", "Banana");
 266         sm.select(2);
 267         table.getItems().clear();
 268         assertNull("Selected Item: " + sm.getSelectedItem(), sm.getSelectedItem());
 269         assertEquals(-1, sm.getSelectedIndex());
 270 
 271         table.getItems().addAll("Kiwifruit", "Mandarin", "Pineapple");
 272         sm.select(2);
 273         assertEquals("Pineapple", sm.getSelectedItem());
 274     }
 275 
 276     @Ignore("Not fixed yet")
 277     @Test public void ensureSelectionShiftsDownWhenOneNewItemIsAdded() {
 278         table.getItems().addAll("Apple", "Orange", "Banana");
 279         sm.select(1);
 280         assertEquals(1, sm.getSelectedIndex());
 281         assertEquals("Orange", sm.getSelectedItem());
 282 
 283         table.getItems().add(0, "Kiwifruit");
 284         assertEquals(2, sm.getSelectedIndex());
 285         assertEquals("Orange", sm.getSelectedItem());
 286     }
 287 
 288     @Ignore("Not fixed yet")
 289     @Test public void ensureSelectionShiftsDownWhenMultipleNewItemAreAdded() {
 290         table.getItems().addAll("Apple", "Orange", "Banana");
 291         sm.select(1);
 292         assertEquals(1, sm.getSelectedIndex());
 293         assertEquals("Orange", sm.getSelectedItem());
 294 
 295         table.getItems().addAll(0, Arrays.asList("Kiwifruit", "Pineapple", "Mandarin"));
 296         assertEquals("Orange", sm.getSelectedItem());
 297         assertEquals(4, sm.getSelectedIndex());
 298     }
 299 
 300     @Ignore("Not fixed yet")
 301     @Test public void ensureSelectionShiftsDownWhenOneItemIsRemoved() {
 302         table.getItems().addAll("Apple", "Orange", "Banana");
 303         sm.select(1);
 304         assertEquals(1, sm.getSelectedIndex());
 305         assertEquals("Orange", sm.getSelectedItem());
 306 
 307         table.getItems().remove("Apple");
 308         assertEquals(0, sm.getSelectedIndex());
 309         assertEquals("Orange", sm.getSelectedItem());
 310     }
 311 
 312     @Ignore("Not fixed yet")
 313     @Test public void ensureSelectionShiftsDownWheMultipleItemsAreRemoved() {
 314         table.getItems().addAll("Apple", "Orange", "Banana");
 315         sm.select(2);
 316         assertEquals(2, sm.getSelectedIndex());
 317         assertEquals("Banana", sm.getSelectedItem());
 318 
 319         table.getItems().removeAll(Arrays.asList("Apple", "Orange"));
 320         assertEquals(0, sm.getSelectedIndex());
 321         assertEquals("Banana", sm.getSelectedItem());
 322     }
 323 
 324     @Test public void ensureSelectionIsCorrectWhenItemsChange() {
 325         table.setItems(FXCollections.observableArrayList("Item 1"));
 326         sm.select(0);
 327         assertEquals("Item 1", sm.getSelectedItem());
 328 
 329         table.setItems(FXCollections.observableArrayList("Item 2"));
 330         assertEquals(-1, sm.getSelectedIndex());
 331         assertNull(sm.getSelectedItem());
 332         assertEquals(0, fm.getFocusedIndex());
 333         assertEquals("Item 2", fm.getFocusedItem());
 334     }
 335 
 336     /*********************************************************************
 337      * Tests for columns                                                 *
 338      ********************************************************************/
 339 
 340     @Test public void testColumns() {
 341         TableColumn col1 = new TableColumn();
 342 
 343         assertNotNull(table.getColumns());
 344         assertEquals(0, table.getColumns().size());
 345 
 346         table.getColumns().add(col1);
 347         assertEquals(1, table.getColumns().size());
 348 
 349         table.getColumns().remove(col1);
 350         assertEquals(0, table.getColumns().size());
 351     }
 352 
 353     @Test public void testVisibleLeafColumns() {
 354         TableColumn col1 = new TableColumn();
 355 
 356         assertNotNull(table.getColumns());
 357         assertEquals(0, table.getColumns().size());
 358 
 359         table.getColumns().add(col1);
 360         assertEquals(1, table.getVisibleLeafColumns().size());
 361 
 362         table.getColumns().remove(col1);
 363         assertEquals(0, table.getVisibleLeafColumns().size());
 364     }
 365 
 366     @Test public void testSortOrderCleanup() {
 367 //        ObservableList<ObservablePerson> persons = ObservablePerson.createFXPersonList();
 368         TableView table = new TableView();
 369         TableColumn<String,String> first = new TableColumn<String,String>("first");
 370         first.setCellValueFactory(new PropertyValueFactory("firstName"));
 371         TableColumn<String,String> second = new TableColumn<String,String>("second");
 372         second.setCellValueFactory(new PropertyValueFactory("lastName"));
 373         table.getColumns().addAll(first, second);
 374         table.getSortOrder().setAll(first, second);
 375         table.getColumns().remove(first);
 376         assertFalse(table.getSortOrder().contains(first));
 377     }
 378 
 379 
 380     /*********************************************************************
 381      * Tests for new sorting API in JavaFX 8.0                           *
 382      ********************************************************************/
 383 
 384     // TODO test for sort policies returning null
 385     // TODO test for changing column sortType out of order
 386     // TODO test comparator returns to original when sort fails / is consumed
 387 
 388     private static final Callback<TableView<String>, Boolean> NO_SORT_FAILED_SORT_POLICY =
 389             tableView -> false;
 390 
 391     private static final Callback<TableView<String>, Boolean> SORT_SUCCESS_ASCENDING_SORT_POLICY =
 392             tableView -> {
 393                 if (tableView.getSortOrder().isEmpty()) return true;
 394                 FXCollections.sort(tableView.getItems(), new Comparator<String>() {
 395                     @Override public int compare(String o1, String o2) {
 396                         return o1.compareTo(o2);
 397                     }
 398                 });
 399                 return true;
 400             };
 401 
 402     private TableColumn<String, String> initSortTestStructure() {
 403         TableColumn<String, String> col = new TableColumn<String, String>("column");
 404         col.setSortType(ASCENDING);
 405         col.setCellValueFactory(param -> new ReadOnlyObjectWrapper<String>(param.getValue()));
 406         table.getColumns().add(col);
 407         table.getItems().addAll("Apple", "Orange", "Banana");
 408         return col;
 409     }
 410 
 411     @Ignore("This test is only valid if sort event consumption should revert changes")
 412     @Test public void testSortEventCanBeConsumedToStopSortOccurring_changeSortOrderList() {
 413         TableColumn<String, String> col = initSortTestStructure();
 414         table.setOnSort(event -> {
 415             event.consume();
 416         });
 417 
 418         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
 419         table.getSortOrder().add(col);
 420         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
 421 
 422         // the sort order list should be returned back to its original state
 423         assertTrue(table.getSortOrder().isEmpty());
 424     }
 425 
 426     @Test public void testSortEventCanBeNotConsumedToAllowSortToOccur_changeSortOrderList() {
 427         TableColumn<String, String> col = initSortTestStructure();
 428         table.setOnSort(event -> {
 429             // do not consume here - this allows the sort to happen
 430         });
 431 
 432         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
 433         table.getSortOrder().add(col);
 434         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Banana", "Orange");
 435 
 436         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getSortOrder(), col);
 437     }
 438 
 439     @Ignore("This test is only valid if sort event consumption should revert changes")
 440     @Test public void testSortEventCanBeConsumedToStopSortOccurring_changeColumnSortType_AscendingToDescending() {
 441         TableColumn<String, String> col = initSortTestStructure();
 442         assertEquals(ASCENDING, col.getSortType());
 443         table.getSortOrder().add(col);
 444         table.setOnSort(event -> {
 445             event.consume();
 446         });
 447 
 448         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Banana", "Orange");
 449 
 450         // when we change from ASCENDING to DESCENDING we don't expect the sort
 451         // to actually change (and in fact we expect the sort type to resort
 452         // back to being ASCENDING)
 453         col.setSortType(DESCENDING);
 454         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Banana", "Orange");
 455         assertEquals(ASCENDING, col.getSortType());
 456 
 457         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getSortOrder(), col);
 458     }
 459 
 460     @Test public void testSortEventCanBeNotConsumedToAllowSortToOccur_changeColumnSortType_AscendingToDescending() {
 461         TableColumn<String, String> col = initSortTestStructure();
 462         assertEquals(ASCENDING, col.getSortType());
 463         table.getSortOrder().add(col);
 464         table.setOnSort(event -> {
 465             // do not consume here - this allows the sort to happen
 466         });
 467 
 468         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Banana", "Orange");
 469 
 470         col.setSortType(DESCENDING);
 471         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
 472         assertEquals(DESCENDING, col.getSortType());
 473 
 474         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getSortOrder(), col);
 475     }
 476 
 477     @Ignore("This test is only valid if sort event consumption should revert changes")
 478     @Test public void testSortEventCanBeConsumedToStopSortOccurring_changeColumnSortType_DescendingToNull() {
 479         TableColumn<String, String> col = initSortTestStructure();
 480         col.setSortType(DESCENDING);
 481         assertEquals(DESCENDING, col.getSortType());
 482         table.getSortOrder().add(col);
 483         table.setOnSort(event -> {
 484             event.consume();
 485         });
 486 
 487         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
 488 
 489         col.setSortType(null);
 490         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
 491         assertEquals(DESCENDING, col.getSortType());
 492 
 493         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getSortOrder(), col);
 494     }
 495 
 496     @Test public void testSortEventCanBeNotConsumedToAllowSortToOccur_changeColumnSortType_DescendingToNull() {
 497         TableColumn<String, String> col = initSortTestStructure();
 498         col.setSortType(DESCENDING);
 499         assertEquals(DESCENDING, col.getSortType());
 500         table.getSortOrder().add(col);
 501         table.setOnSort(event -> {
 502             // do not consume here - this allows the sort to happen
 503         });
 504 
 505         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
 506 
 507         col.setSortType(null);
 508         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
 509         assertNull(col.getSortType());
 510 
 511         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getSortOrder(), col);
 512     }
 513 
 514     @Ignore("This test is only valid if sort event consumption should revert changes")
 515     @Test public void testSortEventCanBeConsumedToStopSortOccurring_changeColumnSortType_NullToAscending() {
 516         TableColumn<String, String> col = initSortTestStructure();
 517         col.setSortType(null);
 518         assertNull(col.getSortType());
 519         table.getSortOrder().add(col);
 520         table.setOnSort(event -> {
 521             event.consume();
 522         });
 523 
 524         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
 525 
 526         col.setSortType(ASCENDING);
 527         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
 528         assertNull(col.getSortType());
 529 
 530         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getSortOrder(), col);
 531     }
 532 
 533     @Test public void testSortEventCanBeNotConsumedToAllowSortToOccur_changeColumnSortType_NullToAscending() {
 534         TableColumn<String, String> col = initSortTestStructure();
 535         col.setSortType(null);
 536         assertNull(col.getSortType());
 537         table.getSortOrder().add(col);
 538         table.setOnSort(event -> {
 539             // do not consume here - this allows the sort to happen
 540         });
 541 
 542         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
 543 
 544         col.setSortType(ASCENDING);
 545         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Banana", "Orange");
 546         assertEquals(ASCENDING, col.getSortType());
 547 
 548         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getSortOrder(), col);
 549     }
 550 
 551     @Test public void testSortMethodWithNullSortPolicy() {
 552         TableColumn<String, String> col = initSortTestStructure();
 553         table.setSortPolicy(null);
 554         assertNull(table.getSortPolicy());
 555         table.sort();
 556     }
 557 
 558     @Test public void testChangingSortPolicyUpdatesItemsList() {
 559         TableColumn<String, String> col = initSortTestStructure();
 560         col.setSortType(DESCENDING);
 561         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
 562         table.getSortOrder().add(col);
 563         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
 564         table.setSortPolicy(SORT_SUCCESS_ASCENDING_SORT_POLICY);
 565         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Banana", "Orange");
 566     }
 567 
 568     @Test public void testChangingSortPolicyDoesNotUpdateItemsListWhenTheSortOrderListIsEmpty() {
 569         TableColumn<String, String> col = initSortTestStructure();
 570         col.setSortType(DESCENDING);
 571         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
 572 
 573         table.setSortPolicy(SORT_SUCCESS_ASCENDING_SORT_POLICY);
 574         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
 575     }
 576 
 577     @Test public void testFailedSortPolicyBacksOutLastChange_sortOrderAddition() {
 578         TableColumn<String, String> col = initSortTestStructure();
 579         col.setSortType(DESCENDING);
 580         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
 581         table.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
 582 
 583         table.getSortOrder().add(col);
 584 
 585         // no sort should be run (as we have a custom sort policy), and the
 586         // sortOrder list should be empty as the sortPolicy failed
 587         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
 588         assertTrue(table.getSortOrder().isEmpty());
 589     }
 590 
 591     @Test public void testFailedSortPolicyBacksOutLastChange_sortOrderRemoval() {
 592         TableColumn<String, String> col = initSortTestStructure();
 593         col.setSortType(DESCENDING);
 594         table.getSortOrder().add(col);
 595         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
 596 
 597         table.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
 598 
 599         // even though we remove the column from the sort order here, because the
 600         // sort policy fails the items list should remain unchanged and the sort
 601         // order list should continue to have the column in it.
 602         table.getSortOrder().remove(col);
 603         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
 604         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getSortOrder(), col);
 605     }
 606 
 607     @Test public void testFailedSortPolicyBacksOutLastChange_sortTypeChange_ascendingToDescending() {
 608         TableColumn<String, String> col = initSortTestStructure();
 609         col.setSortType(ASCENDING);
 610         table.getSortOrder().add(col);
 611         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Banana", "Orange");
 612 
 613         table.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
 614 
 615         col.setSortType(DESCENDING);
 616         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Banana", "Orange");
 617         assertEquals(ASCENDING, col.getSortType());
 618     }
 619 
 620     @Test public void testFailedSortPolicyBacksOutLastChange_sortTypeChange_descendingToNull() {
 621         TableColumn<String, String> col = initSortTestStructure();
 622         col.setSortType(DESCENDING);
 623         table.getSortOrder().add(col);
 624         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
 625 
 626         table.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
 627 
 628         col.setSortType(null);
 629         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
 630         assertEquals(DESCENDING, col.getSortType());
 631     }
 632 
 633     @Test public void testFailedSortPolicyBacksOutLastChange_sortTypeChange_nullToAscending() {
 634         TableColumn<String, String> col = initSortTestStructure();
 635         col.setSortType(null);
 636         table.getSortOrder().add(col);
 637         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
 638 
 639         table.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
 640 
 641         col.setSortType(ASCENDING);
 642         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
 643         assertNull(col.getSortType());
 644     }
 645 
 646     @Test public void testComparatorChangesInSyncWithSortOrder_1() {
 647         TableColumn<String, String> col = initSortTestStructure();
 648         assertNull(table.getComparator());
 649         assertTrue(table.getSortOrder().isEmpty());
 650 
 651         table.getSortOrder().add(col);
 652         TableColumnComparator c = (TableColumnComparator)table.getComparator();
 653         assertNotNull(c);
 654         VirtualFlowTestUtils.assertListContainsItemsInOrder(c.getColumns(), col);
 655     }
 656 
 657     @Ignore
 658     @Test public void testComparatorChangesInSyncWithSortOrder_2() {
 659         // same as test above
 660         TableColumn<String, String> col = initSortTestStructure();
 661         assertNull(table.getComparator());
 662         assertTrue(table.getSortOrder().isEmpty());
 663 
 664         table.getSortOrder().add(col);
 665         TableColumnComparator c = (TableColumnComparator)table.getComparator();
 666         assertNotNull(c);
 667         VirtualFlowTestUtils.assertListContainsItemsInOrder(c.getColumns(), col);
 668 
 669         // now remove column from sort order, and the comparator should go to
 670         // being null
 671         table.getSortOrder().remove(col);
 672         assertNull(table.getComparator());
 673     }
 674 
 675     @Test public void testFailedSortPolicyBacksOutComparatorChange_sortOrderAddition() {
 676         TableColumn<String, String> col = initSortTestStructure();
 677         final TableColumnComparator oldComparator = (TableColumnComparator)table.getComparator();
 678 
 679         col.setSortType(DESCENDING);
 680         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
 681         table.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
 682 
 683         table.getSortOrder().add(col);
 684 
 685         assertEquals(oldComparator, table.getComparator());
 686     }
 687 
 688     @Test public void testFailedSortPolicyBacksOutComparatorChange_sortOrderRemoval() {
 689         TableColumn<String, String> col = initSortTestStructure();
 690         TableColumnComparator oldComparator = (TableColumnComparator)table.getComparator();
 691         assertNull(oldComparator);
 692 
 693         col.setSortType(DESCENDING);
 694         table.getSortOrder().add(col);
 695         VirtualFlowTestUtils.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
 696         oldComparator = (TableColumnComparator)table.getComparator();
 697         VirtualFlowTestUtils.assertListContainsItemsInOrder(oldComparator.getColumns(), col);
 698 
 699         table.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
 700         table.getSortOrder().remove(col);
 701 
 702         assertTrue(table.getSortOrder().contains(col));
 703         VirtualFlowTestUtils.assertListContainsItemsInOrder(oldComparator.getColumns(), col);
 704     }
 705 
 706     @Test public void testFailedSortPolicyBacksOutComparatorChange_sortTypeChange() {
 707         TableColumn<String, String> col = initSortTestStructure();
 708         final TableColumnComparator oldComparator = (TableColumnComparator)table.getComparator();
 709         assertNull(oldComparator);
 710 
 711         table.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
 712         table.getSortOrder().add(col);
 713         col.setSortType(ASCENDING);
 714 
 715         assertTrue(table.getSortOrder().isEmpty());
 716         assertNull(oldComparator);
 717     }
 718 
 719     @Test public void testComparatorIsNullWhenSortOrderListIsEmpty() {
 720         TableColumn<String, String> col = initSortTestStructure();
 721 
 722         assertNull(table.getComparator());
 723 
 724         table.getSortOrder().add(col);
 725         assertFalse(table.getSortOrder().isEmpty());
 726         assertNotNull(table.getComparator());
 727 
 728         table.getSortOrder().clear();
 729         assertTrue(table.getSortOrder().isEmpty());
 730         assertNull(table.getComparator());
 731     }
 732 
 733 
 734 
 735     /*********************************************************************
 736      * Tests for specific bugs                                           *
 737      ********************************************************************/
 738     @Test public void test_rt16019() {
 739         // RT-16019: NodeMemory TableView tests fail with
 740         // IndexOutOfBoundsException (ObservableListWrapper.java:336)
 741         TableView table = new TableView();
 742         for (int i = 0; i < 1000; i++) {
 743             table.getItems().add("data " + i);
 744         }
 745     }
 746 
 747     @Test public void test_rt15793() {
 748         // ListView/TableView selectedIndex is 0 although the items list is empty
 749         final TableView tv = new TableView();
 750         final ObservableList list = FXCollections.observableArrayList();
 751         tv.setItems(list);
 752         list.add("toto");
 753         tv.getSelectionModel().select(0);
 754         assertEquals(0, tv.getSelectionModel().getSelectedIndex());
 755         list.remove(0);
 756         assertEquals(-1, tv.getSelectionModel().getSelectedIndex());
 757     }
 758 
 759     @Ignore("Started failing recently, but manual tests seem to indicate the functionality is intact")
 760     @Test public void test_rt17522_focusShouldMoveWhenItemAddedAtFocusIndex() {
 761         final TableView lv = new TableView();
 762         StageLoader sl = new StageLoader(lv);
 763         FocusModel fm = lv.getFocusModel();
 764         lv.getItems().add("row1");
 765         assertTrue(fm.isFocused(0));
 766 
 767         lv.getItems().add(0, "row0");
 768         assertTrue("Focus is on " + fm.getFocusedIndex(), fm.isFocused(1));
 769         assertFalse(fm.isFocused(0));
 770 
 771         sl.dispose();
 772     }
 773 
 774     @Test public void test_rt17522_focusShouldMoveWhenItemAddedBeforeFocusIndex() {
 775         final TableView lv = new TableView();
 776         FocusModel fm = lv.getFocusModel();
 777         lv.getItems().addAll("row1", "row2");
 778         fm.focus(1);
 779         assertTrue(fm.isFocused(1));
 780         assertEquals("row2", fm.getFocusedItem());
 781 
 782         lv.getItems().add(1, "row0");
 783         assertTrue(fm.isFocused(2));
 784         assertEquals("row2", fm.getFocusedItem());
 785         assertFalse(fm.isFocused(1));
 786     }
 787 
 788     @Test public void test_rt17522_focusShouldNotMoveWhenItemAddedAfterFocusIndex() {
 789         final TableView lv = new TableView();
 790         FocusModel fm = lv.getFocusModel();
 791         lv.getItems().addAll("row1");
 792         fm.focus(0);
 793         assertTrue(fm.isFocused(0));
 794         assertEquals("row1", fm.getFocusedItem());
 795 
 796         lv.getItems().add(1, "row2");
 797         assertTrue(fm.isFocused(0));
 798         assertEquals("row1", fm.getFocusedItem());
 799         assertFalse(fm.isFocused(1));
 800     }
 801 
 802     @Test public void test_rt17522_focusShouldBeResetWhenFocusedItemIsRemoved() {
 803         final TableView lv = new TableView();
 804         FocusModel fm = lv.getFocusModel();
 805         lv.getItems().add("row1");
 806         assertTrue(fm.isFocused(0));
 807 
 808         lv.getItems().remove("row1");
 809         assertTrue(fm.getFocusedIndex() == -1);
 810         assertNull(fm.getFocusedItem());
 811     }
 812 
 813     @Test public void test_rt17522_focusShouldMoveWhenItemRemovedBeforeFocusIndex() {
 814         final TableView lv = new TableView();
 815         FocusModel fm = lv.getFocusModel();
 816         lv.getItems().addAll("row1", "row2");
 817         assertTrue(fm.isFocused(0));
 818 
 819         fm.focus(1);
 820         assertTrue(fm.isFocused(1));
 821         assertEquals("row2", fm.getFocusedItem());
 822 
 823         lv.getItems().remove("row1");
 824         assertTrue(fm.isFocused(0));
 825         assertEquals("row2", fm.getFocusedItem());
 826     }
 827 
 828     @Test public void test_rt17522_focusShouldNotMoveWhenItemRemovedAfterFocusIndex() {
 829         final TableView lv = new TableView();
 830         FocusModel fm = lv.getFocusModel();
 831         lv.getItems().addAll("row1", "row2");
 832         fm.focus(0);
 833         assertTrue(fm.isFocused(0));
 834         assertEquals("row1", fm.getFocusedItem());
 835 
 836         lv.getItems().remove("row2");
 837         assertTrue(fm.isFocused(0));
 838         assertEquals("row1", fm.getFocusedItem());
 839     }
 840 
 841     @Test public void test_rt18385() {
 842         table.getItems().addAll("row1", "row2", "row3");
 843         sm.select(1);
 844         table.getItems().add("Another Row");
 845         assertEquals(1, sm.getSelectedIndices().size());
 846         assertEquals(1, sm.getSelectedItems().size());
 847         assertEquals(1, sm.getSelectedCells().size());
 848     }
 849 
 850     @Test public void test_rt18339_onlyEditWhenTableViewIsEditable_tableEditableIsFalse_columnEditableIsFalse() {
 851         TableColumn<String,String> first = new TableColumn<String,String>("first");
 852         first.setEditable(false);
 853         table.getColumns().add(first);
 854         table.setEditable(false);
 855         table.edit(1, first);
 856         assertEquals(null, table.getEditingCell());
 857     }
 858 
 859     @Test public void test_rt18339_onlyEditWhenTableViewIsEditable_tableEditableIsFalse_columnEditableIsTrue() {
 860         TableColumn<String,String> first = new TableColumn<String,String>("first");
 861         first.setEditable(true);
 862         table.getColumns().add(first);
 863         table.setEditable(false);
 864         table.edit(1, first);
 865         assertEquals(null, table.getEditingCell());
 866     }
 867 
 868     @Test public void test_rt18339_onlyEditWhenTableViewIsEditable_tableEditableIsTrue_columnEditableIsFalse() {
 869         TableColumn<String,String> first = new TableColumn<String,String>("first");
 870         first.setEditable(false);
 871         table.getColumns().add(first);
 872         table.setEditable(true);
 873         table.edit(1, first);
 874         assertEquals(null, table.getEditingCell());
 875     }
 876 
 877     @Test public void test_rt18339_onlyEditWhenTableViewIsEditable_tableEditableIsTrue_columnEditableIsTrue() {
 878         TableColumn<String,String> first = new TableColumn<String,String>("first");
 879         first.setEditable(true);
 880         table.getColumns().add(first);
 881         table.setEditable(true);
 882         table.edit(1, first);
 883         assertEquals(new TablePosition(table, 1, first), table.getEditingCell());
 884     }
 885 
 886     @Test public void test_rt14451() {
 887         table.getItems().addAll("Apple", "Orange", "Banana");
 888         sm.setSelectionMode(SelectionMode.MULTIPLE);
 889         sm.selectRange(0, 2); // select from 0 (inclusive) to 2 (exclusive)
 890         assertEquals(2, sm.getSelectedIndices().size());
 891     }
 892 
 893     @Test public void test_rt21586() {
 894         table.getItems().setAll("Apple", "Orange", "Banana");
 895         table.getSelectionModel().select(1);
 896         assertEquals(1, table.getSelectionModel().getSelectedIndex());
 897         assertEquals("Orange", table.getSelectionModel().getSelectedItem());
 898 
 899         table.getItems().setAll("Kiwifruit", "Pineapple", "Grape");
 900         assertEquals(-1, table.getSelectionModel().getSelectedIndex());
 901         assertNull(table.getSelectionModel().getSelectedItem());
 902         assertEquals(0, table.getFocusModel().getFocusedIndex());
 903         assertEquals("Kiwifruit", table.getFocusModel().getFocusedItem());
 904     }
 905 
 906     @Test public void test_rt27820_1() {
 907         table.getItems().setAll("Apple", "Orange");
 908         table.getSelectionModel().select(0);
 909         assertEquals(1, table.getSelectionModel().getSelectedItems().size());
 910         assertEquals("Apple", table.getSelectionModel().getSelectedItem());
 911 
 912         table.getItems().clear();
 913         assertEquals(0, table.getSelectionModel().getSelectedItems().size());
 914         assertNull(table.getSelectionModel().getSelectedItem());
 915     }
 916 
 917     @Test public void test_rt27820_2() {
 918         table.getItems().setAll("Apple", "Orange");
 919         table.getSelectionModel().select(1);
 920         assertEquals(1, table.getSelectionModel().getSelectedItems().size());
 921         assertEquals("Orange", table.getSelectionModel().getSelectedItem());
 922 
 923         table.getItems().clear();
 924         assertEquals(0, table.getSelectionModel().getSelectedItems().size());
 925         assertNull(table.getSelectionModel().getSelectedItem());
 926     }
 927 
 928     @Test public void test_rt28534() {
 929         TableView<Person> table = new TableView<Person>();
 930         table.setItems(personTestData);
 931 
 932         TableColumn firstNameCol = new TableColumn("First Name");
 933         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
 934 
 935         TableColumn lastNameCol = new TableColumn("Last Name");
 936         lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
 937 
 938         TableColumn emailCol = new TableColumn("Email");
 939         emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));
 940 
 941         table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
 942 
 943         VirtualFlowTestUtils.assertRowsNotEmpty(table, 0, 5); // rows 0 - 5 should be filled
 944         VirtualFlowTestUtils.assertRowsEmpty(table, 5, -1); // rows 5+ should be empty
 945 
 946         // now we replace the data and expect the cells that have no data
 947         // to be empty
 948         table.setItems(FXCollections.observableArrayList(
 949             new Person("*_*Emma", "Jones", "emma.jones@example.com"),
 950             new Person("_Michael", "Brown", "michael.brown@example.com")));
 951 
 952         VirtualFlowTestUtils.assertRowsNotEmpty(table, 0, 2); // rows 0 - 2 should be filled
 953         VirtualFlowTestUtils.assertRowsEmpty(table, 2, -1); // rows 2+ should be empty
 954     }
 955 
 956     @Test public void test_rt22463() {
 957         final TableView<RT_22463_Person> table = new TableView<RT_22463_Person>();
 958         table.setTableMenuButtonVisible(true);
 959         TableColumn c1 = new TableColumn("Id");
 960         TableColumn c2 = new TableColumn("Name");
 961         c1.setCellValueFactory(new PropertyValueFactory<Person, Long>("id"));
 962         c2.setCellValueFactory(new PropertyValueFactory<Person, String>("name"));
 963         table.getColumns().addAll(c1, c2);
 964 
 965         // before the change things display fine
 966         RT_22463_Person p1 = new RT_22463_Person();
 967         p1.setId(1l);
 968         p1.setName("name1");
 969         RT_22463_Person p2 = new RT_22463_Person();
 970         p2.setId(2l);
 971         p2.setName("name2");
 972         table.setItems(FXCollections.observableArrayList(p1, p2));
 973         VirtualFlowTestUtils.assertCellTextEquals(table, 0, "1", "name1");
 974         VirtualFlowTestUtils.assertCellTextEquals(table, 1, "2", "name2");
 975 
 976         // now we change the persons but they are still equal as the ID's don't
 977         // change - but the items list is cleared so the cells should update
 978         RT_22463_Person new_p1 = new RT_22463_Person();
 979         new_p1.setId(1l);
 980         new_p1.setName("updated name1");
 981         RT_22463_Person new_p2 = new RT_22463_Person();
 982         new_p2.setId(2l);
 983         new_p2.setName("updated name2");
 984         table.getItems().clear();
 985         table.setItems(FXCollections.observableArrayList(new_p1, new_p2));
 986         VirtualFlowTestUtils.assertCellTextEquals(table, 0, "1", "updated name1");
 987         VirtualFlowTestUtils.assertCellTextEquals(table, 1, "2", "updated name2");
 988     }
 989 
 990     @Test public void test_rt28637() {
 991         ObservableList<String> items = FXCollections.observableArrayList("String1", "String2", "String3", "String4");
 992 
 993         final TableView<String> tableView = new TableView<String>();
 994         final MultipleSelectionModel sm = tableView.getSelectionModel();
 995         tableView.setItems(items);
 996 
 997         tableView.getSelectionModel().select(0);
 998         assertEquals("String1", sm.getSelectedItem());
 999         assertEquals(1, sm.getSelectedItems().size());
1000         assertEquals("String1", sm.getSelectedItems().get(0));
1001         assertEquals(0, sm.getSelectedIndex());
1002 
1003         items.remove(sm.getSelectedItem());
1004         assertEquals("String2", sm.getSelectedItem());
1005         assertEquals(1, sm.getSelectedItems().size());
1006         assertEquals("String2", sm.getSelectedItems().get(0));
1007         assertEquals(0, sm.getSelectedIndex());
1008     }
1009 
1010     @Test public void test_rt24844() {
1011         // p1 == lowest first name
1012         Person p0, p1, p2, p3, p4;
1013 
1014         TableView<Person> table = new TableView<Person>();
1015         table.setItems(FXCollections.observableArrayList(
1016             p3 = new Person("Jacob", "Smith", "jacob.smith@example.com"),
1017             p2 = new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
1018             p1 = new Person("Ethan", "Williams", "ethan.williams@example.com"),
1019             p0 = new Person("Emma", "Jones", "emma.jones@example.com"),
1020             p4 = new Person("Michael", "Brown", "michael.brown@example.com")));
1021 
1022         TableColumn firstNameCol = new TableColumn("First Name");
1023         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
1024 
1025         // set dummy comparator to lock items in place until new comparator is set
1026         firstNameCol.setComparator((t, t1) -> 0);
1027 
1028         table.getColumns().addAll(firstNameCol);
1029         table.getSortOrder().add(firstNameCol);
1030 
1031         // ensure the existing order is as expected
1032         assertEquals(p3, table.getItems().get(0));
1033         assertEquals(p2, table.getItems().get(1));
1034         assertEquals(p1, table.getItems().get(2));
1035         assertEquals(p0, table.getItems().get(3));
1036         assertEquals(p4, table.getItems().get(4));
1037 
1038         // set a new comparator
1039         firstNameCol.setComparator(new Comparator() {
1040             Random r =  new Random();
1041             @Override public int compare(Object t, Object t1) {
1042                 return t.toString().compareTo(t1.toString());
1043             }
1044         });
1045 
1046         // ensure the new order is as expected
1047         assertEquals(p0, table.getItems().get(0));
1048         assertEquals(p1, table.getItems().get(1));
1049         assertEquals(p2, table.getItems().get(2));
1050         assertEquals(p3, table.getItems().get(3));
1051         assertEquals(p4, table.getItems().get(4));
1052     }
1053 
1054     @Test public void test_rt29331() {
1055         TableView<Person> table = new TableView<Person>();
1056 
1057         TableColumn firstNameCol = new TableColumn("First Name");
1058         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
1059 
1060         TableColumn lastNameCol = new TableColumn("Last Name");
1061         lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
1062 
1063         TableColumn emailCol = new TableColumn("Email");
1064         emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));
1065 
1066         TableColumn parentColumn = new TableColumn<>("Parent");
1067         parentColumn.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
1068 
1069         table.getColumns().addAll(parentColumn);
1070 
1071         // table is setup, now hide the 'last name' column
1072         emailCol.setVisible(false);
1073         assertFalse(emailCol.isVisible());
1074 
1075         // reorder columns inside the parent column
1076         parentColumn.getColumns().setAll(emailCol, firstNameCol, lastNameCol);
1077 
1078         // the email column should not become visible after this, but it does
1079         assertFalse(emailCol.isVisible());
1080     }
1081 
1082     private int rt29330_count = 0;
1083     @Test public void test_rt29330_1() {
1084         TableView<Person> table = new TableView<>(personTestData);
1085 
1086         TableColumn parentColumn = new TableColumn<>("Parent");
1087         table.getColumns().addAll(parentColumn);
1088 
1089         TableColumn firstNameCol = new TableColumn("First Name");
1090         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
1091 
1092         TableColumn lastNameCol = new TableColumn("Last Name");
1093         lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
1094 
1095         parentColumn.getColumns().addAll(firstNameCol, lastNameCol);
1096 
1097         table.setOnSort(event -> {
1098             rt29330_count++;
1099         });
1100 
1101         // test preconditions
1102         assertEquals(ASCENDING, lastNameCol.getSortType());
1103         assertEquals(0, rt29330_count);
1104 
1105         table.getSortOrder().add(lastNameCol);
1106         assertEquals(1, rt29330_count);
1107 
1108         lastNameCol.setSortType(DESCENDING);
1109         assertEquals(2, rt29330_count);
1110 
1111         lastNameCol.setSortType(null);
1112         assertEquals(3, rt29330_count);
1113 
1114         lastNameCol.setSortType(ASCENDING);
1115         assertEquals(4, rt29330_count);
1116     }
1117 
1118     @Test public void test_rt29330_2() {
1119         TableView<Person> table = new TableView<>(personTestData);
1120 
1121         TableColumn firstNameCol = new TableColumn("First Name");
1122         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
1123 
1124         TableColumn lastNameCol = new TableColumn("Last Name");
1125         lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
1126 
1127         // this test differs from the previous one by installing the parent column
1128         // into the tableview after it has the children added into it
1129         TableColumn parentColumn = new TableColumn<>("Parent");
1130         parentColumn.getColumns().addAll(firstNameCol, lastNameCol);
1131         table.getColumns().addAll(parentColumn);
1132 
1133         table.setOnSort(event -> {
1134             rt29330_count++;
1135         });
1136 
1137         // test preconditions
1138         assertEquals(ASCENDING, lastNameCol.getSortType());
1139         assertEquals(0, rt29330_count);
1140 
1141         table.getSortOrder().add(lastNameCol);
1142         assertEquals(1, rt29330_count);
1143 
1144         lastNameCol.setSortType(DESCENDING);
1145         assertEquals(2, rt29330_count);
1146 
1147         lastNameCol.setSortType(null);
1148         assertEquals(3, rt29330_count);
1149 
1150         lastNameCol.setSortType(ASCENDING);
1151         assertEquals(4, rt29330_count);
1152     }
1153 
1154     @Test public void test_rt29313_selectedIndices() {
1155         TableView<Person> table = new TableView<>(personTestData);
1156 
1157         TableSelectionModel sm = table.getSelectionModel();
1158 
1159         TableColumn firstNameCol = new TableColumn("First Name");
1160         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
1161 
1162         TableColumn lastNameCol = new TableColumn("Last Name");
1163         lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
1164 
1165         TableColumn emailCol = new TableColumn("Email");
1166         emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));
1167 
1168         table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
1169         sm.setCellSelectionEnabled(true);
1170         sm.setSelectionMode(SelectionMode.MULTIPLE);
1171 
1172         assertEquals(0, sm.getSelectedIndices().size());
1173 
1174         // only (0,0) should be selected, so selected indices should be [0]
1175         sm.select(0, firstNameCol);
1176         assertEquals(1, sm.getSelectedIndices().size());
1177 
1178         // now (0,0) and (1,0) should be selected, so selected indices should be [0, 1]
1179         sm.select(1, firstNameCol);
1180         assertEquals(2, sm.getSelectedIndices().size());
1181 
1182         // now (0,0), (1,0) and (1,1) should be selected, but selected indices
1183         // should remain as [0, 1], as we don't want selected indices to become
1184         // [0,1,1] (which is what RT-29313 is about)
1185         sm.select(1, lastNameCol);
1186         assertEquals(2, sm.getSelectedIndices().size());
1187         assertEquals(0, sm.getSelectedIndices().get(0));
1188         assertEquals(1, sm.getSelectedIndices().get(1));
1189     }
1190 
1191     @Test public void test_rt29313_selectedItems() {
1192         Person p0, p1;
1193         TableView<Person> table = new TableView<>();
1194         table.setItems(FXCollections.observableArrayList(
1195               p0 = new Person("Jacob", "Smith", "jacob.smith@example.com"),
1196               p1 = new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
1197               new Person("Ethan", "Williams", "ethan.williams@example.com"),
1198               new Person("Emma", "Jones", "emma.jones@example.com"),
1199               new Person("Michael", "Brown", "michael.brown@example.com")));
1200 
1201         TableSelectionModel sm = table.getSelectionModel();
1202 
1203         TableColumn firstNameCol = new TableColumn("First Name");
1204         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
1205 
1206         TableColumn lastNameCol = new TableColumn("Last Name");
1207         lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
1208 
1209         TableColumn emailCol = new TableColumn("Email");
1210         emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));
1211 
1212         table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
1213         sm.setCellSelectionEnabled(true);
1214         sm.setSelectionMode(SelectionMode.MULTIPLE);
1215 
1216         assertEquals(0, sm.getSelectedIndices().size());
1217 
1218         // only (0,0) should be selected, so selected items should be [p0]
1219         sm.select(0, firstNameCol);
1220         assertEquals(1, sm.getSelectedItems().size());
1221 
1222         // now (0,0) and (1,0) should be selected, so selected items should be [p0, p1]
1223         sm.select(1, firstNameCol);
1224         assertEquals(2, sm.getSelectedItems().size());
1225 
1226         // now (0,0), (1,0) and (1,1) should be selected, but selected items
1227         // should remain as [p0, p1], as we don't want selected items to become
1228         // [p0,p1,p1] (which is what RT-29313 is about)
1229         sm.select(1, lastNameCol);
1230         assertEquals(2, sm.getSelectedItems().size());
1231         assertEquals(p0, sm.getSelectedItems().get(0));
1232         assertEquals(p1, sm.getSelectedItems().get(1));
1233     }
1234 
1235     @Test public void test_rt29566() {
1236         TableView<Person> table = new TableView<>(personTestData);
1237 
1238         TableSelectionModel sm = table.getSelectionModel();
1239 
1240         TableColumn firstNameCol = new TableColumn("First Name");
1241         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
1242 
1243         TableColumn lastNameCol = new TableColumn("Last Name");
1244         lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
1245 
1246         TableColumn emailCol = new TableColumn("Email");
1247         emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));
1248 
1249         table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
1250 
1251         // test the state before we hide and re-add a column
1252         VirtualFlowTestUtils.assertCellTextEquals(table, 0, "Jacob", "Smith", "jacob.smith@example.com");
1253         VirtualFlowTestUtils.assertCellTextEquals(table, 1, "Isabella", "Johnson", "isabella.johnson@example.com");
1254         VirtualFlowTestUtils.assertCellTextEquals(table, 2, "Ethan", "Williams", "ethan.williams@example.com");
1255         VirtualFlowTestUtils.assertCellTextEquals(table, 3, "Emma", "Jones", "emma.jones@example.com");
1256         VirtualFlowTestUtils.assertCellTextEquals(table, 4, "Michael", "Brown", "michael.brown@example.com");
1257 
1258         // hide the last name column, and test cells again
1259         table.getColumns().remove(lastNameCol);
1260         VirtualFlowTestUtils.assertCellTextEquals(table, 0, "Jacob", "jacob.smith@example.com");
1261         VirtualFlowTestUtils.assertCellTextEquals(table, 1, "Isabella", "isabella.johnson@example.com");
1262         VirtualFlowTestUtils.assertCellTextEquals(table, 2, "Ethan", "ethan.williams@example.com");
1263         VirtualFlowTestUtils.assertCellTextEquals(table, 3, "Emma", "emma.jones@example.com");
1264         VirtualFlowTestUtils.assertCellTextEquals(table, 4, "Michael", "michael.brown@example.com");
1265 
1266         // re-add the last name column - we should go back to the original state.
1267         // However, what appears to be happening is that, for some reason, some
1268         // of the cells from the removed column do not reappear - meaning in this case
1269         // some of the last name values will not be where we expect them to be.
1270         // This is clearly not ideal!
1271         table.getColumns().add(1, lastNameCol);
1272         VirtualFlowTestUtils.assertCellTextEquals(table, 0, "Jacob", "Smith", "jacob.smith@example.com");
1273         VirtualFlowTestUtils.assertCellTextEquals(table, 1, "Isabella", "Johnson", "isabella.johnson@example.com");
1274         VirtualFlowTestUtils.assertCellTextEquals(table, 2, "Ethan", "Williams", "ethan.williams@example.com");
1275         VirtualFlowTestUtils.assertCellTextEquals(table, 3, "Emma", "Jones", "emma.jones@example.com");
1276         VirtualFlowTestUtils.assertCellTextEquals(table, 4, "Michael", "Brown", "michael.brown@example.com");
1277     }
1278 
1279     @Test public void test_rt29390() {
1280         final TableView<Person> tableView = new TableView<Person>();
1281         tableView.setMaxHeight(50);
1282         tableView.setPrefHeight(50);
1283         tableView.setItems(FXCollections.observableArrayList(
1284             new Person("Jacob", "Smith", "jacob.smith@example.com"),
1285             new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
1286             new Person("Ethan", "Williams", "ethan.williams@example.com"),
1287             new Person("Emma", "Jones", "emma.jones@example.com"),
1288             new Person("Jacob", "Smith", "jacob.smith@example.com"),
1289             new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
1290             new Person("Ethan", "Williams", "ethan.williams@example.com"),
1291             new Person("Emma", "Jones", "emma.jones@example.com"),
1292             new Person("Jacob", "Smith", "jacob.smith@example.com"),
1293             new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
1294             new Person("Ethan", "Williams", "ethan.williams@example.com"),
1295             new Person("Emma", "Jones", "emma.jones@example.com"),
1296             new Person("Jacob", "Smith", "jacob.smith@example.com"),
1297             new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
1298             new Person("Ethan", "Williams", "ethan.williams@example.com"),
1299             new Person("Emma", "Jones", "emma.jones@example.com")
1300         ));
1301 
1302         TableColumn firstNameCol = new TableColumn("First Name");
1303         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
1304 
1305         tableView.getColumns().add(firstNameCol);
1306 
1307         // we want the vertical scrollbar
1308         VirtualScrollBar scrollBar = VirtualFlowTestUtils.getVirtualFlowVerticalScrollbar(tableView);
1309 
1310         assertNotNull(scrollBar);
1311         assertTrue(scrollBar.isVisible());
1312         assertTrue(scrollBar.getVisibleAmount() > 0.0);
1313         assertTrue(scrollBar.getVisibleAmount() < 1.0);
1314 
1315         // this next test is likely to be brittle, but we'll see...If it is the
1316         // cause of failure then it can be commented out
1317         assertEquals(0.0625, scrollBar.getVisibleAmount(), 0.0);
1318     }
1319 
1320     @Test public void test_rt30400() {
1321         // create a listview that'll render cells using the check box cell factory
1322         final TableView<Person> tableView = new TableView<Person>();
1323         tableView.setMinHeight(100);
1324         tableView.setPrefHeight(100);
1325         tableView.setItems(FXCollections.observableArrayList(
1326                 new Person("Jacob", "Smith", "jacob.smith@example.com")
1327         ));
1328 
1329         TableColumn firstNameCol = new TableColumn("First Name");
1330         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
1331         firstNameCol.setCellFactory(CheckBoxTableCell.forTableColumn(param -> new ReadOnlyBooleanWrapper(true)));
1332         tableView.getColumns().add(firstNameCol);
1333 
1334         // because only the first row has data, all other rows should be
1335         // empty (and not contain check boxes - we just check the first four here)
1336         VirtualFlowTestUtils.assertRowsNotEmpty(tableView, 0, 1);
1337         VirtualFlowTestUtils.assertCellNotEmpty(VirtualFlowTestUtils.getCell(tableView, 0));
1338         VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(tableView, 1));
1339         VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(tableView, 2));
1340         VirtualFlowTestUtils.assertCellEmpty(VirtualFlowTestUtils.getCell(tableView, 3));
1341     }
1342 
1343     @Test public void test_rt31165() {
1344         final ObservableList names = FXCollections.observableArrayList("Adam", "Alex", "Alfred", "Albert");
1345 
1346         final TableView<Person> tableView = new TableView<Person>();
1347         tableView.setEditable(true);
1348         tableView.setItems(FXCollections.observableArrayList(
1349             new Person("Jacob", "Smith", "jacob.smith@example.com"),
1350             new Person("Jim", "Bob", "jim.bob@example.com")
1351         ));
1352 
1353         TableColumn firstNameCol = new TableColumn("First Name");
1354         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
1355         firstNameCol.setCellFactory(ChoiceBoxTableCell.forTableColumn(names));
1356         firstNameCol.setEditable(true);
1357 
1358         tableView.getColumns().add(firstNameCol);
1359 
1360         IndexedCell cell = VirtualFlowTestUtils.getCell(tableView, 1, 0);
1361         assertEquals("Jim", cell.getText());
1362         assertFalse(cell.isEditing());
1363 
1364         tableView.edit(1, firstNameCol);
1365 
1366         TablePosition editingCell = tableView.getEditingCell();
1367         assertEquals(1, editingCell.getRow());
1368         assertEquals(firstNameCol, editingCell.getTableColumn());
1369         assertTrue(cell.isEditing());
1370 
1371         VirtualFlowTestUtils.getVirtualFlow(tableView).requestLayout();
1372         Toolkit.getToolkit().firePulse();
1373 
1374         editingCell = tableView.getEditingCell();
1375         assertEquals(1, editingCell.getRow());
1376         assertEquals(firstNameCol, editingCell.getTableColumn());
1377         assertTrue(cell.isEditing());
1378     }
1379 
1380     @Test public void test_rt31471() {
1381         Person jacobSmith;
1382         final TableView<Person> tableView = new TableView<Person>();
1383         tableView.setItems(FXCollections.observableArrayList(
1384                 jacobSmith = new Person("Jacob", "Smith", "jacob.smith@example.com"),
1385                 new Person("Jim", "Bob", "jim.bob@example.com")
1386         ));
1387 
1388         TableColumn firstNameCol = new TableColumn("First Name");
1389         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
1390         tableView.getColumns().add(firstNameCol);
1391 
1392         IndexedCell cell = VirtualFlowTestUtils.getCell(tableView, 0);
1393         assertEquals(jacobSmith, cell.getItem());
1394 
1395         tableView.setFixedCellSize(50);
1396 
1397         VirtualFlowTestUtils.getVirtualFlow(tableView).requestLayout();
1398         Toolkit.getToolkit().firePulse();
1399 
1400         assertEquals(jacobSmith, cell.getItem());
1401         assertEquals(50, cell.getHeight(), 0.00);
1402     }
1403 
1404     private int rt_31200_count = 0;
1405     @Test public void test_rt_31200_tableCell() {
1406         rt_31200_count = 0;
1407         final TableView<Person> tableView = new TableView<Person>();
1408         tableView.setItems(FXCollections.observableArrayList(
1409                 new Person("Jacob", "Smith", "jacob.smith@example.com"),
1410                 new Person("Jim", "Bob", "jim.bob@example.com")
1411         ));
1412 
1413         TableColumn<Person,String> firstNameCol = new TableColumn("First Name");
1414         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
1415         tableView.getColumns().add(firstNameCol);
1416 
1417         firstNameCol.setCellFactory(new Callback<TableColumn<Person,String>, TableCell<Person, String>>() {
1418             @Override
1419             public TableCell<Person, String> call(TableColumn<Person,String> param) {
1420                 return new TableCellShim<Person, String>() {
1421                     ImageView view = new ImageView();
1422                     { setGraphic(view); };
1423 
1424                     @Override
1425                     public void updateItem(String item, boolean empty) {
1426                         if (getItem() == null ? item == null : getItem().equals(item)) {
1427                             rt_31200_count++;
1428                         }
1429                         super.updateItem(item, empty);
1430                         if (item == null || empty) {
1431                             view.setImage(null);
1432                             setText(null);
1433                         } else {
1434                             setText(item);
1435                         }
1436                     }
1437                 };
1438             }
1439         });
1440 
1441         StageLoader sl = new StageLoader(tableView);
1442 
1443         assertEquals(14, rt_31200_count);
1444 
1445         // resize the stage
1446         sl.getStage().setHeight(250);
1447         Toolkit.getToolkit().firePulse();
1448         sl.getStage().setHeight(50);
1449         Toolkit.getToolkit().firePulse();
1450         assertEquals(14, rt_31200_count);
1451 
1452         sl.dispose();
1453     }
1454 
1455     @Test public void test_rt_31200_tableRow() {
1456         rt_31200_count = 0;
1457         final TableView<Person> tableView = new TableView<Person>();
1458         tableView.setItems(FXCollections.observableArrayList(
1459                 new Person("Jacob", "Smith", "jacob.smith@example.com"),
1460                 new Person("Jim", "Bob", "jim.bob@example.com")
1461         ));
1462 
1463         TableColumn<Person,String> firstNameCol = new TableColumn("First Name");
1464         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
1465         tableView.getColumns().add(firstNameCol);
1466 
1467         tableView.setRowFactory(new Callback<TableView<Person>, TableRow<Person>>() {
1468             @Override
1469             public TableRow<Person> call(TableView<Person> param) {
1470                 return new TableRowShim<Person>() {
1471                     ImageView view = new ImageView();
1472                     { setGraphic(view); };
1473 
1474                     @Override
1475                     public void updateItem(Person item, boolean empty) {
1476                         if (getItem() == null ? item == null : getItem().equals(item)) {
1477                             rt_31200_count++;
1478                         }
1479                         super.updateItem(item, empty);
1480                         if (item == null || empty) {
1481                             view.setImage(null);
1482                             setText(null);
1483                         } else {
1484                             setText(item.toString());
1485                         }
1486                     }
1487                 };
1488             }
1489         });
1490 
1491         StageLoader sl = new StageLoader(tableView);
1492 
1493         assertEquals(14, rt_31200_count);
1494 
1495         // resize the stage
1496         sl.getStage().setHeight(250);
1497         Toolkit.getToolkit().firePulse();
1498         sl.getStage().setHeight(50);
1499         Toolkit.getToolkit().firePulse();
1500         assertEquals(14, rt_31200_count);
1501 
1502         sl.dispose();
1503     }
1504 
1505     @Test public void test_rt_31727() {
1506         TableView table = new TableView();
1507         TableColumn<String,String> first = new TableColumn<String,String>("first");
1508         first.setCellValueFactory(new PropertyValueFactory("firstName"));
1509         TableColumn<String,String> second = new TableColumn<String,String>("second");
1510         second.setCellValueFactory(new PropertyValueFactory("lastName"));
1511         table.getColumns().addAll(first, second);
1512 
1513         table.setItems(FXCollections.observableArrayList(
1514                 new Person("Jacob", "Smith", "jacob.smith@example.com"),
1515                 new Person("Jim", "Bob", "jim.bob@example.com")
1516         ));
1517 
1518         table.setEditable(true);
1519         first.setEditable(true);
1520 
1521         // do a normal edit
1522         table.edit(0, first);
1523         TablePosition editingCell = table.getEditingCell();
1524         assertNotNull(editingCell);
1525         assertEquals(0, editingCell.getRow());
1526         assertEquals(0, editingCell.getColumn());
1527         assertEquals(first, editingCell.getTableColumn());
1528         assertEquals(table, editingCell.getTableView());
1529 
1530         // cancel editing
1531         table.edit(-1, null);
1532         editingCell = table.getEditingCell();
1533         assertNull(editingCell);
1534     }
1535 
1536     @Test public void test_rt_21517() {
1537         TableView table = new TableView();
1538         TableColumn<String,String> first = new TableColumn<String,String>("first");
1539         first.setCellValueFactory(new PropertyValueFactory("firstName"));
1540         TableColumn<String,String> second = new TableColumn<String,String>("second");
1541         second.setCellValueFactory(new PropertyValueFactory("lastName"));
1542         table.getColumns().addAll(first, second);
1543 
1544         Person aaa,bbb,ccc;
1545         table.setItems(FXCollections.observableArrayList(
1546                 aaa = new Person("AAA", "Smith", "jacob.smith@example.com"),
1547                 bbb = new Person("BBB", "Bob", "jim.bob@example.com"),
1548                 ccc = new Person("CCC", "Giles", "jim.bob@example.com")
1549         ));
1550 
1551         final TableView.TableViewSelectionModel sm = table.getSelectionModel();
1552 
1553         // test pre-conditions
1554         assertEquals(0, sm.getSelectedCells().size());
1555         assertEquals(0, sm.getSelectedItems().size());
1556         assertEquals(0, sm.getSelectedIndices().size());
1557 
1558         // select the 3rd row (that is, CCC)
1559         sm.select(2);
1560         assertTrue(sm.isSelected(2));
1561         assertEquals(2, sm.getSelectedIndex());
1562         assertEquals(1, sm.getSelectedIndices().size());
1563         assertTrue(sm.getSelectedIndices().contains(2));
1564         assertEquals(ccc, sm.getSelectedItem());
1565         assertEquals(1, sm.getSelectedItems().size());
1566         assertTrue(sm.getSelectedItems().contains(ccc));
1567 
1568         // we also want to test visually
1569         TableRow aaaRow = (TableRow) VirtualFlowTestUtils.getCell(table, 0);
1570         assertFalse(aaaRow.isSelected());
1571         TableRow cccRow = (TableRow) VirtualFlowTestUtils.getCell(table, 2);
1572         assertTrue(cccRow.isSelected());
1573 
1574         // sort tableview by firstname column in ascending (default) order
1575         // (so aaa continues to come first)
1576         table.getSortOrder().add(first);
1577 
1578         // nothing should have changed
1579         assertTrue(sm.isSelected(2));
1580         assertEquals(2, sm.getSelectedIndex());
1581         assertEquals(1, sm.getSelectedIndices().size());
1582         assertTrue(sm.getSelectedIndices().contains(2));
1583         assertEquals(ccc, sm.getSelectedItem());
1584         assertEquals(1, sm.getSelectedItems().size());
1585         assertTrue(sm.getSelectedItems().contains(ccc));
1586         aaaRow = (TableRow) VirtualFlowTestUtils.getCell(table, 0);
1587         assertFalse(aaaRow.isSelected());
1588         cccRow = (TableRow) VirtualFlowTestUtils.getCell(table, 2);
1589         assertTrue(cccRow.isSelected());
1590 
1591         // continue to sort tableview by firstname column, but now in descending
1592         // order, (so ccc to come first)
1593         first.setSortType(TableColumn.SortType.DESCENDING);
1594 
1595         // now test to ensure that CCC is still the only selected item, but now
1596         // located in index 0
1597         assertTrue(sm.isSelected(0));
1598         assertEquals(0, sm.getSelectedIndex());
1599         assertEquals(1, sm.getSelectedIndices().size());
1600         assertTrue(sm.getSelectedIndices().contains(0));
1601         assertEquals(ccc, sm.getSelectedItem());
1602         assertEquals(1, sm.getSelectedItems().size());
1603         assertTrue(sm.getSelectedItems().contains(ccc));
1604 
1605         // we also want to test visually
1606         aaaRow = (TableRow) VirtualFlowTestUtils.getCell(table, 1);
1607         assertFalse(aaaRow.isSelected());
1608         cccRow = (TableRow) VirtualFlowTestUtils.getCell(table, 0);
1609         assertTrue(cccRow.isSelected());
1610     }
1611 
1612     @Test public void test_rt_30484_tableCell() {
1613         TableView<Person> table = new TableView<>();
1614         TableColumn<Person,String> first = new TableColumn<>("first");
1615         first.setCellValueFactory(new PropertyValueFactory("firstName"));
1616         table.getColumns().addAll(first);
1617 
1618         table.setItems(FXCollections.observableArrayList(
1619                 new Person("AAA", "Smith", "jacob.smith@example.com"),
1620                 new Person("BBB", "Bob", "jim.bob@example.com")
1621         ));
1622 
1623         first.setCellFactory(new Callback<TableColumn<Person, String>, TableCell<Person, String>>() {
1624             @Override
1625             public TableCell<Person, String> call(TableColumn<Person, String> param) {
1626                 return new TableCellShim<Person, String>() {
1627                     Rectangle graphic = new Rectangle(10, 10, Color.RED);
1628                     { setGraphic(graphic); };
1629 
1630                     @Override public void updateItem(String item, boolean empty) {
1631                         super.updateItem(item, empty);
1632                         if (item == null || empty) {
1633                             graphic.setVisible(false);
1634                             setText(null);
1635                         } else {
1636                             graphic.setVisible(true);
1637                             setText(item);
1638                         }
1639                     }
1640                 };
1641             }
1642         });
1643 
1644         // First two rows have content, so the graphic should show.
1645         // All other rows have no content, so graphic should not show.
1646 
1647         VirtualFlowTestUtils.assertGraphicIsVisible(table,    0, 0);
1648         VirtualFlowTestUtils.assertGraphicIsVisible(table,    1, 0);
1649         VirtualFlowTestUtils.assertGraphicIsNotVisible(table, 2, 0);
1650         VirtualFlowTestUtils.assertGraphicIsNotVisible(table, 3, 0);
1651         VirtualFlowTestUtils.assertGraphicIsNotVisible(table, 4, 0);
1652         VirtualFlowTestUtils.assertGraphicIsNotVisible(table, 5, 0);
1653     }
1654 
1655     @Test public void test_rt_30484_tableRow() {
1656         TableView<Person> table = new TableView<>();
1657         TableColumn<Person,String> first = new TableColumn<Person,String>("first");
1658         first.setCellValueFactory(new PropertyValueFactory("firstName"));
1659         table.getColumns().addAll(first);
1660 
1661         table.setItems(FXCollections.observableArrayList(
1662                 new Person("AAA", "Smith", "jacob.smith@example.com"),
1663                 new Person("BBB", "Bob", "jim.bob@example.com")
1664         ));
1665 
1666         table.setRowFactory(new Callback<TableView<Person>, TableRow<Person>>() {
1667             @Override public TableRow<Person> call(TableView<Person> param) {
1668                 return new TableRowShim<Person>() {
1669                     Rectangle graphic = new Rectangle(10, 10, Color.RED);
1670                     { setGraphic(graphic); };
1671 
1672                     @Override public void updateItem(Person item, boolean empty) {
1673                         super.updateItem(item, empty);
1674                         if (item == null || empty) {
1675                             graphic.setVisible(false);
1676                             setText(null);
1677                         } else {
1678                             graphic.setVisible(true);
1679                             setText(item.toString());
1680                         }
1681                     }
1682                 };
1683             }
1684         });
1685 
1686         // First two rows have content, so the graphic should show.
1687         // All other rows have no content, so graphic should not show.
1688 
1689         VirtualFlowTestUtils.assertGraphicIsVisible(table,    0);
1690         VirtualFlowTestUtils.assertGraphicIsVisible(table,    1);
1691         VirtualFlowTestUtils.assertGraphicIsNotVisible(table, 2);
1692         VirtualFlowTestUtils.assertGraphicIsNotVisible(table, 3);
1693         VirtualFlowTestUtils.assertGraphicIsNotVisible(table, 4);
1694         VirtualFlowTestUtils.assertGraphicIsNotVisible(table, 5);
1695     }
1696 
1697     @Test public void test_rt_32201() {
1698         final int numRows = 150;
1699         final int numColumns = 30;
1700 
1701         // create data
1702         final ObservableList<List<Object>> bigData = FXCollections.observableArrayList();
1703         for (int row = bigData.size(); row < numRows; row++) {
1704             List<Object> line = new ArrayList<>();
1705             for (int col = 0; col <= numColumns; col++) {
1706                 double value = (col == 0) ? (double)row : Math.random() * 1000;
1707                 line.add(value);
1708             }
1709             bigData.add(line);
1710         }
1711 
1712         TableView<List<Object>> table = new TableView<>(bigData);
1713 
1714         // create columns
1715         for (int i = 0; i <= numColumns; i++) {
1716             TableColumn<List<Object>,Object> col = new TableColumn<>("Col" + i);
1717             final int coli = i;
1718             col.setCellValueFactory(p -> new ReadOnlyObjectWrapper(p.getValue().get(coli)));
1719 
1720             table.getColumns().add(col);
1721         }
1722 
1723         // start test
1724         assertNull(table.getComparator());
1725         assertTrue(table.getSortOrder().isEmpty());
1726 
1727         table.getSortOrder().add(table.getColumns().get(0));
1728         assertNotNull(table.getComparator());
1729         assertTrue(table.getSortOrder().contains(table.getColumns().get(0)));
1730         assertEquals(1, table.getSortOrder().size());
1731 
1732         // remove column again
1733         try {
1734             table.getSortOrder().clear();
1735         } catch (ClassCastException e) {
1736             fail("Comparator should be null as the sort order list is empty.");
1737         }
1738         assertNull(table.getComparator());
1739         assertTrue(table.getSortOrder().isEmpty());
1740     }
1741 
1742     private int rt_31015_count = 0;
1743     @Test public void test_rt_31015() {
1744         TableView<Person> table = new TableView<>();
1745         table.setEditable(true);
1746         TableColumn<Person,String> first = new TableColumn<Person,String>("first");
1747         first.setCellValueFactory(new PropertyValueFactory("firstName"));
1748         table.getColumns().addAll(first);
1749 
1750         table.setItems(FXCollections.observableArrayList(
1751             new Person("John", "Smith", "jacob.smith@example.com")
1752         ));
1753 
1754         //Set cell factory for cells that allow editing
1755         Callback<TableColumn<Person,String>, TableCell<Person, String>> cellFactory = new Callback<TableColumn<Person,String>, TableCell<Person, String>>() {
1756             public TableCell<Person, String> call(TableColumn<Person, String> p) {
1757                 return new TableCell<Person, String>() {
1758                     @Override public void cancelEdit() {
1759                         super.cancelEdit();
1760                         rt_31015_count++;
1761                     }
1762                 };
1763             }
1764         };
1765         first.setCellFactory(cellFactory);
1766 
1767         StageLoader sl = new StageLoader(table);
1768 
1769         assertEquals(0, rt_31015_count);
1770 
1771         table.edit(0, first);
1772         assertEquals(0, rt_31015_count);
1773 
1774         table.edit(-1, null);
1775         assertEquals(1, rt_31015_count);
1776 
1777         sl.dispose();
1778     }
1779 
1780     @Test public void test_rt_31653() {
1781         TableView<Person> table = new TableView<>();
1782         TableColumn<Person,String> first = new TableColumn<Person,String>("first");
1783         first.setCellValueFactory(new PropertyValueFactory("firstName"));
1784         table.getColumns().addAll(first);
1785 
1786         table.setItems(FXCollections.observableArrayList(
1787                 new Person("John", "Smith", "jacob.smith@example.com")
1788         ));
1789 
1790         TableRow<Person> rowCell = (TableRow<Person>)VirtualFlowTestUtils.getCell(table, 0);
1791         final double initialWidth = ControlShim.computePrefWidth(rowCell, -1);
1792 
1793         first.setPrefWidth(200);
1794 
1795         final double newWidth = ControlShim.computePrefWidth(rowCell, -1);
1796         assertEquals(200, newWidth, 0.0);
1797         assertTrue(initialWidth != newWidth);
1798     }
1799 
1800     private int rt_29650_start_count = 0;
1801     private int rt_29650_commit_count = 0;
1802     private int rt_29650_cancel_count = 0;
1803     @Test public void test_rt_29650() {
1804         TableView<Person> table = new TableView<>();
1805         table.setEditable(true);
1806         table.setItems(FXCollections.observableArrayList(
1807                 new Person("John", "Smith", "jacob.smith@example.com")
1808         ));
1809 
1810         TableColumn<Person,String> first = new TableColumn<Person,String>("first");
1811         first.setCellValueFactory(new PropertyValueFactory("firstName"));
1812         Callback<TableColumn<Person, String>, TableCell<Person, String>> factory = TextFieldTableCell.forTableColumn();
1813         first.setCellFactory(factory);
1814         table.getColumns().addAll(first);
1815         first.setOnEditStart(t -> {
1816             rt_29650_start_count++;
1817         });
1818         first.setOnEditCommit(t -> {
1819             rt_29650_commit_count++;
1820         });
1821         first.setOnEditCancel(t -> {
1822             rt_29650_cancel_count++;
1823         });
1824 
1825         StageLoader sl = new StageLoader(table);
1826 
1827         table.edit(0, first);
1828 
1829         Toolkit.getToolkit().firePulse();
1830 
1831         TableCell rootCell = (TableCell) VirtualFlowTestUtils.getCell(table, 0, 0);
1832         TextField textField = (TextField) rootCell.getGraphic();
1833         textField.setText("Testing!");
1834         KeyEventFirer keyboard = new KeyEventFirer(textField);
1835         keyboard.doKeyPress(KeyCode.ENTER);
1836 
1837         // TODO should the following assert be enabled?
1838 //        assertEquals("Testing!", listView.getItems().get(0));
1839         assertEquals(1, rt_29650_start_count);
1840         assertEquals(1, rt_29650_commit_count);
1841         assertEquals(0, rt_29650_cancel_count);
1842 
1843         sl.dispose();
1844     }
1845 
1846     private int rt_29849_start_count = 0;
1847     @Test public void test_rt_29849() {
1848         TableView<Person> table = new TableView<>();
1849         table.setEditable(true);
1850         table.setItems(FXCollections.observableArrayList(
1851             new Person("John", "Smith", "jacob.smith@example.com")
1852         ));
1853 
1854         TableColumn<Person,String> first = new TableColumn<Person,String>("first");
1855         first.setCellValueFactory(new PropertyValueFactory("firstName"));
1856         Callback<TableColumn<Person, String>, TableCell<Person, String>> factory = TextFieldTableCell.forTableColumn();
1857         first.setCellFactory(factory);
1858         table.getColumns().addAll(first);
1859         first.setOnEditStart(t -> {
1860             rt_29849_start_count++;
1861         });
1862 
1863         // load the table so the default cells are created
1864         StageLoader sl = new StageLoader(table);
1865 
1866         // now replace the cell factory
1867         first.setCellFactory(TextFieldTableCell.<Person>forTableColumn());
1868 
1869         Toolkit.getToolkit().firePulse();
1870 
1871         // now start an edit and count the start edit events - it should be just 1
1872         table.edit(0, first);
1873         assertEquals(1, rt_29849_start_count);
1874 
1875         sl.dispose();
1876     }
1877 
1878     @Test public void test_rt_32708_removeFromColumnsList() {
1879         TableView<Person> table = new TableView<>();
1880         table.setEditable(true);
1881         table.setItems(FXCollections.observableArrayList(
1882                 new Person("John", "Smith", "jacob.smith@example.com")
1883         ));
1884 
1885         TableColumn<Person,String> first = new TableColumn<Person,String>("first");
1886         first.setCellValueFactory(new PropertyValueFactory("firstName"));
1887         TableColumn<Person,String> last = new TableColumn<Person,String>("last");
1888         first.setCellValueFactory(new PropertyValueFactory("lastName"));
1889         TableColumn<Person,String> email = new TableColumn<Person,String>("email");
1890         first.setCellValueFactory(new PropertyValueFactory("email"));
1891         table.getColumns().addAll(first, last, email);
1892 
1893         // load the table so the default cells are created
1894         StageLoader sl = new StageLoader(table);
1895 
1896         // test pre-conditions - last column should be visible
1897         VirtualFlowTestUtils.assertTableHeaderColumnExists(table, last, true);
1898 
1899         // remove last column from tableview
1900         table.getColumns().remove(last);
1901         Toolkit.getToolkit().firePulse();
1902 
1903         // test post conditions - last column should not be visible
1904         VirtualFlowTestUtils.assertTableHeaderColumnExists(table, last, false);
1905 
1906         sl.dispose();
1907     }
1908 
1909     @Test public void test_rt_32708_toggleVisible() {
1910         TableView<Person> table = new TableView<>();
1911         table.setEditable(true);
1912         table.setItems(FXCollections.observableArrayList(
1913                 new Person("John", "Smith", "jacob.smith@example.com")
1914         ));
1915 
1916         TableColumn<Person,String> first = new TableColumn<Person,String>("first");
1917         first.setCellValueFactory(new PropertyValueFactory("firstName"));
1918         TableColumn<Person,String> last = new TableColumn<Person,String>("last");
1919         first.setCellValueFactory(new PropertyValueFactory("lastName"));
1920         TableColumn<Person,String> email = new TableColumn<Person,String>("email");
1921         first.setCellValueFactory(new PropertyValueFactory("email"));
1922         table.getColumns().addAll(first, last, email);
1923 
1924         // load the table so the default cells are created
1925         StageLoader sl = new StageLoader(table);
1926 
1927         // test pre-conditions - last column should be visible
1928         VirtualFlowTestUtils.assertTableHeaderColumnExists(table, last, true);
1929 
1930         // hide the last column from tableview
1931         last.setVisible(false);
1932         Toolkit.getToolkit().firePulse();
1933 
1934         // test post conditions - last column should not be visible
1935         VirtualFlowTestUtils.assertTableHeaderColumnExists(table, last, false);
1936 
1937         sl.dispose();
1938     }
1939 
1940 //    @Ignore("Test started intermittently failing, most probably due to RT-36855 changeset")
1941     @Test public void test_rt_34493() {
1942         TableView<Person> table = new TableView<>();
1943         table.setItems(FXCollections.observableArrayList(
1944                 new Person("John", "Smith", "jacob.smith@example.com")
1945         ));
1946 
1947         TableColumn<Person,String> first = new TableColumn<Person,String>("first");
1948         first.setCellValueFactory(new PropertyValueFactory("firstName"));
1949         TableColumn<Person,String> last = new TableColumn<Person,String>("last");
1950         first.setCellValueFactory(new PropertyValueFactory("lastName"));
1951         TableColumn<Person,String> email = new TableColumn<Person,String>("email");
1952         first.setCellValueFactory(new PropertyValueFactory("email"));
1953         table.getColumns().addAll(first, last, email);
1954 
1955         // load the table
1956         StageLoader sl = new StageLoader(table);
1957 
1958         // resize the last column
1959         TableColumnBaseHelper.setWidth(last, 400);
1960         assertEquals(400, last.getWidth(), 0.0);
1961 
1962         // hide the first column
1963         table.getColumns().remove(first);
1964         Toolkit.getToolkit().firePulse();
1965 
1966         // the last column should still be 400px, not the default width or any
1967         // other value (based on the width of the content in that column)
1968         assertEquals(400, last.getWidth(), 0.0);
1969 
1970         sl.dispose();
1971     }
1972 
1973     @Test public void test_rt_34685_directEditCall_cellSelectionMode() {
1974         test_rt_34685_commitCount = 0;
1975         test_rt_34685(false, true);
1976     }
1977 
1978     @Test public void test_rt_34685_directEditCall_rowSelectionMode() {
1979         test_rt_34685_commitCount = 0;
1980         test_rt_34685(false, false);
1981     }
1982 
1983     @Test public void test_rt_34685_mouseDoubleClick_cellSelectionMode() {
1984         test_rt_34685_commitCount = 0;
1985         test_rt_34685(true, true);
1986     }
1987 
1988     @Test public void test_rt_34685_mouseDoubleClick_rowSelectionMode() {
1989         test_rt_34685_commitCount = 0;
1990         test_rt_34685(true, false);
1991     }
1992 
1993     private int test_rt_34685_commitCount = 0;
1994     private void test_rt_34685(boolean useMouseToInitiateEdit, boolean cellSelectionModeEnabled) {
1995         assertEquals(0, test_rt_34685_commitCount);
1996 
1997         TableView<Person> table = new TableView<>();
1998         table.getSelectionModel().setCellSelectionEnabled(cellSelectionModeEnabled);
1999         table.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
2000         table.setEditable(true);
2001 
2002         Person person1;
2003         table.setItems(FXCollections.observableArrayList(
2004             person1 = new Person("John", "Smith", "john.smith@example.com"),
2005             new Person("Jacob", "Michaels", "jacob.michaels@example.com"),
2006             new Person("Jim", "Bob", "jim.bob@example.com")
2007         ));
2008 
2009         TableColumn<Person,String> first = new TableColumn<Person,String>("first");
2010         first.setCellValueFactory(new PropertyValueFactory("firstName"));
2011         Callback<TableColumn<Person, String>, TableCell<Person, String>> factory = TextFieldTableCell.forTableColumn();
2012         first.setCellFactory(factory);       // note that only the first name col is editable
2013 
2014         EventHandler<TableColumn.CellEditEvent<Person, String>> onEditCommit = first.getOnEditCommit();
2015         first.setOnEditCommit(event -> {
2016             test_rt_34685_commitCount++;
2017             onEditCommit.handle(event);
2018         });
2019 
2020         table.getColumns().addAll(first);
2021 
2022         // get the cell at (0,0)
2023         VirtualFlowTestUtils.BLOCK_STAGE_LOADER_DISPOSE = true;
2024         TableCell cell = (TableCell) VirtualFlowTestUtils.getCell(table, 0, 0);
2025         VirtualFlowTestUtils.BLOCK_STAGE_LOADER_DISPOSE = false;
2026         assertTrue(cell.getSkin() instanceof TableCellSkin);
2027         assertNull(cell.getGraphic());
2028         assertEquals("John", cell.getText());
2029         assertEquals("John", person1.getFirstName());
2030 
2031         // set the table to be editing the first cell at 0,0
2032         if (useMouseToInitiateEdit) {
2033             MouseEventFirer mouse = new MouseEventFirer(cell);
2034             mouse.fireMousePressAndRelease(2, 10, 10);  // click 10 pixels in and 10 pixels down
2035             mouse.dispose();
2036         } else {
2037             table.edit(0,first);
2038         }
2039 
2040         Toolkit.getToolkit().firePulse();
2041         assertNotNull(cell.getGraphic());
2042         assertTrue(cell.getGraphic() instanceof TextField);
2043 
2044         TextField textField = (TextField) cell.getGraphic();
2045         assertEquals("John", textField.getText());
2046 
2047         textField.setText("Andrew");
2048         textField.requestFocus();
2049         Toolkit.getToolkit().firePulse();
2050 
2051         KeyEventFirer keyboard = new KeyEventFirer(textField);
2052         keyboard.doKeyPress(KeyCode.ENTER);
2053 
2054         VirtualFlowTestUtils.getVirtualFlow(table).requestLayout();
2055         Toolkit.getToolkit().firePulse();
2056 
2057         VirtualFlowTestUtils.assertTableCellTextEquals(table, 0, 0, "Andrew");
2058         assertEquals("Andrew", cell.getText());
2059         assertEquals("Andrew", person1.getFirstName());
2060         assertEquals(1, test_rt_34685_commitCount);
2061     }
2062 
2063     @Test public void test_rt_35224() {
2064         TableView table = new TableView();
2065         TableColumn col1 = new TableColumn();
2066         TableColumn col2 = new TableColumn();
2067         table.getColumns().setAll(col1, col2);
2068 
2069         StageLoader sl = new StageLoader(table);
2070 
2071         Toolkit.getToolkit().firePulse();
2072         col1.getColumns().setAll(new TableColumn(), new TableColumn());
2073         Toolkit.getToolkit().firePulse();
2074         col2.getColumns().setAll(new TableColumn(), new TableColumn());
2075         Toolkit.getToolkit().firePulse();
2076 
2077         sl.dispose();
2078     }
2079 
2080     @Test public void test_rt_35141_simple_switch_two_columns_move_col1_forward_1_place() {
2081         TableView table = new TableView();
2082         TableColumn col1 = new TableColumn();
2083         TableColumn col2 = new TableColumn();
2084         table.getColumns().setAll(col1, col2);
2085 
2086         StageLoader sl = new StageLoader(table);
2087 
2088         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2089         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col2));
2090 
2091         TableColumnHeaderUtil.moveColumn(col1, 1);
2092         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col1));
2093         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col2));
2094 
2095         sl.dispose();
2096     }
2097 
2098     @Test public void test_rt_35141_simple_switch_two_columns_move_col2_backward_1_place() {
2099         TableView table = new TableView();
2100         TableColumn col1 = new TableColumn();
2101         TableColumn col2 = new TableColumn();
2102         table.getColumns().setAll(col1, col2);
2103 
2104         StageLoader sl = new StageLoader(table);
2105 
2106         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2107         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col2));
2108 
2109         TableColumnHeaderUtil.moveColumn(col2, 0);
2110         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col1));
2111         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col2));
2112 
2113         sl.dispose();
2114     }
2115 
2116     @Test public void test_rt_35141_simple_switch_three_columns_move_col1_forward_1_place() {
2117         TableView table = new TableView();
2118         TableColumn col1 = new TableColumn();
2119         TableColumn col2 = new TableColumn();
2120         TableColumn col3 = new TableColumn();
2121         table.getColumns().setAll(col1, col2, col3);
2122 
2123         StageLoader sl = new StageLoader(table);
2124 
2125         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2126         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col2));
2127         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col3));
2128 
2129         TableColumnHeaderUtil.moveColumn(col1, 1);
2130         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col1));
2131         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col2));
2132         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col3));
2133 
2134         sl.dispose();
2135     }
2136 
2137     @Test public void test_rt_35141_simple_switch_three_columns_move_col2_backward_1_place() {
2138         TableView table = new TableView();
2139         TableColumn col1 = new TableColumn();
2140         TableColumn col2 = new TableColumn();
2141         TableColumn col3 = new TableColumn();
2142         table.getColumns().setAll(col1, col2, col3);
2143 
2144         StageLoader sl = new StageLoader(table);
2145 
2146         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2147         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col2));
2148         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col3));
2149 
2150         TableColumnHeaderUtil.moveColumn(col2, 0);
2151         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col1));
2152         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col2));
2153         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col3));
2154 
2155         sl.dispose();
2156     }
2157 
2158     @Test public void test_rt_35141_simple_switch_three_columns_move_col2_forward_1_place() {
2159         TableView table = new TableView();
2160         TableColumn col1 = new TableColumn();
2161         TableColumn col2 = new TableColumn();
2162         TableColumn col3 = new TableColumn();
2163         table.getColumns().setAll(col1, col2, col3);
2164 
2165         StageLoader sl = new StageLoader(table);
2166 
2167         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2168         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col2));
2169         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col3));
2170 
2171         TableColumnHeaderUtil.moveColumn(col2, 2);
2172         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2173         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col2));
2174         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col3));
2175 
2176         sl.dispose();
2177     }
2178 
2179     @Test public void test_rt_35141_simple_switch_three_columns_move_col3_backward_1_place() {
2180         TableView table = new TableView();
2181         TableColumn col1 = new TableColumn();
2182         TableColumn col2 = new TableColumn();
2183         TableColumn col3 = new TableColumn();
2184         table.getColumns().setAll(col1, col2, col3);
2185 
2186         StageLoader sl = new StageLoader(table);
2187 
2188         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2189         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col2));
2190         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col3));
2191 
2192         TableColumnHeaderUtil.moveColumn(col3, 1);
2193         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2194         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col2));
2195         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col3));
2196 
2197         sl.dispose();
2198     }
2199 
2200     @Test public void test_rt_35141_simple_switch_three_columns_move_col0_forward_2_places() {
2201         TableView table = new TableView();
2202         TableColumn col1 = new TableColumn();
2203         TableColumn col2 = new TableColumn();
2204         TableColumn col3 = new TableColumn();
2205         table.getColumns().setAll(col1, col2, col3);
2206 
2207         StageLoader sl = new StageLoader(table);
2208 
2209         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2210         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col2));
2211         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col3));
2212 
2213         TableColumnHeaderUtil.moveColumn(col1, 2);
2214         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col1));
2215         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col2));
2216         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col3));
2217 
2218         sl.dispose();
2219     }
2220 
2221     @Test public void test_rt_35141_simple_switch_three_columns_move_col3_backward_2_places() {
2222         TableView table = new TableView();
2223         TableColumn col1 = new TableColumn();
2224         TableColumn col2 = new TableColumn();
2225         TableColumn col3 = new TableColumn();
2226         table.getColumns().setAll(col1, col2, col3);
2227 
2228         StageLoader sl = new StageLoader(table);
2229 
2230         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2231         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col2));
2232         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col3));
2233 
2234         TableColumnHeaderUtil.moveColumn(col3, 0);
2235         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col1));
2236         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col2));
2237         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col3));
2238 
2239         sl.dispose();
2240     }
2241 
2242     @Test public void test_rt_35141_hidden_column_move_col1_forward_1_place() {
2243         TableView table = new TableView();
2244         TableColumn col1 = new TableColumn();
2245         TableColumn col2 = new TableColumn();
2246         TableColumn col3 = new TableColumn();
2247         table.getColumns().setAll(col1, col2, col3);
2248 
2249         StageLoader sl = new StageLoader(table);
2250 
2251         col2.setVisible(false);
2252 
2253         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2254         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col2));
2255         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col3));
2256 
2257         TableColumnHeaderUtil.moveColumn(col1, 1);
2258         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col1));
2259         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col2));
2260         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col3));
2261 
2262         sl.dispose();
2263     }
2264 
2265     @Test public void test_rt_35141_hidden_column_move_col1_forward_100_places() {
2266         TableView table = new TableView();
2267         TableColumn col1 = new TableColumn();
2268         TableColumn col2 = new TableColumn();
2269         TableColumn col3 = new TableColumn();
2270         table.getColumns().setAll(col1, col2, col3);
2271 
2272         StageLoader sl = new StageLoader(table);
2273 
2274         col2.setVisible(false);
2275 
2276         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2277         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col2));
2278         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col3));
2279 
2280         TableColumnHeaderUtil.moveColumn(col1, 100);
2281         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col1));
2282         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col2));
2283         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col3));
2284 
2285         sl.dispose();
2286     }
2287 
2288     @Test public void test_rt_35141_hidden_column_move_col3_backward_1_place() {
2289         TableView table = new TableView();
2290         TableColumn col1 = new TableColumn();
2291         TableColumn col2 = new TableColumn();
2292         TableColumn col3 = new TableColumn();
2293         table.getColumns().setAll(col1, col2, col3);
2294 
2295         StageLoader sl = new StageLoader(table);
2296 
2297         col2.setVisible(false);
2298 
2299         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2300         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col2));
2301         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col3));
2302 
2303         TableColumnHeaderUtil.moveColumn(col3, 0);
2304         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col1));
2305         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col2));
2306         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col3));
2307 
2308         sl.dispose();
2309     }
2310 
2311     @Test public void test_rt_35141_multiple_hidden_columns_move_col1_to_middle() {
2312         TableView table = new TableView();
2313         TableColumn col1 = new TableColumn();
2314         TableColumn col2 = new TableColumn();
2315         TableColumn col3 = new TableColumn();
2316         TableColumn col4 = new TableColumn();
2317         TableColumn col5 = new TableColumn();
2318         TableColumn col6 = new TableColumn();
2319         table.getColumns().setAll(col1, col2, col3, col4, col5, col6);
2320 
2321         StageLoader sl = new StageLoader(table);
2322 
2323         col2.setVisible(false);
2324         col4.setVisible(false);
2325 
2326         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2327         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col2));
2328         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col3));
2329         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col4));
2330         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col5));
2331         assertEquals(3, TableColumnHeaderUtil.getColumnIndex(col6));
2332 
2333         TableColumnHeaderUtil.moveColumn(col1, 1);    // 1 should represent the spot between col2 and col4
2334         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col1));
2335         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col2));
2336         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col3));
2337         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col4));
2338         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col5));
2339         assertEquals(3, TableColumnHeaderUtil.getColumnIndex(col6));
2340 
2341         sl.dispose();
2342     }
2343 
2344     @Test public void test_rt_35141_multiple_hidden_columns_move_col1_to_end() {
2345         TableView table = new TableView();
2346         TableColumn col1 = new TableColumn();
2347         TableColumn col2 = new TableColumn();
2348         TableColumn col3 = new TableColumn();
2349         TableColumn col4 = new TableColumn();
2350         TableColumn col5 = new TableColumn();
2351         TableColumn col6 = new TableColumn();
2352         table.getColumns().setAll(col1, col2, col3, col4, col5, col6);
2353 
2354         StageLoader sl = new StageLoader(table);
2355 
2356         col2.setVisible(false);
2357         col4.setVisible(false);
2358 
2359         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2360         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col2));
2361         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col3));
2362         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col4));
2363         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col5));
2364         assertEquals(3, TableColumnHeaderUtil.getColumnIndex(col6));
2365 
2366         TableColumnHeaderUtil.moveColumn(col1, 3);    // 3 should represent the end place
2367         assertEquals(3, TableColumnHeaderUtil.getColumnIndex(col1));
2368         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col2));
2369         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col3));
2370         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col4));
2371         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col5));
2372         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col6));
2373 
2374         sl.dispose();
2375     }
2376 
2377     @Test public void test_rt_35141_multiple_hidden_columns_move_col3_to_start() {
2378         TableView table = new TableView();
2379         TableColumn col1 = new TableColumn();
2380         TableColumn col2 = new TableColumn();
2381         TableColumn col3 = new TableColumn();
2382         TableColumn col4 = new TableColumn();
2383         TableColumn col5 = new TableColumn();
2384         TableColumn col6 = new TableColumn();
2385         table.getColumns().setAll(col1, col2, col3, col4, col5, col6);
2386 
2387         StageLoader sl = new StageLoader(table);
2388 
2389         col2.setVisible(false);
2390         col4.setVisible(false);
2391 
2392         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2393         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col2));
2394         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col3));
2395         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col4));
2396         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col5));
2397         assertEquals(3, TableColumnHeaderUtil.getColumnIndex(col6));
2398 
2399         TableColumnHeaderUtil.moveColumn(col3, 0);
2400         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col1));
2401         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col2));
2402         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col3));
2403         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col4));
2404         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col5));
2405         assertEquals(3, TableColumnHeaderUtil.getColumnIndex(col6));
2406 
2407         sl.dispose();
2408     }
2409 
2410     @Test public void test_rt_35141_multiple_hidden_columns_move_col3_to_end() {
2411         TableView table = new TableView();
2412         TableColumn col1 = new TableColumn();
2413         TableColumn col2 = new TableColumn();
2414         TableColumn col3 = new TableColumn();
2415         TableColumn col4 = new TableColumn();
2416         TableColumn col5 = new TableColumn();
2417         TableColumn col6 = new TableColumn();
2418         table.getColumns().setAll(col1, col2, col3, col4, col5, col6);
2419 
2420         StageLoader sl = new StageLoader(table);
2421 
2422         col2.setVisible(false);
2423         col4.setVisible(false);
2424 
2425         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2426         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col2));
2427         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col3));
2428         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col4));
2429         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col5));
2430         assertEquals(3, TableColumnHeaderUtil.getColumnIndex(col6));
2431 
2432         TableColumnHeaderUtil.moveColumn(col3, 3);    // 3 should represent the end place
2433         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2434         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col2));
2435         assertEquals(3, TableColumnHeaderUtil.getColumnIndex(col3));
2436         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col4));
2437         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col5));
2438         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col6));
2439 
2440         sl.dispose();
2441     }
2442 
2443     @Test public void test_rt_35141_multiple_hidden_columns_move_col6_to_start() {
2444         TableView table = new TableView();
2445         TableColumn col1 = new TableColumn();
2446         TableColumn col2 = new TableColumn();
2447         TableColumn col3 = new TableColumn();
2448         TableColumn col4 = new TableColumn();
2449         TableColumn col5 = new TableColumn();
2450         TableColumn col6 = new TableColumn();
2451         table.getColumns().setAll(col1, col2, col3, col4, col5, col6);
2452 
2453         StageLoader sl = new StageLoader(table);
2454 
2455         col2.setVisible(false);
2456         col4.setVisible(false);
2457 
2458         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2459         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col2));
2460         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col3));
2461         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col4));
2462         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col5));
2463         assertEquals(3, TableColumnHeaderUtil.getColumnIndex(col6));
2464 
2465         TableColumnHeaderUtil.moveColumn(col6, 0);
2466         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col1));
2467         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col2));
2468         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col3));
2469         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col4));
2470         assertEquals(3, TableColumnHeaderUtil.getColumnIndex(col5));
2471         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col6));
2472 
2473         sl.dispose();
2474     }
2475 
2476     @Test public void test_rt_35141_multiple_hidden_columns_move_col6_to_middle() {
2477         TableView table = new TableView();
2478         TableColumn col1 = new TableColumn();
2479         TableColumn col2 = new TableColumn();
2480         TableColumn col3 = new TableColumn();
2481         TableColumn col4 = new TableColumn();
2482         TableColumn col5 = new TableColumn();
2483         TableColumn col6 = new TableColumn();
2484         table.getColumns().setAll(col1, col2, col3, col4, col5, col6);
2485 
2486         StageLoader sl = new StageLoader(table);
2487 
2488         col2.setVisible(false);
2489         col4.setVisible(false);
2490 
2491         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2492         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col2));
2493         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col3));
2494         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col4));
2495         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col5));
2496         assertEquals(3, TableColumnHeaderUtil.getColumnIndex(col6));
2497 
2498         TableColumnHeaderUtil.moveColumn(col6, 1);
2499         assertEquals(0, TableColumnHeaderUtil.getColumnIndex(col1));
2500         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col2));
2501         assertEquals(2, TableColumnHeaderUtil.getColumnIndex(col3));
2502         assertEquals(-1, TableColumnHeaderUtil.getColumnIndex(col4));
2503         assertEquals(3, TableColumnHeaderUtil.getColumnIndex(col5));
2504         assertEquals(1, TableColumnHeaderUtil.getColumnIndex(col6));
2505 
2506         sl.dispose();
2507     }
2508 
2509     @Test public void test_rt_34042() {
2510         Scene scene = new Scene(new Group());
2511         SplitPane splitPane = new SplitPane();
2512         splitPane.setOrientation(Orientation.VERTICAL);
2513 
2514         //TREETABLECOLUMN
2515         TreeTableView<Person> treeTableView = new TreeTableView<>();
2516         TreeTableColumn temp = new TreeTableColumn("First Name");
2517         temp.setMinWidth(100);
2518         temp.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
2519 
2520         TreeTableColumn temp2 = new TreeTableColumn("Last Name");
2521         temp2.setMinWidth(100);
2522         temp2.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
2523 
2524         TreeTableColumn temp3 = new TreeTableColumn("Email");
2525         temp3.setMinWidth(200);
2526         temp3.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));
2527 
2528         treeTableView.getColumns().addAll(temp, temp2, temp3);
2529 
2530         //TABLE
2531         TableView<Person> table = new TableView<Person>();
2532         table.setEditable(true);
2533         table.getSelectionModel().setCellSelectionEnabled(true);
2534         TableColumn firstNameCol = new TableColumn("First Name");
2535         firstNameCol.setMinWidth(100);
2536         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
2537 
2538         TableColumn lastNameCol = new TableColumn("Last Name");
2539         lastNameCol.setMinWidth(100);
2540         lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
2541 
2542         TableColumn emailCol = new TableColumn("Email");
2543         emailCol.setMinWidth(200);
2544         emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));
2545 
2546         table.setItems(personTestData);
2547         table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
2548 
2549         splitPane.getItems().add(treeTableView);
2550         splitPane.getItems().add(table);
2551 
2552         ((Group) scene.getRoot()).getChildren().addAll(splitPane);
2553 
2554         new StageLoader(scene);
2555 
2556         TableView.TableViewSelectionModel sm = table.getSelectionModel();
2557         sm.select(2, lastNameCol);
2558         assertFalse(sm.isSelected(2, firstNameCol));
2559         assertTrue(sm.isSelected(2, lastNameCol));
2560         assertFalse(sm.isSelected(2, emailCol));
2561 
2562         KeyEventFirer keyboard = new KeyEventFirer(table);
2563         keyboard.doKeyPress(KeyCode.LEFT);
2564         assertTrue(sm.isSelected(2, firstNameCol));
2565         assertFalse(sm.isSelected(2, lastNameCol));
2566         assertFalse(sm.isSelected(2, emailCol));
2567 
2568         keyboard.doKeyPress(KeyCode.RIGHT);
2569         assertFalse(sm.isSelected(2, firstNameCol));
2570         assertTrue(sm.isSelected(2, lastNameCol));
2571         assertFalse(sm.isSelected(2, emailCol));
2572 
2573         keyboard.doKeyPress(KeyCode.RIGHT);
2574         assertFalse(sm.isSelected(2, firstNameCol));
2575         assertFalse(sm.isSelected(2, lastNameCol));
2576         assertTrue(sm.isSelected(2, emailCol));
2577     }
2578 
2579     @Test public void test_rt35039() {
2580         final List<String> data = new ArrayList<>();
2581         data.add("aabbaa");
2582         data.add("bbc");
2583 
2584         final TableView<String> tableView = new TableView<>();
2585         tableView.setItems(FXCollections.observableArrayList(data));
2586 
2587         StageLoader sl = new StageLoader(tableView);
2588 
2589         // selection starts off at row -1
2590         assertNull(tableView.getSelectionModel().getSelectedItem());
2591 
2592         // select "bbc" and ensure everything is set to that
2593         tableView.getSelectionModel().select(1);
2594         assertEquals("bbc", tableView.getSelectionModel().getSelectedItem());
2595 
2596         // change the items list - but retain the same content. We expect
2597         // that "bbc" remains selected as it is still in the list
2598         tableView.setItems(FXCollections.observableArrayList(data));
2599         assertEquals("bbc", tableView.getSelectionModel().getSelectedItem());
2600 
2601         sl.dispose();
2602     }
2603 
2604     @Test public void test_rt35763_observableList() {
2605         TableView<Person> table = new TableView();
2606 
2607         TableColumn firstNameCol = new TableColumn("First Name");
2608         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
2609         table.getColumns().add(firstNameCol);
2610 
2611         Person jacob, isabella, ethan, emma, michael;
2612         table.setItems(FXCollections.observableArrayList(
2613                 jacob = new Person("Jacob", "Smith", "jacob.smith@example.com"),
2614                 isabella = new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
2615                 ethan = new Person("Ethan", "Williams", "ethan.williams@example.com"),
2616                 emma = new Person("Emma", "Jones", "emma.jones@example.com"),
2617                 michael = new Person("Michael", "Brown", "michael.brown@example.com")));
2618 
2619         assertEquals(jacob, table.getItems().get(0));
2620         assertEquals(isabella, table.getItems().get(1));
2621         assertEquals(ethan, table.getItems().get(2));
2622         assertEquals(emma, table.getItems().get(3));
2623         assertEquals(michael, table.getItems().get(4));
2624 
2625         // change sort order - expect items to be sorted
2626         table.getSortOrder().setAll(firstNameCol);
2627 
2628         assertEquals(jacob, table.getItems().get(3));
2629         assertEquals(isabella, table.getItems().get(2));
2630         assertEquals(ethan, table.getItems().get(1));
2631         assertEquals(emma, table.getItems().get(0));
2632         assertEquals(michael, table.getItems().get(4));
2633 
2634         // set new items into items list - expect sortOrder list to be reset
2635         // and the items list to remain unsorted
2636         table.setItems(FXCollections.observableArrayList(
2637                 jacob = new Person("Jacob", "Smith", "jacob.smith@example.com"),
2638                 isabella = new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
2639                 ethan = new Person("Ethan", "Williams", "ethan.williams@example.com"),
2640                 emma = new Person("Emma", "Jones", "emma.jones@example.com"),
2641                 michael = new Person("Michael", "Brown", "michael.brown@example.com")));
2642 
2643         assertEquals(jacob, table.getItems().get(0));
2644         assertEquals(isabella, table.getItems().get(1));
2645         assertEquals(ethan, table.getItems().get(2));
2646         assertEquals(emma, table.getItems().get(3));
2647         assertEquals(michael, table.getItems().get(4));
2648 
2649         assertTrue(table.getSortOrder().isEmpty());
2650     }
2651 
2652     @Test public void test_rt35763_sortedList() {
2653         TableView<Person> table = new TableView();
2654 
2655         TableColumn firstNameCol = new TableColumn("First Name");
2656         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
2657         table.getColumns().add(firstNameCol);
2658 
2659         Person jacob, isabella, ethan, emma, michael;
2660         SortedList<Person> sortedList = new SortedList<>(FXCollections.observableArrayList(
2661                 jacob = new Person("Jacob", "Smith", "jacob.smith@example.com"),
2662                 isabella = new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
2663                 ethan = new Person("Ethan", "Williams", "ethan.williams@example.com"),
2664                 emma = new Person("Emma", "Jones", "emma.jones@example.com"),
2665                 michael = new Person("Michael", "Brown", "michael.brown@example.com")));
2666         sortedList.comparatorProperty().bind(table.comparatorProperty());
2667         table.setItems(sortedList);
2668 
2669         assertEquals(jacob, table.getItems().get(0));
2670         assertEquals(isabella, table.getItems().get(1));
2671         assertEquals(ethan, table.getItems().get(2));
2672         assertEquals(emma, table.getItems().get(3));
2673         assertEquals(michael, table.getItems().get(4));
2674 
2675         // change sort order - expect items to be sorted
2676         table.getSortOrder().setAll(firstNameCol);
2677 
2678         assertEquals(jacob, table.getItems().get(3));
2679         assertEquals(isabella, table.getItems().get(2));
2680         assertEquals(ethan, table.getItems().get(1));
2681         assertEquals(emma, table.getItems().get(0));
2682         assertEquals(michael, table.getItems().get(4));
2683 
2684         // set new items into items list - expect sortOrder list to be retained
2685         // as we're inserting a SortedList
2686         SortedList<Person> sortedList2 = new SortedList<>(FXCollections.observableArrayList(
2687                 jacob = new Person("Jacob", "Smith", "jacob.smith@example.com"),
2688                 isabella = new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
2689                 ethan = new Person("Ethan", "Williams", "ethan.williams@example.com"),
2690                 emma = new Person("Emma", "Jones", "emma.jones@example.com"),
2691                 michael = new Person("Michael", "Brown", "michael.brown@example.com")));
2692         sortedList2.comparatorProperty().bind(table.comparatorProperty());
2693         table.setItems(sortedList2);
2694 
2695         assertEquals(jacob, table.getItems().get(3));
2696         assertEquals(isabella, table.getItems().get(2));
2697         assertEquals(ethan, table.getItems().get(1));
2698         assertEquals(emma, table.getItems().get(0));
2699         assertEquals(michael, table.getItems().get(4));
2700 
2701         assertEquals(1, table.getSortOrder().size());
2702         assertEquals(firstNameCol, table.getSortOrder().get(0));
2703     }
2704 
2705     @Test(expected = IndexOutOfBoundsException.class)
2706     public void test_rt35768_negativeFrom() {
2707         readOnlyUnbackedObservableListSubListTest(-1, 0);
2708     }
2709 
2710     @Test(expected = IndexOutOfBoundsException.class)
2711     public void test_rt35768_bigTo() {
2712         readOnlyUnbackedObservableListSubListTest(0, 10);
2713     }
2714 
2715     @Test(expected = IndexOutOfBoundsException.class)
2716     public void test_rt35768_fromEqualsTo() {
2717         readOnlyUnbackedObservableListSubListTest(1, 1);
2718     }
2719 
2720     private void readOnlyUnbackedObservableListSubListTest(int from, int to) {
2721         final SelectedCellsMap<TablePosition> selectedCellsMap = new SelectedCellsMap<TablePosition>(c -> { /* Do nothing */}) {
2722             @Override public boolean isCellSelectionEnabled() {
2723                 return false;
2724             }
2725         };
2726         ReadOnlyUnbackedObservableList<TablePosition<Object, ?>> selectedCellsSeq = new ReadOnlyUnbackedObservableList<TablePosition<Object, ?>>() {
2727             @Override public TablePosition<Object, ?> get(int i) {
2728                 return selectedCellsMap.get(i);
2729             }
2730 
2731             @Override public int size() {
2732                 return selectedCellsMap.size();
2733             }
2734         };
2735 
2736         // This should result in an IOOBE, but didn't until this bug was fixed
2737         selectedCellsSeq.subList(from, to);
2738     }
2739 
2740     @Test public void test_rt35857() {
2741         ObservableList<String> fxList = FXCollections.observableArrayList("A", "B", "C");
2742         final TableView<String> tableView = new TableView<String>(fxList);
2743 
2744         tableView.getSelectionModel().select(0);
2745 
2746         ObservableList<String> selectedItems = tableView.getSelectionModel().getSelectedItems();
2747         assertEquals(1, selectedItems.size());
2748         assertEquals("A", selectedItems.get(0));
2749 
2750         tableView.getItems().removeAll(selectedItems);
2751         assertEquals(2, fxList.size());
2752         assertEquals("B", fxList.get(0));
2753         assertEquals("C", fxList.get(1));
2754     }
2755 
2756     @Test public void test_getColumnHeaderForColumn() {
2757         TableView<Person> table = new TableView<>();
2758         table.setItems(FXCollections.observableArrayList(
2759                 new Person("John", "Smith", "jacob.smith@example.com")
2760         ));
2761 
2762         TableColumn<Person,String> first = new TableColumn<Person,String>("first");
2763         first.setCellValueFactory(new PropertyValueFactory("firstName"));
2764         TableColumn<Person,String> last = new TableColumn<Person,String>("last");
2765         first.setCellValueFactory(new PropertyValueFactory("lastName"));
2766 
2767         TableColumn name = new TableColumn("Name");
2768         name.getColumns().addAll(first, last);
2769 
2770         table.getColumns().setAll(name);
2771 
2772         StageLoader sl = new StageLoader(table);
2773 
2774         TableHeaderRow headerRow = VirtualFlowTestUtils.getTableHeaderRow(table);
2775 
2776         TableColumnHeader nameHeader = TableHeaderRowShim.getColumnHeaderFor(headerRow, name);
2777         TableColumnHeader firstHeader = TableHeaderRowShim.getColumnHeaderFor(headerRow, first);
2778         TableColumnHeader lastHeader = TableHeaderRowShim.getColumnHeaderFor(headerRow, last);
2779         assertNotNull(nameHeader);
2780         assertEquals(name, nameHeader.getTableColumn());
2781         assertNotNull(firstHeader);
2782         assertEquals(first, firstHeader.getTableColumn());
2783         assertNotNull(lastHeader);
2784         assertEquals(last, lastHeader.getTableColumn());
2785 
2786         sl.dispose();
2787     }
2788 
2789     @Test public void test_rt36220() {
2790         ObservableList<AtomicLong> tableItems = FXCollections.observableArrayList();
2791         tableItems.add(new AtomicLong(0L));
2792 
2793         TableView<AtomicLong> tableView = new TableView<>();
2794         tableView.getItems().setAll(tableItems);
2795 
2796         TableColumn<AtomicLong, String> col = new TableColumn<>();
2797         col.setCellValueFactory(obj -> new SimpleStringProperty(String.valueOf(obj.getValue().longValue())));
2798         col.setPrefWidth(180);
2799         tableView.getColumns().add(col);
2800 
2801         new StageLoader(tableView);
2802 
2803         VirtualFlowTestUtils.assertTableCellTextEquals(tableView, 0, 0, "0");
2804 
2805         // 1) using this trick will prevent the first update
2806         col.setMinWidth(col.getPrefWidth() + 1);
2807 
2808         long expected = System.currentTimeMillis();
2809         tableItems.get(0).set(expected);
2810         tableView.getItems().setAll(tableItems);
2811 
2812         Toolkit.getToolkit().firePulse();
2813 
2814         VirtualFlowTestUtils.assertTableCellTextEquals(tableView, 0, 0, ""+expected);
2815     }
2816 
2817     @Test public void test_rt36425() {
2818         TableView<String> tableView = new TableView<>();
2819 
2820         TableColumn<String, String> tableColumn = new TableColumn<>();
2821         tableColumn.setCellValueFactory(data -> new SimpleStringProperty(data.getValue()));
2822         tableColumn.setText("Test");
2823         tableView.getColumns().add(tableColumn);
2824 
2825         SimpleListProperty<String> data = new SimpleListProperty<>(FXCollections.observableArrayList());
2826         tableView.itemsProperty().bind(data);
2827         data.addAll("AAA", "BBB");
2828 
2829         assertEquals("AAA", data.get(0));
2830         assertEquals("BBB", data.get(1));
2831 
2832         tableView.getSortOrder().add(tableColumn);
2833 
2834         assertTrue(tableView.getSortOrder().contains(tableColumn));
2835         assertEquals("AAA", data.get(0));
2836         assertEquals("BBB", data.get(1));
2837 
2838         tableColumn.setSortType(TableColumn.SortType.DESCENDING);
2839         assertEquals("AAA", data.get(1));
2840         assertEquals("BBB", data.get(0));
2841     }
2842 
2843     private int test_rt_36353_selectedItemCount = 0;
2844     private int test_rt_36353_selectedIndexCount = 0;
2845     @Test public void test_rt36353() {
2846         ObservableList<String> data = FXCollections.observableArrayList();
2847         data.addAll("2", "1", "3");
2848         SortedList<String> sortedList = new SortedList<>(data);
2849 
2850         TableView<String> tableView = new TableView<>(sortedList);
2851         sortedList.comparatorProperty().bind(tableView.comparatorProperty());
2852 
2853         TableColumn<String, String> tableColumn = new TableColumn<>();
2854         tableColumn.setCellValueFactory(rowValue -> new SimpleStringProperty(rowValue.getValue()));
2855         tableColumn.setText("Test");
2856         tableView.getColumns().add(tableColumn);
2857 
2858         tableView.getSelectionModel().selectedItemProperty().addListener((e, oldSelection, newSelection) -> {
2859             test_rt_36353_selectedItemCount++;
2860         });
2861         tableView.getSelectionModel().selectedIndexProperty().addListener((e, oldIndex, newIndex) -> {
2862             test_rt_36353_selectedIndexCount++;
2863         });
2864 
2865         assertEquals(0, test_rt_36353_selectedItemCount);
2866         assertEquals(0, test_rt_36353_selectedIndexCount);
2867 
2868         tableView.getSelectionModel().select(1);
2869         assertEquals(1, test_rt_36353_selectedItemCount);
2870         assertEquals(1, test_rt_36353_selectedIndexCount);
2871         assertEquals("2", sortedList.get(0));
2872         assertEquals("1", sortedList.get(1));
2873         assertEquals("3", sortedList.get(2));
2874 
2875         tableView.getSortOrder().add(tableColumn);
2876         assertEquals(1, test_rt_36353_selectedItemCount);
2877         assertEquals(2, test_rt_36353_selectedIndexCount);
2878         assertEquals("1", sortedList.get(0));
2879         assertEquals("2", sortedList.get(1));
2880         assertEquals("3", sortedList.get(2));
2881 
2882         tableColumn.setSortType(TableColumn.SortType.DESCENDING);
2883         assertEquals(1, test_rt_36353_selectedItemCount);
2884         assertEquals(3, test_rt_36353_selectedIndexCount);
2885         assertEquals("3", sortedList.get(0));
2886         assertEquals("2", sortedList.get(1));
2887         assertEquals("1", sortedList.get(2));
2888 
2889         tableView.getSortOrder().remove(tableColumn);
2890         assertEquals(1, test_rt_36353_selectedItemCount);
2891         assertEquals(4, test_rt_36353_selectedIndexCount);
2892         assertEquals("2", sortedList.get(0));
2893         assertEquals("1", sortedList.get(1));
2894         assertEquals("3", sortedList.get(2));
2895     }
2896 
2897     // This test ensures that we reuse column headers when the columns still
2898     // exist after a change to the columns list - rather than recreating new
2899     // column headers. The issue in RT-36290 was that we were creating new column
2900     // headers that were then in their initial states, allowing them to call
2901     // TableColumnHeader#updateScene(), which would resize the column based on the
2902     // data within it.
2903     @Test public void test_rt36290() {
2904         TableView<String> tableView = new TableView<>();
2905 
2906         TableColumn<String, String> tableColumn1 = new TableColumn<>();
2907         tableColumn1.setCellValueFactory(data -> new SimpleStringProperty(data.getValue()));
2908         tableColumn1.setText("Test1");
2909 
2910         TableColumn<String, String> tableColumn2 = new TableColumn<>();
2911         tableColumn2.setCellValueFactory(data -> new SimpleStringProperty(data.getValue()));
2912         tableColumn2.setText("Test2");
2913 
2914         tableView.getColumns().setAll(tableColumn1, tableColumn2);
2915 
2916         StageLoader sl = new StageLoader(tableView);
2917 
2918         final TableColumnHeader header1 = VirtualFlowTestUtils.getTableColumnHeader(tableView, tableColumn1);
2919         final TableColumnHeader header2 = VirtualFlowTestUtils.getTableColumnHeader(tableView, tableColumn2);
2920 
2921         tableView.getColumns().setAll(tableColumn2, tableColumn1);
2922         Toolkit.getToolkit().firePulse();
2923 
2924         final TableColumnHeader header1_after = VirtualFlowTestUtils.getTableColumnHeader(tableView, tableColumn1);
2925         final TableColumnHeader header2_after = VirtualFlowTestUtils.getTableColumnHeader(tableView, tableColumn2);
2926 
2927         assertEquals(header1, header1_after);
2928         assertEquals(header2, header2_after);
2929 
2930         sl.dispose();
2931     }
2932 
2933     @Test public void test_rt25679_rowSelection() {
2934         test_rt25679(true);
2935     }
2936 
2937     @Test public void test_rt25679_cellSelection() {
2938         test_rt25679(false);
2939     }
2940 
2941     private void test_rt25679(boolean rowSelection) {
2942         Button focusBtn = new Button("Focus here");
2943 
2944         TableView<String> tableView = new TableView<>(FXCollections.observableArrayList("A", "B", "C"));
2945 
2946         TableColumn<String, String> tableColumn = new TableColumn<>();
2947         tableColumn.setCellValueFactory(rowValue -> new SimpleStringProperty(rowValue.getValue()));
2948         tableView.getColumns().add(tableColumn);
2949         TableView.TableViewSelectionModel<String> sm = tableView.getSelectionModel();
2950         sm.setCellSelectionEnabled(! rowSelection);
2951 
2952         VBox vbox = new VBox(focusBtn, tableView);
2953 
2954         StageLoader sl = new StageLoader(vbox);
2955         sl.getStage().requestFocus();
2956         focusBtn.requestFocus();
2957         Toolkit.getToolkit().firePulse();
2958 
2959         // test initial state
2960         assertEquals(sl.getStage().getScene().getFocusOwner(), focusBtn);
2961         assertTrue(focusBtn.isFocused());
2962         assertEquals(-1, sm.getSelectedIndex());
2963         assertNull(sm.getSelectedItem());
2964 
2965         // move focus to the tableView
2966         tableView.requestFocus();
2967 
2968         // ensure that there is a selection (where previously there was not one)
2969         assertEquals(sl.getStage().getScene().getFocusOwner(), tableView);
2970         assertTrue(tableView.isFocused());
2971 
2972         if (rowSelection) {
2973             assertEquals(0, sm.getSelectedIndices().size());
2974             assertNull(sm.getSelectedItem());
2975             assertFalse(sm.isSelected(0));
2976             assertEquals(0, sm.getSelectedCells().size());
2977         } else {
2978             assertFalse(sm.isSelected(0, tableColumn));
2979             assertEquals(0, sm.getSelectedCells().size());
2980         }
2981 
2982         sl.dispose();
2983     }
2984 
2985     private int rt36556_instanceCount;
2986     @Test public void test_rt36556_scrollTo() {
2987         rt36556_instanceCount = 0;
2988 
2989         TableView<String> tableView = new TableView<>();
2990         tableView.setRowFactory(new Callback<TableView<String>, TableRow<String>>() {
2991             @Override public TableRow<String> call(TableView<String> param) {
2992                 rt36556_instanceCount++;
2993                 return new TableRow<>();
2994             }
2995         });
2996 
2997         TableColumn<String, String> tableColumn = new TableColumn<>();
2998         tableColumn.setCellValueFactory(rowValue -> new SimpleStringProperty(rowValue.getValue()));
2999 
3000         tableView.getColumns().add(tableColumn);
3001 
3002         for (int i = 0; i < 1000; i++) {
3003             tableView.getItems().add("Row " + i);
3004         }
3005 
3006         StackPane root = new StackPane();
3007         root.getChildren().add(tableView);
3008 
3009         StageLoader sl = new StageLoader(root);
3010 
3011         final int cellCountAtStart = rt36556_instanceCount;
3012 
3013         // start scrolling
3014         for (int i = 0; i < 1000; i++) {
3015             tableView.scrollTo(i);
3016 //            Toolkit.getToolkit().firePulse();
3017         }
3018 
3019         assertEquals(cellCountAtStart, rt36556_instanceCount);
3020         sl.dispose();
3021     }
3022 
3023     @Test public void test_rt36556_mouseWheel() {
3024         rt36556_instanceCount = 0;
3025 
3026         TableView<String> tableView = new TableView<>();
3027         tableView.setRowFactory(new Callback<TableView<String>, TableRow<String>>() {
3028             @Override public TableRow<String> call(TableView<String> param) {
3029                 rt36556_instanceCount++;
3030                 return new TableRow<String>();
3031             }
3032         });
3033 
3034         TableColumn<String, String> tableColumn = new TableColumn<>();
3035         tableColumn.setCellValueFactory(rowValue -> new SimpleStringProperty(rowValue.getValue()));
3036         tableView.getColumns().add(tableColumn);
3037 
3038         for (int i = 0; i < 1000; i++) {
3039             tableView.getItems().add("Row " + i);
3040         }
3041 
3042         StackPane root = new StackPane();
3043         root.getChildren().add(tableView);
3044 
3045         StageLoader sl = new StageLoader(root);
3046 
3047         final int cellCountAtStart = rt36556_instanceCount;
3048 
3049         // start scrolling - we call VirtualFlow.adjustPixels, which is what
3050         // is called when the mouse wheel is scrolled
3051         VirtualFlow flow = VirtualFlowTestUtils.getVirtualFlow(tableView);
3052         flow.scrollPixels(1000 * 24);
3053 
3054         assertEquals(cellCountAtStart, rt36556_instanceCount);
3055 
3056         sl.dispose();
3057     }
3058 
3059     @Test public void test_rt_36656_removeFromSortOrder() {
3060         test_rt_36656(true, false, false);
3061     }
3062 
3063     @Test public void test_rt_36656_removeFromColumns() {
3064         test_rt_36656(false, true, false);
3065     }
3066 
3067     @Test public void test_rt_36656_setInvisible() {
3068         test_rt_36656(false, false, true);
3069     }
3070 
3071     private void test_rt_36656(boolean removeFromSortOrder, boolean removeFromColumns, boolean setInvisible) {
3072         TableView<Person> table = new TableView<Person>();
3073         table.setItems(personTestData);
3074 
3075         TableColumn firstNameCol = new TableColumn("First Name");
3076         firstNameCol.setMinWidth(100);
3077         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
3078 
3079         TableColumn lastNameCol = new TableColumn("Last Name");
3080         lastNameCol.setMinWidth(100);
3081         lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
3082 
3083         TableColumn emailCol = new TableColumn("Email");
3084         emailCol.setMinWidth(200);
3085         emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));
3086 
3087         table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
3088 
3089         new StageLoader(table);
3090 
3091         TableColumnHeader firstNameColHeader = VirtualFlowTestUtils.getTableColumnHeader(table, firstNameCol);
3092         TableColumnHeader lastNameColHeader = VirtualFlowTestUtils.getTableColumnHeader(table, lastNameCol);
3093         TableColumnHeader emailColHeader = VirtualFlowTestUtils.getTableColumnHeader(table, emailCol);
3094 
3095         // test initial state
3096         assertEquals(-1, TableColumnHeaderShim.getSortPos(firstNameColHeader));
3097         assertEquals(-1, TableColumnHeaderShim.getSortPos(lastNameColHeader));
3098         assertEquals(-1, TableColumnHeaderShim.getSortPos(emailColHeader));
3099 
3100         // set an order including all columns
3101         table.getSortOrder().addAll(firstNameCol, lastNameCol, emailCol);
3102         assertEquals(0, TableColumnHeaderShim.getSortPos(firstNameColHeader));
3103         assertEquals(1, TableColumnHeaderShim.getSortPos(lastNameColHeader));
3104         assertEquals(2, TableColumnHeaderShim.getSortPos(emailColHeader));
3105 
3106         if (removeFromSortOrder) {
3107             // Remove lastNameCol from the table sortOrder list, so this column
3108             // is no longer part of the sort comparator
3109             table.getSortOrder().remove(lastNameCol);
3110         } else if (removeFromColumns) {
3111             // Remove lastNameCol from the table entirely.
3112             table.getColumns().remove(lastNameCol);
3113         } else if (setInvisible) {
3114             // Hide the lastNameColumn.
3115             lastNameCol.setVisible(false);
3116         }
3117 
3118         // Regardless of action taken above, expect lastNameCol sortPos to be -1
3119         // and emailCol sortPos to shift from 2 to 1.
3120         assertEquals(0, TableColumnHeaderShim.getSortPos(firstNameColHeader));
3121         assertEquals(-1, TableColumnHeaderShim.getSortPos(lastNameColHeader));
3122         assertEquals(1, TableColumnHeaderShim.getSortPos(emailColHeader));
3123     }
3124 
3125     @Test public void test_rt_36670() {
3126         final ObservableList<Person> data = FXCollections.observableArrayList(
3127                         new Person("Jacob", "Smith", "jacob.smith@example.com", true),
3128                         new Person("Isabella", "Johnson", "isabella.johnson@example.com", false),
3129                         new Person("Ethan", "Williams", "ethan.williams@example.com", true),
3130                         new Person("Emma", "Jones", "emma.jones@example.com", true),
3131                         new Person("Michael", "Brown", "michael.brown@example.com", false));
3132 
3133         TableColumn invitedCol = new TableColumn<>();
3134         invitedCol.setText("Invited");
3135         invitedCol.setMinWidth(70);
3136         invitedCol.setCellValueFactory(new PropertyValueFactory("invited"));
3137         invitedCol.setCellFactory(CheckBoxTableCell.forTableColumn(invitedCol));
3138 
3139         TableColumn firstNameCol = new TableColumn();
3140         firstNameCol.setText("First");
3141         firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
3142 
3143         TableView tableView = new TableView(data);
3144         tableView.getColumns().addAll(invitedCol, firstNameCol);
3145 
3146         StageLoader sl = new StageLoader(tableView);
3147 
3148         // get the checkboxes
3149         CheckBox row0CheckBox = (CheckBox) VirtualFlowTestUtils.getCell(tableView, 0, 0).getGraphic();
3150         CheckBox row1CheckBox = (CheckBox) VirtualFlowTestUtils.getCell(tableView, 1, 0).getGraphic();
3151         CheckBox row2CheckBox = (CheckBox) VirtualFlowTestUtils.getCell(tableView, 2, 0).getGraphic();
3152         CheckBox row3CheckBox = (CheckBox) VirtualFlowTestUtils.getCell(tableView, 3, 0).getGraphic();
3153         CheckBox row4CheckBox = (CheckBox) VirtualFlowTestUtils.getCell(tableView, 4, 0).getGraphic();
3154 
3155         // check initial state of all checkboxes
3156         assertTrue(row0CheckBox.isSelected());
3157         assertFalse(row1CheckBox.isSelected());
3158         assertTrue(row2CheckBox.isSelected());
3159         assertTrue(row3CheckBox.isSelected());
3160         assertFalse(row4CheckBox.isSelected());
3161 
3162         // sort the table based on the invited column
3163         tableView.getSortOrder().add(invitedCol);
3164         Toolkit.getToolkit().firePulse();
3165 
3166         // The sort order has changed, with unselected items at the top and
3167         // selected items beneath them.
3168         assertFalse(row0CheckBox.isSelected());
3169         assertFalse(row1CheckBox.isSelected());
3170         assertTrue(row2CheckBox.isSelected());
3171         assertTrue(row3CheckBox.isSelected());
3172         assertTrue(row4CheckBox.isSelected());
3173 
3174         // now, select the 'Michael' row, which is row 1
3175         row1CheckBox.setSelected(true);
3176         Toolkit.getToolkit().firePulse();
3177 
3178         // only the Michael row should have changed state - but the bug
3179         // identified in RT-36670 shows that row 0 is also selected
3180         assertFalse(row0CheckBox.isSelected());
3181         assertTrue(row1CheckBox.isSelected());
3182         assertTrue(row2CheckBox.isSelected());
3183         assertTrue(row3CheckBox.isSelected());
3184         assertTrue(row4CheckBox.isSelected());
3185 
3186         sl.dispose();
3187     }
3188 
3189     @Test public void test_rt_36669() {
3190         TableView<Person> table = new TableView<>(personTestData);
3191 
3192         TableColumn firstNameCol = new TableColumn("First Name");
3193         firstNameCol.setMinWidth(100);
3194         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
3195 
3196         TableColumn lastNameCol = new TableColumn("Last Name");
3197         lastNameCol.setMinWidth(100);
3198         lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
3199 
3200         TableColumn emailCol = new TableColumn("Email");
3201         emailCol.setMinWidth(200);
3202         emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));
3203 
3204         table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
3205         table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
3206 
3207         StageLoader sl = new StageLoader(table);
3208 
3209         ScrollBar vbar = VirtualFlowTestUtils.getVirtualFlowVerticalScrollbar(table);
3210         ScrollBar hbar = VirtualFlowTestUtils.getVirtualFlowHorizontalScrollbar(table);
3211 
3212         // firstly test case where the TableView is tall enough to not need a vbar
3213         assertFalse(vbar.isVisible());
3214         assertFalse(hbar.isVisible());
3215 
3216         // now make the table quite narrow and ensure that even if a vbar appears
3217         // that the hbar does not appear
3218         table.setMaxHeight(30);
3219         Toolkit.getToolkit().firePulse();
3220         assertTrue(vbar.isVisible());
3221         assertFalse(hbar.isVisible());
3222 
3223         sl.dispose();
3224     }
3225 
3226     private int rt_37061_index_counter = 0;
3227     private int rt_37061_item_counter = 0;
3228     @Test public void test_rt_37061() {
3229         TableView<Integer> tv = new TableView<>();
3230         tv.getItems().add(1);
3231         tv.getSelectionModel().select(0);
3232 
3233         // note we add the listeners after the selection is made, so the counters
3234         // at this point are still both at zero.
3235         tv.getSelectionModel().selectedIndexProperty().addListener((observable, oldValue, newValue) -> {
3236             rt_37061_index_counter++;
3237         });
3238 
3239         tv.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
3240             rt_37061_item_counter++;
3241         });
3242 
3243         // add a new item. This does not impact the selected index or selected item
3244         // so the counters should remain at zero.
3245         tv.getItems().add(2);
3246         assertEquals(0, rt_37061_index_counter);
3247         assertEquals(0, rt_37061_item_counter);
3248     }
3249 
3250     @Test public void test_rt_37058_noContent() {
3251         test_rt_37058(false);
3252     }
3253 
3254     @Test public void test_rt_37058_withContent() {
3255         test_rt_37058(true);
3256     }
3257 
3258     private void test_rt_37058(boolean hasContent) {
3259         // create table with a bunch of column and no rows...
3260         TableView<Integer> table = new TableView<>();
3261         TableColumn<Integer, Integer> column = new TableColumn<>("Column");
3262         table.getColumns().add(column);
3263         column.setPrefWidth(150);
3264 
3265         if (hasContent) {
3266             table.getItems().add(1);
3267         }
3268 
3269         StageLoader sl = new StageLoader(table);
3270         Toolkit.getToolkit().firePulse();
3271 
3272         assertEquals(150, column.getWidth(), 0.0);
3273 
3274         sl.dispose();
3275     }
3276 
3277     @Test public void test_rt_37057_test1_MoveColumn() {
3278         // create table with a bunch of column and no rows...
3279         TableView<Integer> table = new TableView<>();
3280         for ( int i = 1; i <= 10; i++ ) {
3281             TableColumn<Integer, Integer> column = new TableColumn<>("" + i);
3282             table.getColumns().add( column );
3283 
3284             // sneak some hidden columns in there
3285             column = new TableColumn<>("h" + i);
3286             column.setVisible( false );
3287             table.getColumns().add( column );
3288         }
3289 
3290         StageLoader sl = new StageLoader(table);
3291 
3292         TableColumn column1 = table.getVisibleLeafColumn(0);
3293         TableColumn column2 = table.getVisibleLeafColumn(1);
3294 
3295         // get the headers of a few columns
3296         TableColumnHeader header1 = VirtualFlowTestUtils.getTableColumnHeader(table, column1);
3297         TableColumnHeader header2 = VirtualFlowTestUtils.getTableColumnHeader(table, column2);
3298 
3299         assertEquals(0, TableColumnHeaderShim.getColumnIndex(header1));
3300         assertEquals(1, TableColumnHeaderShim.getColumnIndex(header2));
3301 
3302         // move as per first instructions in RT-37057. Note that the moveColumn
3303         // positions seem counter-intuitive. I got these numbers by printing
3304         // the positions when in a manual test run (using the test script in
3305         // RT-37057).
3306 
3307         // Drag column 1 to slot 1. As expected, the column position doesn't change.
3308         TableColumnHeaderUtil.moveColumn(column1, 0);
3309         assertEquals(column1, table.getVisibleLeafColumn(0));
3310         assertEquals(column2, table.getVisibleLeafColumn(1));
3311         assertEquals(0, TableColumnHeaderShim.getColumnIndex(header1));
3312         assertEquals(1, TableColumnHeaderShim.getColumnIndex(header2));
3313 
3314         // Drag column 1 to slot 2. As expected, the column 1 and 2 swap positions.
3315         TableColumnHeaderUtil.moveColumn(column1, 1);
3316         assertEquals(column2, table.getVisibleLeafColumn(0));
3317         assertEquals(column1, table.getVisibleLeafColumn(1));
3318         assertEquals(1, TableColumnHeaderShim.getColumnIndex(header1));
3319         assertEquals(0, TableColumnHeaderShim.getColumnIndex(header2));
3320 
3321         // Drag column 1 to slot 0. As expected, the column 1 and 2 swap positions.
3322         TableColumnHeaderUtil.moveColumn(column1, 0);
3323         assertEquals(column1, table.getVisibleLeafColumn(0));
3324         assertEquals(column2, table.getVisibleLeafColumn(1));
3325         assertEquals(0, TableColumnHeaderShim.getColumnIndex(header1));
3326         assertEquals(1, TableColumnHeaderShim.getColumnIndex(header2));
3327 
3328         // Drag column 1 to slot 1 again. What? Why did they swap positions this time?
3329         TableColumnHeaderUtil.moveColumn(column1, 0);
3330         assertEquals(column1, table.getVisibleLeafColumn(0));
3331         assertEquals(column2, table.getVisibleLeafColumn(1));
3332         assertEquals(0, TableColumnHeaderShim.getColumnIndex(header1));
3333         assertEquals(1, TableColumnHeaderShim.getColumnIndex(header2));
3334 
3335         sl.dispose();
3336     }
3337 
3338     @Test public void test_rt_37057_test1_MouseEvents() {
3339         // create table with a bunch of column and no rows...
3340         TableView<Integer> table = new TableView<>();
3341         for ( int i = 1; i <= 10; i++ ) {
3342             TableColumn<Integer, Integer> column = new TableColumn<>("" + i);
3343             table.getColumns().add( column );
3344 
3345             // sneak some hidden columns in there
3346             column = new TableColumn<>("h" + i);
3347             column.setVisible( false );
3348             TableColumnBaseHelper.setWidth(column, 50);
3349             column.setResizable(false);
3350             table.getColumns().add( column );
3351         }
3352 
3353         StageLoader sl = new StageLoader(table);
3354 
3355         TableColumn column1 = table.getVisibleLeafColumn(0);
3356         TableColumn column2 = table.getVisibleLeafColumn(1);
3357 
3358         // get the headers of a few columns
3359         TableColumnHeader header1 = VirtualFlowTestUtils.getTableColumnHeader(table, column1);
3360         TableColumnHeader header2 = VirtualFlowTestUtils.getTableColumnHeader(table, column2);
3361 
3362         assertEquals(0, TableColumnHeaderShim.getColumnIndex(header1));
3363         assertEquals(1, TableColumnHeaderShim.getColumnIndex(header2));
3364 
3365         // move as per first instructions in RT-37057. The dragOffset and sceneX
3366         // values passed into moveColumn have been derived from a manual run of
3367         // the test application attached to RT-37057 with debug output printed
3368         // in TableColumnHeader
3369 
3370         // Drag column 1 to slot 1. As expected, the column position doesn't change.
3371         TableColumnHeaderUtil.moveColumn(column1, 9, 61);
3372         assertEquals(column1, table.getVisibleLeafColumn(0));
3373         assertEquals(column2, table.getVisibleLeafColumn(1));
3374         assertEquals(0, TableColumnHeaderShim.getColumnIndex(header1));
3375         assertEquals(1, TableColumnHeaderShim.getColumnIndex(header2));
3376 
3377         // Drag column 1 to slot 2. As expected, the column 1 and 2 swap positions.
3378         TableColumnHeaderUtil.moveColumn(column1, 12, 139);
3379         assertEquals(column2, table.getVisibleLeafColumn(0));
3380         assertEquals(column1, table.getVisibleLeafColumn(1));
3381         assertEquals(1, TableColumnHeaderShim.getColumnIndex(header1));
3382         assertEquals(0, TableColumnHeaderShim.getColumnIndex(header2));
3383 
3384         // Drag column 1 to slot 0. As expected, the column 1 and 2 swap positions.
3385         TableColumnHeaderUtil.moveColumn(column1, 45, 21);
3386         assertEquals(column1, table.getVisibleLeafColumn(0));
3387         assertEquals(column2, table.getVisibleLeafColumn(1));
3388         assertEquals(0, TableColumnHeaderShim.getColumnIndex(header1));
3389         assertEquals(1, TableColumnHeaderShim.getColumnIndex(header2));
3390 
3391         // Drag column 1 to slot 1 again. What? Why did they swap positions this time?
3392         TableColumnHeaderUtil.moveColumn(column1, 19, 63);
3393         assertEquals(column1, table.getVisibleLeafColumn(0));
3394         assertEquals(column2, table.getVisibleLeafColumn(1));
3395         assertEquals(0, TableColumnHeaderShim.getColumnIndex(header1));
3396         assertEquals(1, TableColumnHeaderShim.getColumnIndex(header2));
3397 
3398         sl.dispose();
3399     }
3400 
3401     @Test public void test_rt_37057_test2_MoveColumn() {
3402         // create table with a bunch of column and no rows...
3403         TableView<Integer> table = new TableView<>();
3404         for ( int i = 1; i <= 10; i++ ) {
3405             TableColumn<Integer, Integer> column = new TableColumn<>("" + i);
3406             table.getColumns().add( column );
3407 
3408             // sneak some hidden columns in there
3409             column = new TableColumn<>("h" + i);
3410             column.setVisible( false );
3411             table.getColumns().add( column );
3412         }
3413 
3414         StageLoader sl = new StageLoader(table);
3415 
3416         TableColumn column1 = table.getVisibleLeafColumn(0);
3417         TableColumn column2 = table.getVisibleLeafColumn(1);
3418         TableColumn column4 = table.getVisibleLeafColumn(3);
3419 
3420         // get the headers of a few columns
3421         TableColumnHeader header1 = VirtualFlowTestUtils.getTableColumnHeader(table, column1);
3422         TableColumnHeader header2 = VirtualFlowTestUtils.getTableColumnHeader(table, column2);
3423         TableColumnHeader header4 = VirtualFlowTestUtils.getTableColumnHeader(table, column4);
3424 
3425         assertEquals(0, TableColumnHeaderShim.getColumnIndex(header1));
3426         assertEquals(1, TableColumnHeaderShim.getColumnIndex(header2));
3427 
3428         // move as per second instructions in RT-37057. Note that the moveColumn
3429         // positions seem counter-intuitive. I got these numbers by printing
3430         // the positions when in a manual test run (using the test script in
3431         // RT-37057).
3432 
3433         // Drag column 1 to slot 2. As expected, the column 1 and 2 swap positions
3434         TableColumnHeaderUtil.moveColumn(column1, 1);
3435         assertEquals(column2, table.getVisibleLeafColumn(0));
3436         assertEquals(column1, table.getVisibleLeafColumn(1));
3437         assertEquals(1, TableColumnHeaderShim.getColumnIndex(header1));
3438         assertEquals(0, TableColumnHeaderShim.getColumnIndex(header2));
3439 
3440         // Drag column 1 to slot 0. As expected, the column 1 and 2 swap positions.
3441         TableColumnHeaderUtil.moveColumn(column1, 0);
3442         assertEquals(column1, table.getVisibleLeafColumn(0));
3443         assertEquals(column2, table.getVisibleLeafColumn(1));
3444         assertEquals(0, TableColumnHeaderShim.getColumnIndex(header1));
3445         assertEquals(1, TableColumnHeaderShim.getColumnIndex(header2));
3446 
3447         // Drag column 4 to slot 1. What? It behaves like it was dragged to slot 0?!
3448         TableColumnHeaderUtil.moveColumn(column4, 1);
3449         assertEquals(column1, table.getVisibleLeafColumn(0));
3450         assertEquals(column4, table.getVisibleLeafColumn(1));
3451         assertEquals(column2, table.getVisibleLeafColumn(2));
3452         assertEquals(0, TableColumnHeaderShim.getColumnIndex(header1));
3453         assertEquals(1, TableColumnHeaderShim.getColumnIndex(header4));
3454         assertEquals(2, TableColumnHeaderShim.getColumnIndex(header2));
3455 
3456         sl.dispose();
3457     }
3458 
3459     @Test public void test_rt_37057_test2_MouseEvents() {
3460         // create table with a bunch of column and no rows...
3461         TableView<Integer> table = new TableView<>();
3462         for ( int i = 1; i <= 10; i++ ) {
3463             TableColumn<Integer, Integer> column = new TableColumn<>("" + i);
3464             table.getColumns().add( column );
3465 
3466             // sneak some hidden columns in there
3467             column = new TableColumn<>("h" + i);
3468             column.setVisible( false );
3469             TableColumnBaseHelper.setWidth(column, 50);
3470             column.setResizable(false);
3471             table.getColumns().add( column );
3472         }
3473 
3474         StageLoader sl = new StageLoader(table);
3475 
3476         TableColumn column1 = table.getVisibleLeafColumn(0);
3477         TableColumn column2 = table.getVisibleLeafColumn(1);
3478         TableColumn column4 = table.getVisibleLeafColumn(3);
3479 
3480         // get the headers of a few columns
3481         TableColumnHeader header1 = VirtualFlowTestUtils.getTableColumnHeader(table, column1);
3482         TableColumnHeader header2 = VirtualFlowTestUtils.getTableColumnHeader(table, column2);
3483         TableColumnHeader header4 = VirtualFlowTestUtils.getTableColumnHeader(table, column4);
3484 
3485         assertEquals(0, TableColumnHeaderShim.getColumnIndex(header1));
3486         assertEquals(1, TableColumnHeaderShim.getColumnIndex(header2));
3487 
3488         // move as per second instructions in RT-37057. The dragOffset and sceneX
3489         // values passed into moveColumn have been derived from a manual run of
3490         // the test application attached to RT-37057 with debug output printed
3491         // in TableColumnHeader
3492 
3493         // Drag column 1 to slot 2. As expected, the column 1 and 2 swap positions
3494         TableColumnHeaderUtil.moveColumn(column1, 25, 136);
3495         assertEquals(column2, table.getVisibleLeafColumn(0));
3496         assertEquals(column1, table.getVisibleLeafColumn(1));
3497         assertEquals(1, TableColumnHeaderShim.getColumnIndex(header1));
3498         assertEquals(0, TableColumnHeaderShim.getColumnIndex(header2));
3499 
3500         // Drag column 1 to slot 0. As expected, the column 1 and 2 swap positions.
3501         TableColumnHeaderUtil.moveColumn(column1, 51, 23);
3502         assertEquals(column1, table.getVisibleLeafColumn(0));
3503         assertEquals(column2, table.getVisibleLeafColumn(1));
3504         assertEquals(0, TableColumnHeaderShim.getColumnIndex(header1));
3505         assertEquals(1, TableColumnHeaderShim.getColumnIndex(header2));
3506 
3507         // Drag column 4 to slot 1. What? It behaves like it was dragged to slot 0?!
3508         TableColumnHeaderUtil.moveColumn(column4, 56, 103);
3509         assertEquals(column1, table.getVisibleLeafColumn(0));
3510         assertEquals(column4, table.getVisibleLeafColumn(1));
3511         assertEquals(column2, table.getVisibleLeafColumn(2));
3512         assertEquals(0, TableColumnHeaderShim.getColumnIndex(header1));
3513         assertEquals(1, TableColumnHeaderShim.getColumnIndex(header4));
3514         assertEquals(2, TableColumnHeaderShim.getColumnIndex(header2));
3515 
3516         sl.dispose();
3517     }
3518 
3519     @Test public void test_rt_37054_noScroll() {
3520         test_rt_37054(false);
3521     }
3522 
3523     @Test public void test_rt_37054_scroll() {
3524         test_rt_37054(true);
3525     }
3526 
3527     private void test_rt_37054(boolean scroll) {
3528         ObjectProperty<Integer> offset = new SimpleObjectProperty<Integer>(0);
3529 
3530         // create table with a bunch of rows and 1 column...
3531         TableView<Integer> table = new TableView<>();
3532         for ( int i = 1; i <= 50; i++ ) {
3533             table.getItems().add(i);
3534         }
3535         final TableColumn<Integer, Integer> column = new TableColumn<>("Column");
3536         table.getColumns().add( column );
3537         column.setPrefWidth( 150 );
3538 
3539         // each cell displays x, where x = "cell row number + offset"
3540         column.setCellValueFactory( cdf -> new ObjectBinding<Integer>() {
3541             { super.bind( offset ); }
3542 
3543             @Override protected Integer computeValue() {
3544                 return cdf.getValue() + offset.get();
3545             }
3546         });
3547 
3548         StackPane root = new StackPane();
3549         root.getChildren().add( table );
3550 
3551         StageLoader sl = new StageLoader(root);
3552 
3553         int index = scroll ? 0 : 25;
3554 
3555         if (scroll) {
3556             // we scroll to force the table cells to update the objects they observe
3557             table.scrollTo(index);
3558             Toolkit.getToolkit().firePulse();
3559         }
3560 
3561         TableCell cell = (TableCell) VirtualFlowTestUtils.getCell(table, index + 3, 0);
3562         final int initialValue = (Integer) cell.getItem();
3563 
3564         // increment the offset value
3565         offset.setValue(offset.get() + 1);
3566         Toolkit.getToolkit().firePulse();
3567 
3568         final int incrementedValue = (Integer) cell.getItem();
3569         assertEquals(initialValue + 1, incrementedValue);
3570 
3571         sl.dispose();
3572     }
3573 
3574     @Test public void test_rt_37429() {
3575         // get the current exception handler before replacing with our own,
3576         // as ListListenerHelp intercepts the exception otherwise
3577         final Thread.UncaughtExceptionHandler exceptionHandler = Thread.currentThread().getUncaughtExceptionHandler();
3578         Thread.currentThread().setUncaughtExceptionHandler((t, e) -> fail("We don't expect any exceptions in this test!"));
3579 
3580         // table columns - 1 column; name
3581         TableColumn<String, String> nameColumn = new TableColumn<>("name");
3582         nameColumn.setCellValueFactory(param -> new ReadOnlyObjectWrapper(param.getValue()));
3583         nameColumn.setPrefWidth(200);
3584 
3585         // table
3586         TableView<String> table = new TableView<>();
3587         table.setItems(FXCollections.observableArrayList("one", "two", "three", "four", "five"));
3588         table.getColumns().addAll(nameColumn);
3589 
3590         table.getSelectionModel().getSelectedItems().addListener((ListChangeListener<String>) c -> {
3591             while (c.next()) {
3592                 if(c.wasRemoved()) {
3593                     // The removed list of items must be iterated or the AIOOBE will
3594                     // not be thrown when getAddedSubList is called.
3595                     c.getRemoved().forEach(item -> {});
3596                 }
3597 
3598                 if (c.wasAdded()) {
3599                     c.getAddedSubList();
3600                 }
3601             }
3602         });
3603 
3604         StageLoader sl = new StageLoader(table);
3605 
3606         table.getSelectionModel().select(0);
3607         table.getSortOrder().add(nameColumn);
3608 
3609         sl.dispose();
3610 
3611         // reset the exception handler
3612         Thread.currentThread().setUncaughtExceptionHandler(exceptionHandler);
3613     }
3614 
3615     private int rt_37429_items_change_count = 0;
3616     private int rt_37429_cells_change_count = 0;
3617     @Test public void test_rt_37429_sortEventsShouldNotFireExtraChangeEvents() {
3618         // table columns - 1 column; name
3619         TableColumn<String, String> nameColumn = new TableColumn<>("name");
3620         nameColumn.setCellValueFactory(param -> new ReadOnlyObjectWrapper(param.getValue()));
3621         nameColumn.setPrefWidth(200);
3622 
3623         // table
3624         TableView<String> table = new TableView<>();
3625         table.setItems(FXCollections.observableArrayList("a", "c", "b"));
3626         table.getColumns().addAll(nameColumn);
3627 
3628         table.getSelectionModel().getSelectedItems().addListener((ListChangeListener<String>) c -> {
3629             while (c.next()) {
3630                 rt_37429_items_change_count++;
3631             }
3632         });
3633         table.getSelectionModel().getSelectedCells().addListener((ListChangeListener<TablePosition>) c -> {
3634             while (c.next()) {
3635                 rt_37429_cells_change_count++;
3636             }
3637         });
3638 
3639         StageLoader sl = new StageLoader(table);
3640 
3641         assertEquals(0, rt_37429_items_change_count);
3642         assertEquals(0, rt_37429_cells_change_count);
3643 
3644         table.getSelectionModel().select(0);
3645         assertEquals(1, rt_37429_items_change_count);
3646         assertEquals(1, rt_37429_cells_change_count);
3647 
3648         table.getSortOrder().add(nameColumn);
3649         assertEquals(1, rt_37429_items_change_count);
3650         assertEquals(1, rt_37429_cells_change_count);
3651 
3652         nameColumn.setSortType(TableColumn.SortType.DESCENDING);
3653         assertEquals(1, rt_37429_items_change_count);
3654         assertEquals(2, rt_37429_cells_change_count);
3655 
3656         nameColumn.setSortType(TableColumn.SortType.ASCENDING);
3657         assertEquals(1, rt_37429_items_change_count);
3658         assertEquals(3, rt_37429_cells_change_count);
3659 
3660         sl.dispose();
3661     }
3662 
3663     private int rt_37538_count = 0;
3664     @Test public void test_rt_37538_noCNextCall() {
3665         test_rt_37538(false, false);
3666     }
3667 
3668     @Test public void test_rt_37538_callCNextOnce() {
3669         test_rt_37538(true, false);
3670     }
3671 
3672     @Test public void test_rt_37538_callCNextInLoop() {
3673         test_rt_37538(false, true);
3674     }
3675 
3676     private void test_rt_37538(boolean callCNextOnce, boolean callCNextInLoop) {
3677         TableView<Integer> table = new TableView<>();
3678         for ( int i = 1; i <= 50; i++ ) {
3679             table.getItems().add(i);
3680         }
3681         final TableColumn<Integer, Integer> column = new TableColumn<>("Column");
3682         table.getColumns().add(column);
3683         column.setCellValueFactory( cdf -> new ReadOnlyObjectWrapper<Integer>(cdf.getValue()));
3684 
3685         table.getSelectionModel().getSelectedItems().addListener((ListChangeListener.Change<? extends Integer> c) -> {
3686             if (callCNextOnce) {
3687                 c.next();
3688             } else if (callCNextInLoop) {
3689                 while (c.next()) {
3690                     // no-op
3691                 }
3692             }
3693 
3694             if (rt_37538_count >= 1) {
3695                 Thread.dumpStack();
3696                 fail("This method should only be called once");
3697             }
3698 
3699             rt_37538_count++;
3700         });
3701 
3702         StageLoader sl = new StageLoader(table);
3703         assertEquals(0, rt_37538_count);
3704         table.getSelectionModel().select(0);
3705         assertEquals(1, rt_37538_count);
3706         sl.dispose();
3707     }
3708 
3709     @Ignore("Fix not yet developed for TableView")
3710     @Test public void test_rt_35395_testCell_fixedCellSize() {
3711         test_rt_35395(true, true);
3712     }
3713 
3714     @Ignore("Fix not yet developed for TableView")
3715     @Test public void test_rt_35395_testCell_notFixedCellSize() {
3716         test_rt_35395(true, false);
3717     }
3718 
3719     @Ignore("Fix not yet developed for TableView")
3720     @Test public void test_rt_35395_testRow_fixedCellSize() {
3721         test_rt_35395(false, true);
3722     }
3723 
3724     @Ignore("Fix not yet developed for TableView")
3725     @Test public void test_rt_35395_testRow_notFixedCellSize() {
3726         test_rt_35395(false, false);
3727     }
3728 
3729     private int rt_35395_counter;
3730     private void test_rt_35395(boolean testCell, boolean useFixedCellSize) {
3731         rt_35395_counter = 0;
3732 
3733         ObservableList<String> items = FXCollections.observableArrayList();
3734         for (int i = 0; i < 20; ++i) {
3735             items.addAll("red", "green", "blue", "purple");
3736         }
3737 
3738         TableView<String> tableView = new TableView<>(items);
3739         if (useFixedCellSize) {
3740             tableView.setFixedCellSize(24);
3741         }
3742         tableView.setRowFactory(tv -> new TableRowShim<String>() {
3743             @Override public void updateItem(String color, boolean empty) {
3744                 rt_35395_counter += testCell ? 0 : 1;
3745                 super.updateItem(color, empty);
3746             }
3747         });
3748 
3749         TableColumn<String,String> column = new TableColumn<>("Column");
3750         column.setCellValueFactory(param -> new ReadOnlyStringWrapper(param.getValue()));
3751         column.setCellFactory(tv -> new TableCellShim<String,String>() {
3752             @Override public void updateItem(String color, boolean empty) {
3753                 rt_35395_counter += testCell ? 1 : 0;
3754                 super.updateItem(color, empty);
3755                 setText(null);
3756                 if (empty) {
3757                     setGraphic(null);
3758                 } else {
3759                     Rectangle rect = new Rectangle(16, 16);
3760                     rect.setStyle("-fx-fill: " + color);
3761                     setGraphic(rect);
3762                 }
3763             }
3764         });
3765         tableView.getColumns().addAll(column);
3766 
3767         StageLoader sl = new StageLoader(tableView);
3768 
3769         Platform.runLater(() -> {
3770             rt_35395_counter = 0;
3771             items.set(10, "yellow");
3772             Platform.runLater(() -> {
3773                 Toolkit.getToolkit().firePulse();
3774                 assertEquals(1, rt_35395_counter);
3775                 rt_35395_counter = 0;
3776                 items.set(30, "yellow");
3777                 Platform.runLater(() -> {
3778                     Toolkit.getToolkit().firePulse();
3779                     assertEquals(0, rt_35395_counter);
3780                     rt_35395_counter = 0;
3781                     tableView.scrollTo(5);
3782                     Platform.runLater(() -> {
3783                         Toolkit.getToolkit().firePulse();
3784                         assertEquals(5, rt_35395_counter);
3785                         rt_35395_counter = 0;
3786                         tableView.scrollTo(55);
3787                         Platform.runLater(() -> {
3788                             Toolkit.getToolkit().firePulse();
3789 
3790                             int expected = useFixedCellSize ? 17 : 53;
3791                             assertEquals(expected, rt_35395_counter);
3792                             sl.dispose();
3793                         });
3794                     });
3795                 });
3796             });
3797         });
3798     }
3799 
3800     @Test public void test_rt_37632() {
3801         final ObservableList<String> listOne = FXCollections.observableArrayList("A", "B", "C");
3802         final ObservableList<String> listTwo = FXCollections.observableArrayList("C");
3803 
3804         TableColumn<String,String> tableColumn = new TableColumn("column");
3805         tableColumn.setCellValueFactory(c -> new ReadOnlyStringWrapper(c.getValue()));
3806 
3807         final TableView<String> tableView = new TableView<>();
3808         tableView.getColumns().add(tableColumn);
3809         MultipleSelectionModel<String> sm = tableView.getSelectionModel();
3810         tableView.setItems(listOne);
3811         tableView.getSelectionModel().selectFirst();
3812 
3813         assertEquals(0, sm.getSelectedIndex());
3814         assertEquals("A", sm.getSelectedItem());
3815         assertEquals(1, sm.getSelectedIndices().size());
3816         assertEquals(0, (int) sm.getSelectedIndices().get(0));
3817         assertEquals(1, sm.getSelectedItems().size());
3818         assertEquals("A", sm.getSelectedItems().get(0));
3819 
3820         tableView.setItems(listTwo);
3821 
3822         assertEquals(-1, sm.getSelectedIndex());
3823         assertNull(sm.getSelectedItem());
3824         assertEquals(0, sm.getSelectedIndices().size());
3825         assertEquals(0, sm.getSelectedItems().size());
3826     }
3827 
3828     @Test public void test_rt_38464_rowSelection_selectFirstRowOnly() {
3829         TableColumn firstNameCol = new TableColumn("First");
3830         firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
3831 
3832         TableColumn lastNameCol = new TableColumn("Last");
3833         lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));
3834 
3835         TableView tableView = new TableView(personTestData);
3836         tableView.getColumns().addAll(firstNameCol, lastNameCol);
3837 
3838         TableView.TableViewSelectionModel<Person> sm = tableView.getSelectionModel();
3839         sm.setCellSelectionEnabled(false);
3840         sm.setSelectionMode(SelectionMode.MULTIPLE);
3841 
3842         sm.select(0);
3843 
3844         assertTrue(sm.isSelected(0));
3845         assertTrue(sm.isSelected(0, firstNameCol));
3846         assertTrue(sm.isSelected(0, lastNameCol));
3847 
3848         assertEquals(1, sm.getSelectedIndices().size());
3849         assertEquals(1, sm.getSelectedItems().size());
3850         assertEquals(1, sm.getSelectedCells().size());
3851     }
3852 
3853     @Test public void test_rt_38464_rowSelection_selectFirstRowAndThenCallNoOpMethods() {
3854         TableColumn firstNameCol = new TableColumn("First");
3855         firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
3856 
3857         TableColumn lastNameCol = new TableColumn("Last");
3858         lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));
3859 
3860         TableView tableView = new TableView(personTestData);
3861         tableView.getColumns().addAll(firstNameCol, lastNameCol);
3862 
3863         TableView.TableViewSelectionModel<Person> sm = tableView.getSelectionModel();
3864         sm.setCellSelectionEnabled(false);
3865         sm.setSelectionMode(SelectionMode.MULTIPLE);
3866 
3867         sm.select(0);               // select first row
3868         sm.select(0);               // this should be a no-op
3869         sm.select(0, firstNameCol); // so should this, as we are in row selection mode
3870         sm.select(0, lastNameCol);  // and same here
3871 
3872         assertTrue(sm.isSelected(0));
3873         assertTrue(sm.isSelected(0, firstNameCol));
3874         assertTrue(sm.isSelected(0, lastNameCol));
3875 
3876         assertEquals(1, sm.getSelectedIndices().size());
3877         assertEquals(1, sm.getSelectedItems().size());
3878         assertEquals(1, sm.getSelectedCells().size());
3879     }
3880 
3881 
3882     @Test public void test_rt_38464_cellSelection_selectFirstRowOnly() {
3883         TableColumn firstNameCol = new TableColumn("First");
3884         firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
3885 
3886         TableColumn lastNameCol = new TableColumn("Last");
3887         lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));
3888 
3889         TableView tableView = new TableView(personTestData);
3890         tableView.getColumns().addAll(firstNameCol, lastNameCol);
3891 
3892         TableView.TableViewSelectionModel<Person> sm = tableView.getSelectionModel();
3893         sm.setCellSelectionEnabled(true);
3894         sm.setSelectionMode(SelectionMode.MULTIPLE);
3895 
3896         // select first row. This should be translated into selection of all
3897         // cells in this row, and (as of JDK 9) _does_ result in the row itself being
3898         // considered selected.
3899         sm.select(0);
3900 
3901         assertTrue(sm.isSelected(0));
3902         assertTrue(sm.isSelected(0, firstNameCol));
3903         assertTrue(sm.isSelected(0, lastNameCol));
3904 
3905         assertEquals(1, sm.getSelectedIndices().size());
3906         assertEquals(1, sm.getSelectedItems().size());
3907         assertEquals(2, sm.getSelectedCells().size());
3908     }
3909 
3910     @Test public void test_rt_38464_cellSelection_selectFirstRowAndThenCallNoOpMethods() {
3911         TableColumn firstNameCol = new TableColumn("First");
3912         firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
3913 
3914         TableColumn lastNameCol = new TableColumn("Last");
3915         lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));
3916 
3917         TableView tableView = new TableView(personTestData);
3918         tableView.getColumns().addAll(firstNameCol, lastNameCol);
3919 
3920         TableView.TableViewSelectionModel<Person> sm = tableView.getSelectionModel();
3921         sm.setCellSelectionEnabled(true);
3922         sm.setSelectionMode(SelectionMode.MULTIPLE);
3923 
3924         // select first row. This should be translated into selection of all
3925         // cells in this row, and (as of JDK 9) _does_ result in the row itself being
3926         // considered selected.
3927         sm.select(0);                            // select first row
3928         sm.select(0, firstNameCol); // This line and the next should be no-ops
3929         sm.select(0, lastNameCol);
3930 
3931         assertTrue(sm.isSelected(0));
3932         assertTrue(sm.isSelected(0, firstNameCol));
3933         assertTrue(sm.isSelected(0, lastNameCol));
3934 
3935         assertEquals(1, sm.getSelectedIndices().size());
3936         assertEquals(1, sm.getSelectedItems().size());
3937         assertEquals(2, sm.getSelectedCells().size());
3938     }
3939 
3940     @Test public void test_rt38464_selectCellMultipleTimes() {
3941         TableColumn firstNameCol = new TableColumn("First");
3942         firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
3943 
3944         TableColumn lastNameCol = new TableColumn("Last");
3945         lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));
3946 
3947         TableView tableView = new TableView(personTestData);
3948         tableView.getColumns().addAll(firstNameCol, lastNameCol);
3949 
3950         TableView.TableViewSelectionModel<Person> sm = tableView.getSelectionModel();
3951         sm.setCellSelectionEnabled(true);
3952         sm.setSelectionMode(SelectionMode.MULTIPLE);
3953 
3954         // default selection when in cell selection mode
3955         assertEquals(0, sm.getSelectedCells().size());
3956         assertEquals(0, sm.getSelectedItems().size());
3957         assertEquals(0, sm.getSelectedIndices().size());
3958 
3959         // select the first cell
3960         sm.select(0, firstNameCol);
3961         assertEquals(1, sm.getSelectedCells().size());
3962         assertEquals(1, sm.getSelectedItems().size());
3963         assertEquals(1, sm.getSelectedIndices().size());
3964 
3965         // select the first cell....again
3966         sm.select(0, firstNameCol);
3967         assertEquals(1, sm.getSelectedCells().size());
3968         assertEquals(1, sm.getSelectedItems().size());
3969         assertEquals(1, sm.getSelectedIndices().size());
3970     }
3971 
3972     @Test public void test_rt38464_selectCellThenRow() {
3973         TableColumn firstNameCol = new TableColumn("First");
3974         firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
3975 
3976         TableColumn lastNameCol = new TableColumn("Last");
3977         lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));
3978 
3979         TableView tableView = new TableView(personTestData);
3980         tableView.getColumns().addAll(firstNameCol, lastNameCol);
3981 
3982         TableView.TableViewSelectionModel<Person> sm = tableView.getSelectionModel();
3983         sm.setCellSelectionEnabled(true);
3984         sm.setSelectionMode(SelectionMode.MULTIPLE);
3985 
3986         // default selection when in cell selection mode
3987         assertEquals(0, sm.getSelectedCells().size());
3988         assertEquals(0, sm.getSelectedItems().size());
3989         assertEquals(0, sm.getSelectedIndices().size());
3990 
3991         // select the first cell
3992         sm.select(0, firstNameCol);
3993         assertEquals(1, sm.getSelectedCells().size());
3994         assertEquals(1, sm.getSelectedItems().size());
3995         assertEquals(1, sm.getSelectedIndices().size());
3996 
3997         // select the first row
3998         sm.select(0);
3999 
4000         // we go to 2 here as all cells in the row become selected. What we do
4001         // not expect is to go to 3, as that would mean duplication
4002         assertEquals(2, sm.getSelectedCells().size());
4003         assertEquals(1, sm.getSelectedItems().size());
4004         assertEquals(1, sm.getSelectedIndices().size());
4005     }
4006 
4007     @Test public void test_rt38464_selectRowThenCell() {
4008         TableColumn firstNameCol = new TableColumn("First");
4009         firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
4010 
4011         TableColumn lastNameCol = new TableColumn("Last");
4012         lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));
4013 
4014         TableView tableView = new TableView(personTestData);
4015         tableView.getColumns().addAll(firstNameCol, lastNameCol);
4016 
4017         TableView.TableViewSelectionModel<Person> sm = tableView.getSelectionModel();
4018         sm.setCellSelectionEnabled(true);
4019         sm.setSelectionMode(SelectionMode.MULTIPLE);
4020 
4021         // default selection when in cell selection mode
4022         assertEquals(0, sm.getSelectedCells().size());
4023         assertEquals(0, sm.getSelectedItems().size());
4024         assertEquals(0, sm.getSelectedIndices().size());
4025 
4026         // select the first row
4027         sm.select(0);
4028 
4029         // we go to 2 here as all cells in the row become selected.
4030         assertEquals(2, sm.getSelectedCells().size());
4031         assertEquals(1, sm.getSelectedItems().size());
4032         assertEquals(1, sm.getSelectedIndices().size());
4033 
4034         // select the first cell - no change is expected
4035         sm.select(0, firstNameCol);
4036         assertEquals(2, sm.getSelectedCells().size());
4037         assertEquals(1, sm.getSelectedItems().size());
4038         assertEquals(1, sm.getSelectedIndices().size());
4039     }
4040 
4041     @Test public void test_rt38464_selectTests_cellSelection_singleSelection_selectsOneRow() {
4042         test_rt38464_selectTests(true, true, true);
4043     }
4044 
4045     @Test public void test_rt38464_selectTests_cellSelection_singleSelection_selectsTwoRows() {
4046         test_rt38464_selectTests(true, true, false);
4047     }
4048 
4049     @Test public void test_rt38464_selectTests_cellSelection_multipleSelection_selectsOneRow() {
4050         test_rt38464_selectTests(true, false, true);
4051     }
4052 
4053     @Test public void test_rt38464_selectTests_cellSelection_multipleSelection_selectsTwoRows() {
4054         test_rt38464_selectTests(true, false, false);
4055     }
4056 
4057     @Test public void test_rt38464_selectTests_rowSelection_singleSelection_selectsOneRow() {
4058         test_rt38464_selectTests(false, true, true);
4059     }
4060 
4061     @Test public void test_rt38464_selectTests_rowSelection_singleSelection_selectsTwoRows() {
4062         test_rt38464_selectTests(false, true, false);
4063     }
4064 
4065     @Test public void test_rt38464_selectTests_rowSelection_multipleSelection_selectsOneRow() {
4066         test_rt38464_selectTests(false, false, true);
4067     }
4068 
4069     @Test public void test_rt38464_selectTests_rowSelection_multipleSelection_selectsTwoRows() {
4070         test_rt38464_selectTests(false, false, false);
4071     }
4072 
4073     private void test_rt38464_selectTests(boolean cellSelection, boolean singleSelection, boolean selectsOneRow) {
4074         TableColumn firstNameCol = new TableColumn("First");
4075         firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
4076 
4077         TableColumn lastNameCol = new TableColumn("Last");
4078         lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));
4079 
4080         TableView tableView = new TableView(personTestData);
4081         tableView.getColumns().addAll(firstNameCol, lastNameCol);
4082 
4083         TableView.TableViewSelectionModel<Person> sm = tableView.getSelectionModel();
4084         sm.setCellSelectionEnabled(cellSelection);
4085         sm.setSelectionMode(singleSelection ? SelectionMode.SINGLE : SelectionMode.MULTIPLE);
4086 
4087         // default selection when in cell selection mode
4088         assertEquals(0, sm.getSelectedCells().size());
4089         assertEquals(0, sm.getSelectedItems().size());
4090         assertEquals(0, sm.getSelectedIndices().size());
4091 
4092         if (selectsOneRow) {
4093             sm.select(0);
4094         } else {
4095             // select the first two rows
4096             sm.selectIndices(0, 1);
4097         }
4098 
4099         final int expectedCells = singleSelection                    ? 1 :
4100                                   selectsOneRow   && cellSelection   ? 2 :
4101                                   selectsOneRow   && !cellSelection  ? 1 :
4102                                   !selectsOneRow  && cellSelection   ? 4 :
4103                                /* !selectsOneRow  && !cellSelection */ 2;
4104 
4105         final int expectedItems = singleSelection ? 1 :
4106                                   selectsOneRow   ? 1 : 2;
4107 
4108         assertEquals(expectedCells, sm.getSelectedCells().size());
4109         assertEquals(expectedItems, sm.getSelectedItems().size());
4110         assertEquals(expectedItems, sm.getSelectedIndices().size());
4111 
4112         // we expect the table column of all selected cells, in this instance,
4113         // to be null as we have not explicitly stated a column, nor have we clicked
4114         // on a column. The only alternative is to use the first column.
4115         for (TablePosition<?,?> tp : sm.getSelectedCells()) {
4116             if (cellSelection) {
4117                 assertNotNull(tp.getTableColumn());
4118             } else {
4119                 assertNull(tp.getTableColumn());
4120             }
4121         }
4122     }
4123 
4124     private int rt_37853_cancelCount;
4125     private int rt_37853_commitCount;
4126     @Test public void test_rt_37853() {
4127         TableColumn<String,String> first = new TableColumn<>("first");
4128         first.setEditable(true);
4129         first.setCellFactory(TextFieldTableCell.forTableColumn());
4130         table.getColumns().add(first);
4131         table.setEditable(true);
4132 
4133         for (int i = 0; i < 10; i++) {
4134             table.getItems().add("" + i);
4135         }
4136 
4137         StageLoader sl = new StageLoader(table);
4138 
4139         first.setOnEditCancel(editEvent -> rt_37853_cancelCount++);
4140         first.setOnEditCommit(editEvent -> rt_37853_commitCount++);
4141 
4142         assertEquals(0, rt_37853_cancelCount);
4143         assertEquals(0, rt_37853_commitCount);
4144 
4145         table.edit(1, first);
4146         assertNotNull(table.getEditingCell());
4147 
4148         table.getItems().clear();
4149         assertEquals(1, rt_37853_cancelCount);
4150         assertEquals(0, rt_37853_commitCount);
4151 
4152         sl.dispose();
4153     }
4154 
4155 
4156     /**************************************************************************
4157      *
4158      * Tests (and related code) for RT-38892
4159      *
4160      *************************************************************************/
4161 
4162     private final Supplier<TableColumn<Person,String>> columnCallable = () -> {
4163         TableColumn<Person,String> column = new TableColumn<>("Last Name");
4164         column.setCellValueFactory(new PropertyValueFactory<Person,String>("lastName"));
4165         return column;
4166     };
4167 
4168     private TableColumn<Person, String> test_rt_38892_firstNameCol;
4169     private TableColumn<Person, String> test_rt_38892_lastNameCol;
4170 
4171     private TableView<Person> init_test_rt_38892() {
4172         ObservableList<Person> data = FXCollections.observableArrayList(
4173                 new Person("Jacob", "Smith", ""),
4174                 new Person("Isabella", "Johnson", ""),
4175                 new Person("Ethan", "Williams", ""),
4176                 new Person("Emma", "Jones", ""),
4177                 new Person("Michael", "Brown", "")
4178         );
4179 
4180         TableView<Person> table = new TableView<>();
4181         table.getSelectionModel().setCellSelectionEnabled(true);
4182         table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
4183         table.setItems(data);
4184 
4185         test_rt_38892_firstNameCol = new TableColumn<>("First Name");
4186         test_rt_38892_firstNameCol.setCellValueFactory(new PropertyValueFactory<>("firstName"));
4187         test_rt_38892_lastNameCol = columnCallable.get();
4188         table.getColumns().addAll(test_rt_38892_firstNameCol, test_rt_38892_lastNameCol);
4189 
4190         return table;
4191     }
4192 
4193     @Test public void test_rt_38892_focusMovesToLeftWhenPossible() {
4194         TableView<Person> table = init_test_rt_38892();
4195 
4196         TableView.TableViewFocusModel<Person> fm = table.getFocusModel();
4197         fm.focus(0, test_rt_38892_lastNameCol);
4198 
4199         // assert pre-conditions
4200         assertEquals(0, fm.getFocusedIndex());
4201         assertEquals(0, fm.getFocusedCell().getRow());
4202         assertEquals(test_rt_38892_lastNameCol, fm.getFocusedCell().getTableColumn());
4203         assertEquals(1, fm.getFocusedCell().getColumn());
4204 
4205         // now remove column where focus is and replace it with a new column.
4206         // We expect focus to move to the left one cell.
4207         table.getColumns().remove(1);
4208         table.getColumns().add(columnCallable.get());
4209 
4210         assertEquals(0, fm.getFocusedIndex());
4211         assertEquals(0, fm.getFocusedCell().getRow());
4212         assertEquals(test_rt_38892_firstNameCol, fm.getFocusedCell().getTableColumn());
4213         assertEquals(0, fm.getFocusedCell().getColumn());
4214     }
4215 
4216     @Test public void test_rt_38892_removeLeftMostColumn() {
4217         TableView<Person> table = init_test_rt_38892();
4218 
4219         TableView.TableViewFocusModel<Person> fm = table.getFocusModel();
4220         fm.focus(0, test_rt_38892_firstNameCol);
4221 
4222         // assert pre-conditions
4223         assertEquals(0, fm.getFocusedIndex());
4224         assertEquals(0, fm.getFocusedCell().getRow());
4225         assertEquals(test_rt_38892_firstNameCol, fm.getFocusedCell().getTableColumn());
4226         assertEquals(0, fm.getFocusedCell().getColumn());
4227 
4228         // now remove column where focus is and replace it with a new column.
4229         // In the current (non-specified) behavior, this results in focus being
4230         // shifted to a cell in the remaining column, even when we add a new column
4231         // as we index based on the column, not on its index.
4232         table.getColumns().remove(0);
4233         TableColumn<Person,String> newColumn = columnCallable.get();
4234         table.getColumns().add(0, newColumn);
4235 
4236         assertEquals(0, fm.getFocusedIndex());
4237         assertEquals(0, fm.getFocusedCell().getRow());
4238         assertEquals(test_rt_38892_lastNameCol, fm.getFocusedCell().getTableColumn());
4239         assertEquals(0, fm.getFocusedCell().getColumn());
4240     }
4241 
4242     @Test public void test_rt_38892_removeSelectionFromCellsInRemovedColumn() {
4243         TableView<Person> table = init_test_rt_38892();
4244 
4245         TableView.TableViewSelectionModel sm = table.getSelectionModel();
4246         sm.select(0, test_rt_38892_firstNameCol);
4247         sm.select(1, test_rt_38892_lastNameCol);    // this should go
4248         sm.select(2, test_rt_38892_firstNameCol);
4249         sm.select(3, test_rt_38892_lastNameCol);    // so should this
4250         sm.select(4, test_rt_38892_firstNameCol);
4251 
4252         assertEquals(5, sm.getSelectedCells().size());
4253 
4254         table.getColumns().remove(1);
4255 
4256         assertEquals(3, sm.getSelectedCells().size());
4257         assertTrue(sm.isSelected(0, test_rt_38892_firstNameCol));
4258         assertFalse(sm.isSelected(1, test_rt_38892_lastNameCol));
4259         assertTrue(sm.isSelected(2, test_rt_38892_firstNameCol));
4260         assertFalse(sm.isSelected(3, test_rt_38892_lastNameCol));
4261         assertTrue(sm.isSelected(4, test_rt_38892_firstNameCol));
4262     }
4263 
4264     @Test public void test_rt_38787_remove_b() {
4265         // selection moves to "a"
4266         test_rt_38787("a", 0, "b");
4267     }
4268 
4269     @Test public void test_rt_38787_remove_b_c() {
4270         // selection moves to "a"
4271         test_rt_38787("a", 0, "b", "c");
4272     }
4273 
4274     @Test public void test_rt_38787_remove_c_d() {
4275         // selection stays on "b"
4276         test_rt_38787("b", 1, "c", "d");
4277     }
4278 
4279     @Test public void test_rt_38787_remove_a() {
4280         // selection moves to "b", now in index 0
4281         test_rt_38787("b", 0, "a");
4282     }
4283 
4284     @Test public void test_rt_38787_remove_z() {
4285         // selection shouldn't move as 'z' doesn't exist
4286         test_rt_38787("b", 1, "z");
4287     }
4288 
4289     private void test_rt_38787(String expectedItem, int expectedIndex, String... itemsToRemove) {
4290         TableView<String> stringTableView = new TableView<>();
4291         stringTableView.getItems().addAll("a","b","c","d");
4292 
4293         TableColumn<String,String> column = new TableColumn<>("Column");
4294         column.setCellValueFactory(cdf -> new ReadOnlyStringWrapper(cdf.getValue()));
4295         stringTableView.getColumns().add(column);
4296 
4297         MultipleSelectionModel<String> sm = stringTableView.getSelectionModel();
4298         sm.select("b");
4299 
4300         // test pre-conditions
4301         assertEquals(1, sm.getSelectedIndex());
4302         assertEquals(1, (int)sm.getSelectedIndices().get(0));
4303         assertEquals("b", sm.getSelectedItem());
4304         assertEquals("b", sm.getSelectedItems().get(0));
4305         assertFalse(sm.isSelected(0));
4306         assertTrue(sm.isSelected(1));
4307         assertFalse(sm.isSelected(2));
4308 
4309         // removing items
4310         stringTableView.getItems().removeAll(itemsToRemove);
4311 
4312         // testing against expectations
4313         assertEquals(expectedIndex, sm.getSelectedIndex());
4314         assertEquals(expectedIndex, (int)sm.getSelectedIndices().get(0));
4315         assertEquals(expectedItem, sm.getSelectedItem());
4316         assertEquals(expectedItem, sm.getSelectedItems().get(0));
4317     }
4318 
4319     private int rt_38341_indices_count = 0;
4320     private int rt_38341_items_count = 0;
4321     @Test public void test_rt_38341() {
4322         TableView<String> stringTableView = new TableView<>();
4323         stringTableView.getItems().addAll("a","b","c","d");
4324 
4325         TableColumn<String,String> column = new TableColumn<>("Column");
4326         column.setCellValueFactory(cdf -> new ReadOnlyStringWrapper(cdf.getValue()));
4327         stringTableView.getColumns().add(column);
4328 
4329         MultipleSelectionModel<String> sm = stringTableView.getSelectionModel();
4330         sm.getSelectedIndices().addListener((ListChangeListener<Integer>) c -> {
4331             rt_38341_indices_count++;
4332         });
4333         sm.getSelectedItems().addListener((ListChangeListener<String>) c -> rt_38341_items_count++);
4334 
4335         assertEquals(0, rt_38341_indices_count);
4336         assertEquals(0, rt_38341_items_count);
4337 
4338         // expand the first child of root, and select it (note: root isn't visible)
4339         sm.select(1);
4340         assertEquals(1, sm.getSelectedIndex());
4341         assertEquals(1, sm.getSelectedIndices().size());
4342         assertEquals(1, (int)sm.getSelectedIndices().get(0));
4343         assertEquals(1, sm.getSelectedItems().size());
4344         assertEquals("b", sm.getSelectedItem());
4345         assertEquals("b", sm.getSelectedItems().get(0));
4346 
4347         assertEquals(1, rt_38341_indices_count);
4348         assertEquals(1, rt_38341_items_count);
4349 
4350         // now delete it
4351         stringTableView.getItems().remove(1);
4352 
4353         // selection should move to the childs parent in index 0
4354         assertEquals(0, sm.getSelectedIndex());
4355         assertEquals(1, sm.getSelectedIndices().size());
4356         assertEquals(0, (int)sm.getSelectedIndices().get(0));
4357         assertEquals(1, sm.getSelectedItems().size());
4358         assertEquals("a", sm.getSelectedItem());
4359         assertEquals("a", sm.getSelectedItems().get(0));
4360 
4361         // we also expect there to be an event in the selection model for
4362         // selected indices and selected items
4363         assertEquals(2, rt_38341_indices_count);
4364         assertEquals(2, rt_38341_items_count);
4365     }
4366 
4367     @Test public void test_rt_39132() {
4368         TableView<String> tableView = new TableView<>();
4369 
4370         ObservableList items = FXCollections.observableArrayList("one", "two", "three");
4371         tableView.setItems(items);
4372 
4373         TableColumn<String,String> column = new TableColumn<>("Column");
4374         column.setCellValueFactory(cdf -> new ReadOnlyStringWrapper(cdf.getValue()));
4375         tableView.getColumns().add(column);
4376 
4377         MultipleSelectionModel sm = tableView.getSelectionModel();
4378         sm.select(0);
4379 
4380         assertEquals(0, sm.getSelectedIndex());
4381         assertEquals("one", sm.getSelectedItem());
4382 
4383         items.add(0, "new item");
4384         assertEquals(1, sm.getSelectedIndex());
4385         assertEquals("one", sm.getSelectedItem());
4386     }
4387 
4388     private int rt_38943_index_count = 0;
4389     private int rt_38943_item_count = 0;
4390     @Test public void test_rt_38943() {
4391         TableView<String> tableView = new TableView<>(FXCollections.observableArrayList("one", "two", "three"));
4392 
4393         TableColumn<String,String> column = new TableColumn<>("Column");
4394         column.setCellValueFactory(cdf -> new ReadOnlyStringWrapper(cdf.getValue()));
4395         tableView.getColumns().add(column);
4396 
4397         MultipleSelectionModel sm = tableView.getSelectionModel();
4398 
4399         sm.selectedIndexProperty().addListener((observable, oldValue, newValue) -> rt_38943_index_count++);
4400         sm.selectedItemProperty().addListener((observable, oldValue, newValue) -> rt_38943_item_count++);
4401 
4402         assertEquals(-1, sm.getSelectedIndex());
4403         assertNull(sm.getSelectedItem());
4404         assertEquals(0, rt_38943_index_count);
4405         assertEquals(0, rt_38943_item_count);
4406 
4407         sm.select(0);
4408         assertEquals(0, sm.getSelectedIndex());
4409         assertEquals("one", sm.getSelectedItem());
4410         assertEquals(1, rt_38943_index_count);
4411         assertEquals(1, rt_38943_item_count);
4412 
4413         sm.clearSelection(0);
4414         assertEquals(-1, sm.getSelectedIndex());
4415         assertNull(sm.getSelectedItem());
4416         assertEquals(2, rt_38943_index_count);
4417         assertEquals(2, rt_38943_item_count);
4418     }
4419 
4420     @Test public void test_rt_38884() {
4421         TableView<String> table = new TableView<>();
4422         ObservableList<String> items = table.getItems();
4423 
4424         table.getSelectionModel().getSelectedItems().addListener((ListChangeListener.Change<? extends String> c) -> {
4425             while (c.next()) {
4426                 if (c.wasRemoved()) {
4427                     assertTrue(c.getRemovedSize() > 0);
4428 
4429                     List<? extends String> removed = c.getRemoved();
4430                     String removedItem = null;
4431                     try {
4432                         removedItem = removed.get(0);
4433                     } catch (Exception e) {
4434                         fail();
4435                     }
4436 
4437                     assertEquals("foo", removedItem);
4438                 }
4439             }
4440         });
4441 
4442         items.add("foo");
4443         table.getSelectionModel().select(0);
4444         items.clear();
4445     }
4446 
4447     private int rt_37360_add_count = 0;
4448     private int rt_37360_remove_count = 0;
4449     @Test public void test_rt_37360() {
4450         TableView<String> stringTableView = new TableView<>();
4451         stringTableView.getItems().addAll("a","b");
4452 
4453         TableColumn<String,String> column = new TableColumn<>("Column");
4454         column.setCellValueFactory(cdf -> new ReadOnlyStringWrapper(cdf.getValue()));
4455         stringTableView.getColumns().add(column);
4456 
4457         MultipleSelectionModel<String> sm = stringTableView.getSelectionModel();
4458         sm.setSelectionMode(SelectionMode.MULTIPLE);
4459         sm.getSelectedItems().addListener((ListChangeListener<String>) c -> {
4460             while (c.next()) {
4461                 if (c.wasAdded()) {
4462                     rt_37360_add_count += c.getAddedSize();
4463                 }
4464                 if (c.wasRemoved()) {
4465                     rt_37360_remove_count += c.getRemovedSize();
4466                 }
4467             }
4468         });
4469 
4470         assertEquals(0, sm.getSelectedItems().size());
4471         assertEquals(0, rt_37360_add_count);
4472         assertEquals(0, rt_37360_remove_count);
4473 
4474         sm.select(0);
4475         assertEquals(1, sm.getSelectedItems().size());
4476         assertEquals(1, rt_37360_add_count);
4477         assertEquals(0, rt_37360_remove_count);
4478 
4479         sm.select(1);
4480         assertEquals(2, sm.getSelectedItems().size());
4481         assertEquals(2, rt_37360_add_count);
4482         assertEquals(0, rt_37360_remove_count);
4483 
4484         sm.clearAndSelect(1);
4485         assertEquals(1, sm.getSelectedItems().size());
4486         assertEquals(2, rt_37360_add_count);
4487         assertEquals(1, rt_37360_remove_count);
4488     }
4489 
4490     @Test public void test_rt_38491() {
4491         TableView<String> stringTableView = new TableView<>();
4492         stringTableView.getItems().addAll("a","b", "c", "d");
4493 
4494         TableColumn<String,String> column = new TableColumn<>("Column");
4495         column.setCellValueFactory(cdf -> new ReadOnlyStringWrapper(cdf.getValue()));
4496         stringTableView.getColumns().add(column);
4497 
4498         TableSelectionModel<String> sm = stringTableView.getSelectionModel();
4499         sm.setSelectionMode(SelectionMode.MULTIPLE);
4500 
4501         TableView.TableViewFocusModel fm = stringTableView.getFocusModel();
4502 
4503         StageLoader sl = new StageLoader(stringTableView);
4504 
4505         // click on row 0
4506         sm.select(0, column);
4507         assertTrue(sm.isSelected(0));
4508         assertEquals("a", sm.getSelectedItem());
4509         assertTrue(fm.isFocused(0));
4510         assertEquals("a", fm.getFocusedItem());
4511         assertEquals(0, fm.getFocusedCell().getRow());
4512         assertEquals(column, fm.getFocusedCell().getTableColumn());
4513 
4514         TablePosition anchor = TableCellBehavior.getAnchor(stringTableView, null);
4515         assertTrue(TableCellBehavior.hasNonDefaultAnchor(stringTableView));
4516         assertEquals(0, anchor.getRow());
4517         assertEquals(column, anchor.getTableColumn());
4518 
4519         // now add a new item at row 0. This has the effect of pushing down
4520         // the selected item into row 1.
4521         stringTableView.getItems().add(0, "z");
4522 
4523         Toolkit.getToolkit().firePulse();
4524 
4525         // The first bug was that selection and focus were not moving down to
4526         // be on row 1, so we test that now
4527         assertFalse(sm.isSelected(0));
4528         assertFalse(fm.isFocused(0));
4529         assertTrue(sm.isSelected(1));
4530         assertEquals("a", sm.getSelectedItem());
4531         assertTrue(fm.isFocused(1));
4532         assertEquals("a", fm.getFocusedItem());
4533         assertEquals(1, fm.getFocusedCell().getRow());
4534         assertEquals(column, fm.getFocusedCell().getTableColumn());
4535 
4536         // The second bug was that the anchor was not being pushed down as well
4537         // (when it should).
4538         anchor = TableCellBehavior.getAnchor(stringTableView, null);
4539         assertTrue(TableCellBehavior.hasNonDefaultAnchor(stringTableView));
4540         assertEquals(1, anchor.getRow());
4541         assertEquals(column, anchor.getTableColumn());
4542 
4543         sl.dispose();
4544     }
4545 
4546     private final ObservableList<String> rt_39256_list = FXCollections.observableArrayList();
4547     @Test public void test_rt_39256() {
4548         TableView<String> stringTableView = new TableView<>();
4549         stringTableView.getItems().addAll("a","b", "c", "d");
4550 
4551         TableColumn<String,String> column = new TableColumn<>("Column");
4552         column.setCellValueFactory(cdf -> new ReadOnlyStringWrapper(cdf.getValue()));
4553         stringTableView.getColumns().add(column);
4554 
4555         TableSelectionModel<String> sm = stringTableView.getSelectionModel();
4556         sm.setSelectionMode(SelectionMode.MULTIPLE);
4557 
4558 //        rt_39256_list.addListener((ListChangeListener<String>) change -> {
4559 //            while (change.next()) {
4560 //                System.err.println("number of selected persons (in bound list): " + change.getList().size());
4561 //            }
4562 //        });
4563 
4564         Bindings.bindContent(rt_39256_list, sm.getSelectedItems());
4565 
4566         assertEquals(0, sm.getSelectedItems().size());
4567         assertEquals(0, rt_39256_list.size());
4568 
4569         sm.selectAll();
4570         assertEquals(4, sm.getSelectedItems().size());
4571         assertEquals(4, rt_39256_list.size());
4572 
4573         sm.selectAll();
4574         assertEquals(4, sm.getSelectedItems().size());
4575         assertEquals(4, rt_39256_list.size());
4576 
4577         sm.selectAll();
4578         assertEquals(4, sm.getSelectedItems().size());
4579         assertEquals(4, rt_39256_list.size());
4580     }
4581 
4582     @Test public void test_rt_39256_noTableView() {
4583         ObservableList<String> listOne = FXCollections.observableArrayList();
4584         ObservableList<String> listTwo = FXCollections.observableArrayList();
4585 
4586 //        listOne.addListener((ListChangeListener<String>) change -> {
4587 //            while (change.next()) {
4588 //                System.out.println("number of strings in bound list - listOne: " + change.getList().size() + ", change: " + change);
4589 //            }
4590 //        });
4591 //
4592 //        listTwo.addListener((ListChangeListener<String>) change -> {
4593 //            while (change.next()) {
4594 //                System.out.println("number of strings in unbound list - listTwo: " + change.getList().size() + ", change: " + change);
4595 //            }
4596 //        });
4597 
4598         Bindings.bindContent(listOne, listTwo);
4599 
4600         assertEquals(0, listOne.size());
4601         assertEquals(0, listTwo.size());
4602 
4603         System.out.println("Test One:");
4604         listTwo.setAll("a", "b", "c", "d");
4605         assertEquals(4, listOne.size());
4606         assertEquals(4, listTwo.size());
4607 
4608         System.out.println("\nTest Two:");
4609         listTwo.setAll("e", "f", "g", "h");
4610         assertEquals(4, listOne.size());
4611         assertEquals(4, listTwo.size());
4612 
4613         System.out.println("\nTest Three:");
4614         listTwo.setAll("i", "j", "k", "l");
4615         assertEquals(4, listOne.size());
4616         assertEquals(4, listTwo.size());
4617     }
4618 
4619     private final ObservableList<String> rt_39482_list = FXCollections.observableArrayList();
4620     @Test public void test_rt_39482() {
4621         TableView<String> stringTableView = new TableView<>();
4622         stringTableView.getItems().addAll("a","b", "c", "d");
4623 
4624         TableColumn<String,String> column = new TableColumn<>("Column");
4625         column.setCellValueFactory(cdf -> new ReadOnlyStringWrapper(cdf.getValue()));
4626         stringTableView.getColumns().add(column);
4627 
4628         TableView.TableViewSelectionModel<String> sm = stringTableView.getSelectionModel();
4629         sm.setSelectionMode(SelectionMode.MULTIPLE);
4630 
4631         sm.getSelectedItems().addListener((ListChangeListener<String>) change -> {
4632             while (change.next()) {
4633                 System.out.println("sm.getSelectedItems(): " + change.getList());
4634             }
4635         });
4636 
4637         rt_39482_list.addListener((ListChangeListener<String>) change -> {
4638             while (change.next()) {
4639                 System.out.println("rt_39482_list: " + change.getList());
4640             }
4641         });
4642 
4643         Bindings.bindContent(rt_39482_list, sm.getSelectedItems());
4644 
4645         assertEquals(0, sm.getSelectedItems().size());
4646         assertEquals(0, rt_39482_list.size());
4647 
4648         test_rt_39482_selectRow("a", sm, 0, column);
4649         test_rt_39482_selectRow("b", sm, 1, column);
4650         test_rt_39482_selectRow("c", sm, 2, column);
4651         test_rt_39482_selectRow("d", sm, 3, column);
4652     }
4653 
4654     private void test_rt_39482_selectRow(String expectedString,
4655                                          TableView.TableViewSelectionModel<String> sm,
4656                                          int rowToSelect,
4657                                          TableColumn<String,String> columnToSelect) {
4658         System.out.println("\nSelect row " + rowToSelect);
4659         sm.selectAll();
4660         assertEquals(4, sm.getSelectedCells().size());
4661         assertEquals(4, sm.getSelectedIndices().size());
4662         assertEquals(4, sm.getSelectedItems().size());
4663         assertEquals(4, rt_39482_list.size());
4664 
4665         sm.clearAndSelect(rowToSelect, columnToSelect);
4666         assertEquals(1, sm.getSelectedCells().size());
4667         assertEquals(1, sm.getSelectedIndices().size());
4668         assertEquals(1, sm.getSelectedItems().size());
4669         assertEquals(expectedString, sm.getSelectedItem());
4670         assertEquals(expectedString, rt_39482_list.get(0));
4671         assertEquals(1, rt_39482_list.size());
4672     }
4673 
4674     @Test public void test_rt_39559_useSM_selectAll() {
4675         test_rt_39559(true);
4676     }
4677 
4678     @Test public void test_rt_39559_useKeyboard_selectAll() {
4679         test_rt_39559(false);
4680     }
4681 
4682     private void test_rt_39559(boolean useSMSelectAll) {
4683         TableView<String> stringTableView = new TableView<>();
4684         stringTableView.getItems().addAll("a","b", "c", "d");
4685 
4686         TableColumn<String,String> column = new TableColumn<>("Column");
4687         column.setCellValueFactory(cdf -> new ReadOnlyStringWrapper(cdf.getValue()));
4688         stringTableView.getColumns().add(column);
4689 
4690         TableView.TableViewSelectionModel<String> sm = stringTableView.getSelectionModel();
4691         sm.setSelectionMode(SelectionMode.MULTIPLE);
4692 
4693         StageLoader sl = new StageLoader(stringTableView);
4694         KeyEventFirer keyboard = new KeyEventFirer(stringTableView);
4695 
4696         assertEquals(0, sm.getSelectedItems().size());
4697 
4698         sm.clearAndSelect(0);
4699 
4700         if (useSMSelectAll) {
4701             sm.selectAll();
4702         } else {
4703             keyboard.doKeyPress(KeyCode.A, KeyModifier.getShortcutKey());
4704         }
4705 
4706         assertEquals(4, sm.getSelectedItems().size());
4707         assertEquals(0, ((TablePosition) TableCellBehavior.getAnchor(stringTableView, null)).getRow());
4708 
4709         keyboard.doKeyPress(KeyCode.DOWN, KeyModifier.SHIFT);
4710 
4711         assertEquals(0, ((TablePosition) TableCellBehavior.getAnchor(stringTableView, null)).getRow());
4712         assertEquals(2, sm.getSelectedItems().size());
4713         assertEquals("a", sm.getSelectedItems().get(0));
4714         assertEquals("b", sm.getSelectedItems().get(1));
4715 
4716         sl.dispose();
4717     }
4718 
4719     @Test public void test_rt_16068_firstElement_selectAndRemoveSameRow() {
4720         // select and then remove the 'a' item, selection and focus should both
4721         // stay at the first row, now 'b'
4722         test_rt_16068(0, 0, 0);
4723     }
4724 
4725     @Test public void test_rt_16068_firstElement_selectRowAndRemoveLaterSibling() {
4726         // select row 'a', and remove row 'c', selection and focus should not change
4727         test_rt_16068(0, 2, 0);
4728     }
4729 
4730     @Test public void test_rt_16068_middleElement_selectAndRemoveSameRow() {
4731         // select and then remove the 'b' item, selection and focus should both
4732         // move up one row to the 'a' item
4733         test_rt_16068(1, 1, 0);
4734     }
4735 
4736     @Test public void test_rt_16068_middleElement_selectRowAndRemoveLaterSibling() {
4737         // select row 'b', and remove row 'c', selection and focus should not change
4738         test_rt_16068(1, 2, 1);
4739     }
4740 
4741     @Test public void test_rt_16068_middleElement_selectRowAndRemoveEarlierSibling() {
4742         // select row 'b', and remove row 'a', selection and focus should move up
4743         // one row, remaining on 'b'
4744         test_rt_16068(1, 0, 0);
4745     }
4746 
4747     @Test public void test_rt_16068_lastElement_selectAndRemoveSameRow() {
4748         // select and then remove the 'd' item, selection and focus should both
4749         // move up one row to the 'c' item
4750         test_rt_16068(3, 3, 2);
4751     }
4752 
4753     @Test public void test_rt_16068_lastElement_selectRowAndRemoveEarlierSibling() {
4754         // select row 'd', and remove row 'a', selection and focus should move up
4755         // one row, remaining on 'd'
4756         test_rt_16068(3, 0, 2);
4757     }
4758 
4759     private void test_rt_16068(int indexToSelect, int indexToRemove, int expectedIndex) {
4760         TableView<String> stringTableView = new TableView<>();
4761         stringTableView.getItems().addAll("a","b", "c", "d");
4762 
4763         TableColumn<String,String> column = new TableColumn<>("Column");
4764         column.setCellValueFactory(cdf -> new ReadOnlyStringWrapper(cdf.getValue()));
4765         stringTableView.getColumns().add(column);
4766 
4767         TableView.TableViewSelectionModel<?> sm = stringTableView.getSelectionModel();
4768         FocusModel<?> fm = stringTableView.getFocusModel();
4769 
4770         sm.select(indexToSelect);
4771         assertEquals(indexToSelect, sm.getSelectedIndex());
4772         assertEquals(stringTableView.getItems().get(indexToSelect), sm.getSelectedItem());
4773         assertEquals(indexToSelect, fm.getFocusedIndex());
4774         assertEquals(stringTableView.getItems().get(indexToSelect), fm.getFocusedItem());
4775 
4776         stringTableView.getItems().remove(indexToRemove);
4777         assertEquals(expectedIndex, sm.getSelectedIndex());
4778         assertEquals(stringTableView.getItems().get(expectedIndex), sm.getSelectedItem());
4779         assertEquals(expectedIndex, fm.getFocusedIndex());
4780         assertEquals(stringTableView.getItems().get(expectedIndex), fm.getFocusedItem());
4781     }
4782 
4783     private int test_rt_39822_count = 0;
4784     @Test public void test_rt_39822() {
4785         // get the current exception handler before replacing with our own,
4786         // as ListListenerHelp intercepts the exception otherwise
4787         final Thread.UncaughtExceptionHandler exceptionHandler = Thread.currentThread().getUncaughtExceptionHandler();
4788         Thread.currentThread().setUncaughtExceptionHandler((t, e) -> {
4789             e.printStackTrace();
4790 
4791             if (test_rt_39822_count == 0) {
4792                 test_rt_39822_count++;
4793                 if (! (e instanceof IllegalStateException)) {
4794                     fail("Incorrect exception type - expecting IllegalStateException");
4795                 }
4796             } else {
4797                 // don't care
4798                 test_rt_39822_count++;
4799             }
4800         });
4801 
4802         TableView<String> table = new TableView<>();
4803         TableColumn<String, String> col1 = new TableColumn<>("Foo");
4804         table.getColumns().addAll(col1, col1);  // add column twice
4805 
4806         StageLoader sl = null;
4807         try {
4808             sl = new StageLoader(table);
4809         } finally {
4810             if (sl != null) {
4811                 sl.dispose();
4812             }
4813 
4814             // reset the exception handler
4815             Thread.currentThread().setUncaughtExceptionHandler(exceptionHandler);
4816         }
4817     }
4818 
4819     private int test_rt_39842_count = 0;
4820     @Test public void test_rt_39842_selectLeftDown() {
4821         test_rt_39842(true, false);
4822     }
4823 
4824     @Test public void test_rt_39842_selectLeftUp() {
4825         test_rt_39842(true, true);
4826     }
4827 
4828     @Test public void test_rt_39842_selectRightDown() {
4829         test_rt_39842(false, false);
4830     }
4831 
4832     @Test public void test_rt_39842_selectRightUp() {
4833         test_rt_39842(false, true);
4834     }
4835 
4836     private void test_rt_39842(boolean selectToLeft, boolean selectUpwards) {
4837         test_rt_39842_count = 0;
4838 
4839         TableColumn firstNameCol = new TableColumn("First Name");
4840         firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
4841 
4842         TableColumn lastNameCol = new TableColumn("Last Name");
4843         lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
4844 
4845         TableView<Person> table = new TableView<>();
4846         table.setItems(personTestData);
4847         table.getColumns().addAll(firstNameCol, lastNameCol);
4848 
4849         sm = table.getSelectionModel();
4850         sm.setCellSelectionEnabled(true);
4851         sm.setSelectionMode(SelectionMode.MULTIPLE);
4852         sm.getSelectedCells().addListener((ListChangeListener) c -> test_rt_39842_count++);
4853 
4854         StageLoader sl = new StageLoader(table);
4855 
4856         assertEquals(0, test_rt_39842_count);
4857 
4858         if (selectToLeft) {
4859             if (selectUpwards) {
4860                 sm.selectRange(3, lastNameCol, 0, firstNameCol);
4861             } else {
4862                 sm.selectRange(0, lastNameCol, 3, firstNameCol);
4863             }
4864         } else {
4865             if (selectUpwards) {
4866                 sm.selectRange(3, firstNameCol, 0, lastNameCol);
4867             } else {
4868                 sm.selectRange(0, firstNameCol, 3, lastNameCol);
4869             }
4870         }
4871 
4872         // test model state
4873         assertEquals(8, sm.getSelectedCells().size());
4874         assertEquals(1, test_rt_39842_count);
4875 
4876         // test visual state
4877         for (int row = 0; row <= 3; row++) {
4878             for (int column = 0; column <= 1; column++) {
4879                 IndexedCell cell = VirtualFlowTestUtils.getCell(table, row, column);
4880                 assertTrue(cell.isSelected());
4881             }
4882         }
4883 
4884         sl.dispose();
4885     }
4886 
4887     @Test public void test_rt_22599() {
4888         ObservableList<RT22599_DataType> initialData = FXCollections.observableArrayList(
4889                 new RT22599_DataType(1, "row1"),
4890                 new RT22599_DataType(2, "row2"),
4891                 new RT22599_DataType(3, "row3")
4892         );
4893 
4894         TableColumn<RT22599_DataType, String> col = new TableColumn<>("Header");
4895         col.setCellValueFactory(param -> new ReadOnlyStringWrapper(param.getValue().text));
4896 
4897         TableView<RT22599_DataType> table = new TableView<>();
4898         table.setItems(initialData);
4899         table.getColumns().addAll(col);
4900 
4901         StageLoader sl = new StageLoader(table);
4902 
4903         // testing initial state
4904         assertNotNull(table.getSkin());
4905         assertEquals("row1", VirtualFlowTestUtils.getCell(table, 0, 0).getText());
4906         assertEquals("row2", VirtualFlowTestUtils.getCell(table, 1, 0).getText());
4907         assertEquals("row3", VirtualFlowTestUtils.getCell(table, 2, 0).getText());
4908 
4909         // change row 0 (where "row1" currently resides), keeping same id.
4910         // Because 'set' is called, the control should update to the new content
4911         // without any user interaction
4912         RT22599_DataType data;
4913         initialData.set(0, data = new RT22599_DataType(0, "row1a"));
4914         Toolkit.getToolkit().firePulse();
4915         assertEquals("row1a", VirtualFlowTestUtils.getCell(table, 0, 0).getText());
4916 
4917         // change the row 0 (where we currently have "row1a") value directly.
4918         // Because there is no associated property, this won't be observed, so
4919         // the control should still show "row1a" rather than "row1b"
4920         data.text = "row1b";
4921         Toolkit.getToolkit().firePulse();
4922         assertEquals("row1a", VirtualFlowTestUtils.getCell(table, 0, 0).getText());
4923 
4924         // call refresh() to force a refresh of all visible cells
4925         table.refresh();
4926         Toolkit.getToolkit().firePulse();
4927         assertEquals("row1b", VirtualFlowTestUtils.getCell(table, 0, 0).getText());
4928 
4929         sl.dispose();
4930     }
4931 
4932     private static class RT22599_DataType {
4933         public int id = 0;
4934         public String text = "";
4935 
4936         public RT22599_DataType(int id, String text) {
4937             this.id = id;
4938             this.text = text;
4939         }
4940 
4941         @Override public boolean equals(Object obj) {
4942             if (obj == null) return false;
4943             return id == ((RT22599_DataType)obj).id;
4944         }
4945     }
4946 
4947     private int rt_39966_count = 0;
4948     @Test public void test_rt_39966() {
4949         ObservableList<String> initialData = FXCollections.observableArrayList("Hello World");
4950 
4951         TableColumn<String, String> col = new TableColumn<>("Header");
4952         col.setCellValueFactory(param -> new ReadOnlyStringWrapper(param.getValue()));
4953 
4954         TableView<String> table = new TableView<>(initialData);
4955         table.getColumns().addAll(col);
4956 
4957         StageLoader sl = new StageLoader(table);
4958 
4959         // initially there is no selection
4960         assertTrue(table.getSelectionModel().isEmpty());
4961 
4962         table.getSelectionModel().selectedItemProperty().addListener((value, s1, s2) -> {
4963             if (rt_39966_count == 0) {
4964                 rt_39966_count++;
4965                 assertFalse(table.getSelectionModel().isEmpty());
4966             } else {
4967                 assertTrue(table.getSelectionModel().isEmpty());
4968             }
4969         });
4970 
4971         // our assertion two lines down always succeeds. What fails is our
4972         // assertion above within the listener.
4973         table.getSelectionModel().select(0);
4974         assertFalse(table.getSelectionModel().isEmpty());
4975 
4976         initialData.remove(0);
4977         assertTrue(table.getSelectionModel().isEmpty());
4978 
4979         sl.dispose();
4980     }
4981 
4982     /**
4983      * Bullet 1: selected index must be updated
4984      * Corner case: last selected. Fails for core
4985      */
4986     @Test public void test_rt_40012_selectedAtLastOnDisjointRemoveItemsAbove() {
4987         ObservableList<String> items = FXCollections.observableArrayList("0", "1", "2", "3", "4", "5");
4988         TableView<String> stringTableView = new TableView<>(items);
4989         TableView.TableViewSelectionModel<?> sm = stringTableView.getSelectionModel();
4990 
4991         TableColumn<String,String> column = new TableColumn<>("Column");
4992         column.setCellValueFactory(cdf -> new ReadOnlyStringWrapper(cdf.getValue()));
4993         stringTableView.getColumns().add(column);
4994 
4995         int last = items.size() - 1;
4996 
4997         // selecting item "5"
4998         sm.select(last);
4999 
5000         // disjoint remove of 2 elements above the last selected
5001         // Removing "1" and "3"
5002         items.removeAll(items.get(1), items.get(3));
5003 
5004         // selection should move up two places such that it remains on item "5",
5005         // but in index (last - 2).
5006         int expected = last - 2;
5007         assertEquals("5", sm.getSelectedItem());
5008         assertEquals("selected index after disjoint removes above", expected, sm.getSelectedIndex());
5009     }
5010 
5011     /**
5012      * Variant of 1: if selectedIndex is not updated,
5013      * the old index is no longer valid
5014      * for accessing the items.
5015      */
5016     @Test public void test_rt_40012_accessSelectedAtLastOnDisjointRemoveItemsAbove() {
5017         ObservableList<String> items = FXCollections.observableArrayList("0", "1", "2", "3", "4", "5");
5018         TableView<String> stringTableView = new TableView<>(items);
5019         TableView.TableViewSelectionModel<?> sm = stringTableView.getSelectionModel();
5020 
5021         TableColumn<String,String> column = new TableColumn<>("Column");
5022         column.setCellValueFactory(cdf -> new ReadOnlyStringWrapper(cdf.getValue()));
5023         stringTableView.getColumns().add(column);
5024 
5025         int last = items.size() - 1;
5026 
5027         // selecting item "5"
5028         sm.select(last);
5029 
5030         // disjoint remove of 2 elements above the last selected
5031         items.removeAll(items.get(1), items.get(3));
5032         int selected = sm.getSelectedIndex();
5033         if (selected > -1) {
5034             items.get(selected);
5035         }
5036     }
5037 
5038     /**
5039      * Bullet 2: selectedIndex notification count
5040      *
5041      * Note that we don't use the corner case of having the last index selected
5042      * (which fails already on updating the index)
5043      */
5044     private int rt_40012_count = 0;
5045     @Test public void test_rt_40012_selectedIndexNotificationOnDisjointRemovesAbove() {
5046         ObservableList<String> items = FXCollections.observableArrayList("0", "1", "2", "3", "4", "5");
5047         TableView<String> stringTableView = new TableView<>(items);
5048         TableView.TableViewSelectionModel<?> sm = stringTableView.getSelectionModel();
5049 
5050         TableColumn<String,String> column = new TableColumn<>("Column");
5051         column.setCellValueFactory(cdf -> new ReadOnlyStringWrapper(cdf.getValue()));
5052         stringTableView.getColumns().add(column);
5053 
5054         int last = items.size() - 2;
5055         sm.select(last);
5056         assertEquals(last, sm.getSelectedIndex());
5057 
5058         rt_40012_count = 0;
5059         sm.selectedIndexProperty().addListener(o -> rt_40012_count++);
5060 
5061         // disjoint remove of 2 elements above the last selected
5062         items.removeAll(items.get(1), items.get(3));
5063         assertEquals("sanity: selectedIndex must be shifted by -2", last - 2, sm.getSelectedIndex());
5064         assertEquals("must fire single event on removes above", 1, rt_40012_count);
5065     }
5066 
5067     /**
5068      * Bullet 3: unchanged selectedItem must not fire change
5069      */
5070     @Test
5071     public void test_rt_40012_selectedItemNotificationOnDisjointRemovesAbove() {
5072         ObservableList<String> items = FXCollections.observableArrayList("0", "1", "2", "3", "4", "5");
5073         TableView<String> stringTableView = new TableView<>(items);
5074         TableView.TableViewSelectionModel<?> sm = stringTableView.getSelectionModel();
5075 
5076         TableColumn<String,String> column = new TableColumn<>("Column");
5077         column.setCellValueFactory(cdf -> new ReadOnlyStringWrapper(cdf.getValue()));
5078         stringTableView.getColumns().add(column);
5079 
5080         int last = items.size() - 2;
5081         Object lastItem = items.get(last);
5082         sm.select(last);
5083         assertEquals(lastItem, sm.getSelectedItem());
5084 
5085         rt_40012_count = 0;
5086         sm.selectedItemProperty().addListener(o -> rt_40012_count++);
5087 
5088         // disjoint remove of 2 elements above the last selected
5089         items.removeAll(items.get(1), items.get(3));
5090         assertEquals("sanity: selectedItem unchanged", lastItem, sm.getSelectedItem());
5091         assertEquals("must not fire on unchanged selected item", 0, rt_40012_count);
5092     }
5093 
5094     /**
5095      * ClearAndSelect fires invalid change event if selectedIndex is unchanged.
5096      */
5097     private int rt_40212_count = 0;
5098     @Test public void test_rt_40212() {
5099         final TableView<Number> table = new TableView<>();
5100         for (int i = 0; i < 10; i++) {
5101             table.getItems().add(i);
5102         }
5103 
5104         TableColumn<Number,Number> column = new TableColumn<>("Column");
5105         column.setCellValueFactory(cdf -> new ReadOnlyIntegerWrapper((int) cdf.getValue()));
5106         table.getColumns().add(column);
5107 
5108         MultipleSelectionModel<Number> sm = table.getSelectionModel();
5109         sm.setSelectionMode(SelectionMode.MULTIPLE);
5110 
5111         sm.selectRange(3, 5);
5112         int selected = sm.getSelectedIndex();
5113 
5114         sm.getSelectedIndices().addListener((ListChangeListener<Integer>) change -> {
5115             assertEquals("sanity: selectedIndex unchanged", selected, sm.getSelectedIndex());
5116             while(change.next()) {
5117                 assertEquals("single event on clearAndSelect already selected", 1, ++rt_40212_count);
5118 
5119                 boolean type = change.wasAdded() || change.wasRemoved() || change.wasPermutated() || change.wasUpdated();
5120                 assertTrue("at least one of the change types must be true", type);
5121             }
5122         });
5123 
5124         sm.clearAndSelect(selected);
5125     }
5126 
5127     @Test public void test_rt_40280() {
5128         final TableView<String> view = new TableView<>();
5129         StageLoader sl = new StageLoader(view);
5130         MultipleSelectionModelBaseShim.getFocusedIndex(view.getSelectionModel());
5131         view.getFocusModel().getFocusedIndex();
5132         sl.dispose();
5133     }
5134 
5135     /**
5136      * Test list change of selectedIndices on setIndices. Fails for core ..
5137      */
5138     @Test public void test_rt_40263() {
5139         final TableView<Integer> view = new TableView<>();
5140         for (int i = 0; i < 10; i++) {
5141             view.getItems().add(i);
5142         }
5143 
5144         MultipleSelectionModel<Integer> sm = view.getSelectionModel();
5145         sm.setSelectionMode(SelectionMode.MULTIPLE);
5146 
5147         int[] indices = new int[]{2, 5, 7};
5148         ListChangeListener<Integer> l = c -> {
5149             // firstly, we expect only one change
5150             int subChanges = 0;
5151             while(c.next()) {
5152                 subChanges++;
5153             }
5154             assertEquals(1, subChanges);
5155 
5156             // secondly, we expect the added size to be three, as that is the
5157             // number of items selected
5158             c.reset();
5159             c.next();
5160             System.out.println("Added items: " + c.getAddedSubList());
5161             assertEquals(indices.length, c.getAddedSize());
5162             assertArrayEquals(indices, c.getAddedSubList().stream().mapToInt(i -> i).toArray());
5163         };
5164         sm.getSelectedIndices().addListener(l);
5165         sm.selectIndices(indices[0], indices);
5166     }
5167 
5168     @Test public void test_rt_40319_toRight_toBottom()          { test_rt_40319(true, true, false);   }
5169     @Test public void test_rt_40319_toRight_toTop()             { test_rt_40319(true, false, false);  }
5170     @Test public void test_rt_40319_toLeft_toBottom()           { test_rt_40319(false, true, false);  }
5171     @Test public void test_rt_40319_toLeft_toTop()              { test_rt_40319(false, false, false); }
5172     @Test public void test_rt_40319_toRight_toBottom_useMouse() { test_rt_40319(true, true, true);    }
5173     @Test public void test_rt_40319_toRight_toTop_useMouse()    { test_rt_40319(true, false, true);   }
5174     @Test public void test_rt_40319_toLeft_toBottom_useMouse()  { test_rt_40319(false, true, true);   }
5175     @Test public void test_rt_40319_toLeft_toTop_useMouse()     { test_rt_40319(false, false, true);  }
5176 
5177     private void test_rt_40319(boolean toRight, boolean toBottom, boolean useMouse) {
5178         ObservableList<Person> p = FXCollections.observableArrayList();
5179         p.add(new Person("FirstName1", "LastName1", ""));
5180         p.add(new Person("FirstName2", "LastName2", ""));
5181         p.add(new Person("FirstName3", "LastName3", ""));
5182 
5183         TableView<Person> t = new TableView<>(p);
5184         sm = t.getSelectionModel();
5185         sm.setSelectionMode(SelectionMode.MULTIPLE);
5186 
5187         TableColumn<Person, String> c1 = new TableColumn<>("First Name");
5188         c1.setCellValueFactory(new PropertyValueFactory<>("firstname"));
5189         TableColumn<Person, String> c2 = new TableColumn<>("Last Name");
5190         c2.setCellValueFactory(new PropertyValueFactory<>("lastname"));
5191         t.getColumns().addAll(c1, c2);
5192 
5193         final int startIndex = toRight ? 0 : 2;
5194         final int endIndex = toRight ? 2 : 0;
5195         final TableColumn<Person,String> startColumn = toBottom ? c1 : c2;
5196         final TableColumn<Person,String> endColumn = toBottom ? c2 : c1;
5197 
5198         sm.select(startIndex, startColumn);
5199 
5200         if (useMouse) {
5201             Cell endCell = VirtualFlowTestUtils.getCell(t, endIndex, toRight ? 1 : 0);
5202             MouseEventFirer mouse = new MouseEventFirer(endCell);
5203             mouse.fireMousePressAndRelease(KeyModifier.SHIFT);
5204         } else {
5205             t.getSelectionModel().selectRange(startIndex, startColumn, endIndex, endColumn);
5206         }
5207 
5208         assertEquals(3, sm.getSelectedItems().size());
5209         assertEquals(3, sm.getSelectedIndices().size());
5210         assertEquals(3, sm.getSelectedCells().size());
5211     }
5212 
5213     private int rt_40546_count = 0;
5214     @Test public void test_rt_40546() {
5215         ObservableList<Person> p = FXCollections.observableArrayList();
5216 
5217         TableView<Person> t = new TableView<>(p);
5218         sm = t.getSelectionModel();
5219         sm.setSelectionMode(SelectionMode.MULTIPLE);
5220 
5221         TableColumn<Person, String> c1 = new TableColumn<>("First Name");
5222         c1.setCellValueFactory(new PropertyValueFactory<>("firstname"));
5223         TableColumn<Person, String> c2 = new TableColumn<>("Last Name");
5224         c2.setCellValueFactory(new PropertyValueFactory<>("lastname"));
5225         t.getColumns().addAll(c1, c2);
5226 
5227         p.add(new Person("FirstName1", "LastName1", ""));
5228         p.add(new Person("FirstName2", "LastName2", ""));
5229         p.add(new Person("FirstName3", "LastName3", ""));
5230 
5231         // add a listener - although we don't expect to hear anything
5232         sm.getSelectedItems().addListener((ListChangeListener) c -> {
5233             while (c.next()) {
5234                 rt_40546_count++;
5235             }
5236         });
5237 
5238         assertEquals(0, rt_40546_count);
5239 
5240         t.getSelectionModel().clearSelection();
5241         assertEquals(0, rt_40546_count);
5242 
5243         p.clear();
5244         assertEquals(0, rt_40546_count);
5245 
5246         p.add(new Person("FirstName1", "LastName1", ""));
5247         assertEquals(0, rt_40546_count);
5248         p.add(new Person("FirstName2", "LastName2", ""));
5249         assertEquals(0, rt_40546_count);
5250         p.add(new Person("FirstName3", "LastName3", ""));
5251         assertEquals(0, rt_40546_count);
5252     }
5253 
5254     @Test public void test_jdk_8144681_removeColumn() {
5255         TableView<Book> table = new TableView<>();
5256         Book books[] = {
5257                 new Book("Book 1", "Author 1", "Remark 1")
5258                 , new Book("Book 2", "Author 2", "Remark 2")
5259                 , new Book("Book 3", "Author 3", "Remark 3")
5260                 , new Book("Book 4", "Author 4", "Remark 4")
5261         };
5262         table.setItems(FXCollections.observableArrayList());
5263         table.getItems().addAll(books);
5264 
5265         String[] columns = { "title", "author", "remark" };
5266         for (String prop : columns) {
5267             TableColumn<Book, String> col = new TableColumn<>(prop);
5268             col.setCellValueFactory(new PropertyValueFactory<>(prop));
5269             table.getColumns().add(col);
5270         }
5271         table.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);
5272         table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
5273         table.getSelectionModel().setCellSelectionEnabled(true);
5274 
5275         table.getSelectionModel().selectAll();
5276 
5277         ControlTestUtils.runWithExceptionHandler(() -> table.getColumns().remove(2));
5278     }
5279 
5280     @Test public void test_jdk_8144681_moveColumn() {
5281         TableView<Book> table = new TableView<>();
5282         Book books[] = {
5283                 new Book("Book 1", "Author 1", "Remark 1")
5284                 , new Book("Book 2", "Author 2", "Remark 2")
5285                 , new Book("Book 3", "Author 3", "Remark 3")
5286                 , new Book("Book 4", "Author 4", "Remark 4")
5287         };
5288         table.setItems(FXCollections.observableArrayList());
5289         table.getItems().addAll(books);
5290 
5291         String[] columns = { "title", "author", "remark" };
5292         for (String prop : columns) {
5293             TableColumn<Book, String> col = new TableColumn<>(prop);
5294             col.setCellValueFactory(new PropertyValueFactory<>(prop));
5295             table.getColumns().add(col);
5296         }
5297         table.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);
5298         table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
5299         table.getSelectionModel().setCellSelectionEnabled(true);
5300 
5301         table.getSelectionModel().selectAll();
5302 
5303         ControlTestUtils.runWithExceptionHandler(() -> {
5304             table.getColumns().setAll(table.getColumns().get(0), table.getColumns().get(2), table.getColumns().get(1));
5305         });
5306     }
5307 
5308     public static class Book {
5309         private SimpleStringProperty title = new SimpleStringProperty();
5310         private SimpleStringProperty author = new SimpleStringProperty();
5311         private SimpleStringProperty remark = new SimpleStringProperty();
5312 
5313         public Book(String title, String author, String remark) {
5314             super();
5315             setTitle(title);
5316             setAuthor(author);
5317             setRemark(remark);
5318         }
5319 
5320         public SimpleStringProperty titleProperty() {
5321             return this.title;
5322         }
5323 
5324         public java.lang.String getTitle() {
5325             return this.titleProperty().get();
5326         }
5327 
5328         public void setTitle(final java.lang.String title) {
5329             this.titleProperty().set(title);
5330         }
5331 
5332         public SimpleStringProperty authorProperty() {
5333             return this.author;
5334         }
5335 
5336         public java.lang.String getAuthor() {
5337             return this.authorProperty().get();
5338         }
5339 
5340         public void setAuthor(final java.lang.String author) {
5341             this.authorProperty().set(author);
5342         }
5343 
5344         public SimpleStringProperty remarkProperty() {
5345             return this.remark;
5346         }
5347 
5348         public java.lang.String getRemark() {
5349             return this.remarkProperty().get();
5350         }
5351 
5352         public void setRemark(final java.lang.String remark) {
5353             this.remarkProperty().set(remark);
5354         }
5355 
5356         @Override
5357         public String toString() {
5358             return String.format("%s(%s) - %s", getTitle(), getAuthor(), getRemark());
5359         }
5360     }
5361 
5362     @Test public void test_8139460_heightDoesntShrinkAfterRemovingNestedColumns() throws Exception {
5363         TableColumn parent = new TableColumn("Parent column");
5364         TableColumn child = new TableColumn("Child column");
5365         parent.getColumns().add(child);
5366         table.getColumns().setAll(child);
5367         StageLoader sl = new StageLoader(table);
5368         TableHeaderRow row = VirtualFlowTestUtils.getTableHeaderRow(table);
5369         double initialHeight = row.getHeight();
5370         table.getColumns().setAll(parent);
5371         Toolkit.getToolkit().firePulse();
5372         double nestedHeaderHeight = row.getHeight();
5373         assertTrue("Nested column header should be larger.", nestedHeaderHeight > initialHeight);
5374         table.getColumns().setAll(child);
5375         Toolkit.getToolkit().firePulse();
5376         assertEquals("Header should shrink to initial size.", initialHeight, row.getHeight(), 0.01);
5377         sl.dispose();
5378     }
5379 }