// Get row's index by a specified row object
  private int getRowIndexByObject(T object) {
    for (int row = 0; row < dataDisplay.getRowCount(); row++) {
      if (dataDisplay.getVisibleItems().get(row).equals(object)) {
        return row;
      }
    }

    return -1;
  }
  // Select a row with regarding a specified shift
  private void selectRow(int shift) {
    if (selectedSet.isEmpty() || dataDisplay == null) {
      return;
    }

    int shiftSelectedRow = lastSelectedRow + shift;
    int nextRow =
        shiftSelectedRow >= 0
            ? shiftSelectedRow % dataDisplay.getRowCount()
            : shiftSelectedRow + dataDisplay.getRowCount();

    if (disabledRows.contains(nextRow)) {
      selectRow(shift > 0 ? shift + 1 : shift - 1);
      return;
    }

    setSelected(dataDisplay.getVisibleItems().get(nextRow), true);
  }
  // Select a range of multiple rows
  private void selectRange(T object) {
    // Select multiple selection origin row
    if (originSelectedRow == -1) {
      originSelectedRow = lastSelectedRow;
    }

    // Get start/end rows
    int selectedRow = getRowIndexByObject(object);
    int startRow = originSelectedRow < selectedRow ? originSelectedRow : selectedRow;
    int endRow = originSelectedRow > selectedRow ? originSelectedRow : selectedRow;

    // Clear current selection and select row in range
    clearSelection();
    for (int row = startRow; row <= endRow; row++) {
      selectionChanges.put(dataDisplay.getVisibleItems().get(row), true);
    }
  }
  void resolveChanges() {
    Set<Object> selectedKeys = selectedSet.keySet();
    List<Object> visibleKeys = new ArrayList<>();
    for (T visible : dataDisplay.getVisibleItems()) {
      visibleKeys.add(getKey(visible));
    }

    if (!visibleKeys.containsAll(selectedKeys)) {
      for (Map.Entry<Object, T> selectedEntry : selectedSet.entrySet()) {
        if (!visibleKeys.contains(selectedEntry.getKey())) {
          selectionChanges.put(selectedEntry.getValue(), false);
        }
      }
    }

    if (selectionChanges.isEmpty()) {
      return;
    }

    boolean changed = false;
    for (Map.Entry<T, Boolean> entry : selectionChanges.entrySet()) {
      T object = entry.getKey();
      boolean selected = entry.getValue();

      Object key = getKey(object);
      T oldValue = selectedSet.get(key);
      if (selected) {
        if (oldValue == null || !oldValue.equals(object)) {
          selectedSet.put(getKey(object), object);
          changed = true;
        }
      } else {
        if (oldValue != null) {
          selectedSet.remove(key);
          changed = true;
        }
      }
    }
    selectionChanges.clear();

    if (changed) {
      SelectionChangeEvent.fire(this);
    }
  }
 private T lastItem() {
   return dataDisplay.getVisibleItems().get(dataDisplay.getVisibleItems().size() - 1);
 }
 private T firstItem() {
   return dataDisplay.getVisibleItems().get(0);
 }