/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.bearsoft.gui.grid.rows;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.swing.DefaultListSelectionModel;
import javax.swing.ListSelectionModel;
import javax.swing.RowSorter;
import javax.swing.RowSorter.SortKey;
import javax.swing.SortOrder;
import javax.swing.table.TableModel;
/**
*
* @author mg
* @param <M>
*/
public class TabularRowsSorter<M extends TableModel> extends RowSorter<M> {
public boolean applySelection(ListSelectionModel aSelection) {
boolean res = true;
if (viewSelection != null) {
viewSelection.clearSelection();
for (int irow = aSelection.getMinSelectionIndex(); irow <= aSelection.getMaxSelectionIndex(); irow++) {
if (aSelection.isSelectedIndex(irow)) {
if (irow >= 0 && irow < getModelRowCount()) {
int viewRow = convertRowIndexToView(irow);
if (viewRow >= 0 && viewRow < getViewRowCount()) {
viewSelection.addSelectionInterval(viewRow, viewRow);
if (!viewSelection.isSelectedIndex(viewRow)) {
res = false;
}
} else {
res = false;
}
} else {
res = false;
}
}
}
}
return res;
}
public ListSelectionModel saveSelection() {
ListSelectionModel cacheSelection = new DefaultListSelectionModel();
if (viewSelection != null) {
for (int irow = viewSelection.getMinSelectionIndex(); irow <= viewSelection.getMaxSelectionIndex(); irow++) {
if (viewSelection.isSelectedIndex(irow)) {
int modelRow = convertRowIndexToModel(irow);
cacheSelection.addSelectionInterval(modelRow, modelRow);
}
}
}
return cacheSelection;
}
protected class SorterRowComparator implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return compareRows(o1, o2);
}
}
protected M model;
protected List<? extends SortKey> criteria = new ArrayList<>();
protected int modelRowCount = -1;
protected int viewToModel[] = null;
protected int modelToView[] = null;
protected ListSelectionModel viewSelection;
public TabularRowsSorter(M aModel, ListSelectionModel aViewSelection) {
super();
model = aModel;
viewSelection = aViewSelection;
}
@Override
public M getModel() {
return model;
}
private boolean isColumnValid(int column) {
return column >= 0 && column < model.getColumnCount();
}
private void checkColumn(int column) {
if (!isColumnValid(column)) {
throw new IndexOutOfBoundsException(
"column beyond range of TableModel");
}
}
public boolean isSortingCriteria(int column) {
checkColumn(column);
for (int i = 0; i < criteria.size(); i++) {
SortKey key = criteria.get(i);
if (key.getColumn() == column) {
return true;
}
}
return false;
}
@Override
public void toggleSortOrder(int column) {
checkColumn(column);
List<SortKey> toMutate = new ArrayList<>(criteria);
boolean found = false;
for (int i = 0; i < toMutate.size(); i++) {
SortKey key = toMutate.get(i);
if (key.getColumn() == column) {
SortOrder newSortOrder = null;
if (key.getSortOrder() == SortOrder.ASCENDING) {
newSortOrder = SortOrder.DESCENDING;
} else if (key.getSortOrder() == SortOrder.DESCENDING) {
newSortOrder = SortOrder.UNSORTED;
} else if (key.getSortOrder() == SortOrder.UNSORTED) {
newSortOrder = SortOrder.ASCENDING;
}
assert newSortOrder != null;
toMutate.set(i, new SortKey(key.getColumn(),
newSortOrder));
found = true;
break;
}
}
if (!found) {
toMutate.add(new SortKey(column, SortOrder.ASCENDING));
}
setSortKeys(toMutate);
}
@Override
public void setSortKeys(List<? extends SortKey> keys) {
criteria = keys;
sort();
}
@Override
public List<? extends SortKey> getSortKeys() {
return Collections.unmodifiableList(criteria);
}
@Override
public int convertRowIndexToModel(int index) {
if (viewToModel != null && index >= 0 && index < viewToModel.length) {
if (isSorted()) {
return viewToModel[index];
} else {
return index;
}
} else {
return index;
}
}
@Override
public int convertRowIndexToView(int index) {
if (modelToView != null && index >= 0 && index < modelToView.length) {
if (isSorted()) {
return modelToView[index];
} else {
return index;
}
} else {
return index;
}
}
@Override
public int getViewRowCount() {
return model.getRowCount();
}
@Override
public int getModelRowCount() {
return model.getRowCount();
}
@Override
public void modelStructureChanged() {
if (isSorted()) {
unsort();
}
}
@Override
public void allRowsChanged() {
if (isSorted()) {
sort();
}
}
@Override
public void rowsInserted(int firstRow, int endRow) {
if (isSorted()) {
sort();
}
}
@Override
public void rowsDeleted(int firstRow, int endRow) {
if (isSorted()) {
sort();
}
}
@Override
public void rowsUpdated(int firstRow, int endRow) {
if (isSorted()) {
sort();
}
}
@Override
public void rowsUpdated(int firstRow, int endRow, int column) {
if (isSorted() && isSortingCriteria(column)) {
sort();
}
}
private boolean isSorted() {
return modelToView != null;
}
public int compareRows(int row1, int row2) {
int order = 0;
for (int i = 0; i < criteria.size(); i++) {
SortKey key = criteria.get(i);
if (key.getSortOrder() != SortOrder.UNSORTED) {
Object o1 = model.getValueAt(row1, key.getColumn());
Object o2 = model.getValueAt(row2, key.getColumn());
if (o1 instanceof Comparable<?> && o2 instanceof Comparable<?>) {
Comparable<Object> c1 = (Comparable<Object>) o1;
Comparable<Object> c2 = (Comparable<Object>) o2;
order = c1.compareTo(c2);
} else if (o1 == null && o2 != null) {
order = 1;
} else if (o1 != null && o2 == null) {
order = -1;
}
order = key.getSortOrder() == SortOrder.DESCENDING ? -order : order;
if (order != 0) {
break;
}
}
}
return order;
}
private void sort() {
ListSelectionModel savedSelection = saveSelection();
try {
modelRowCount = model.getRowCount();
Integer[] modelRows = new Integer[modelRowCount];
for (int i = 0; i < modelRows.length; i++) {
modelRows[i] = i;
}
Arrays.sort(modelRows, new SorterRowComparator());
viewToModel = new int[modelRows.length];
modelToView = new int[modelRows.length];
for (int i = 0; i < modelRows.length; i++) {
viewToModel[i] = modelRows[i];
modelToView[modelRows[i]] = i;
}
fireSortOrderChanged();
} finally {
applySelection(savedSelection);
}
}
private void unsort() {
ListSelectionModel savedSelection = saveSelection();
try {
modelRowCount = -1;
criteria.clear();
viewToModel = null;
modelToView = null;
fireSortOrderChanged();
} finally {
applySelection(savedSelection);
}
}
}