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