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