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