I am using JTable with an empty row at the bottom of the table in order to give the ability of adding new line to the table.
After insert or writing data in the empt
I found another solution by subclassing only TableRowSorter.
From the DefaultRowSorter documentation we know :
The Comparator is never passed null
When subclassing DefaultRowSorter.ModelWrapper, we can return a special Null-Object and create a custom comparator handling that value.
Here follows my code. Probably it is not as efficient as a custom RowSorter implementation and it may still contain some bugs, I did not test everything, but for my requirements it works.
class EmptyTableRowSorter extends TableRowSorter {
private static final EmptyValue emptyValue = new EmptyValue();
public EmptyTableRowSorter(M model) {
super(model);
}
@Override
public void modelStructureChanged() {
// deletes comparators, so we must set again
super.modelStructureChanged();
M model = getModelWrapper().getModel();
for (int i = 0; i < model.getColumnCount(); i++) {
Comparator> comparator = this.getComparator(i);
if (comparator != null) {
Comparator wrapper = new EmptyValueComparator(comparator, this, i);
this.setComparator(i, wrapper);
}
}
}
@Override
public void setModel(M model) {
// also calls setModelWrapper method
super.setModel(model);
ModelWrapper modelWrapper = getModelWrapper();
EmptyTableModelWrapper emptyTableModelWrapper = new EmptyTableModelWrapper(modelWrapper);
// calls modelStructureChanged method
setModelWrapper(emptyTableModelWrapper);
}
/**
* The DefaulRowSorter implementation does not pass null values from the table
* to the comparator.
* This implementation is a wrapper around the default ModelWrapper,
* returning a non null object for our empty row that our comparator can handle.
*/
private class EmptyTableModelWrapper extends DefaultRowSorter.ModelWrapper {
private final DefaultRowSorter.ModelWrapper modelWrapperImplementation;
public EmptyTableModelWrapper(ModelWrapper modelWrapperImplementation) {
this.modelWrapperImplementation = modelWrapperImplementation;
}
@Override
public Object getModel() {
return modelWrapperImplementation.getModel();
}
@Override
public int getColumnCount() {
return modelWrapperImplementation.getColumnCount();
}
@Override
public int getRowCount() {
return modelWrapperImplementation.getRowCount();
}
@Override
public Object getValueAt(int row, int column) {
M model = EmptyTableRowSorter.this.getModel();
// my model has the empty row always at the end,
// change this depending on your needs
int lastRow = model.getRowCount() - 1;
if (row == lastRow) {
return emptyValue;
}
return modelWrapperImplementation.getValueAt(row, column);
}
//@Override
//public String getStringValueAt(int row, int column) {
// // also override this if there is no comparator definied for a column
//}
@Override
public Object getIdentifier(int row) {
return modelWrapperImplementation.getIdentifier(row);
}
}
/**
* This is a wrapper around another comparator.
* We handle our empty value and if none, we invoke the base comparator.
*/
private class EmptyValueComparator implements Comparator {
private final Comparator defaultComparator;
private final TableRowSorter tableRowSorter;
private final int columnIndex;
public EmptyValueComparator(Comparator defaultComparator, TableRowSorter tableRowSorter, int columnIndex) {
this.defaultComparator = defaultComparator;
this.tableRowSorter = tableRowSorter;
this.columnIndex = columnIndex;
}
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof EmptyValue && o2 instanceof EmptyValue) {
return 0;
}
if (o1 instanceof EmptyValue) {
return adjustSortOrder(1);
}
if (o2 instanceof EmptyValue) {
return adjustSortOrder(-1);
}
return defaultComparator.compare(o1, o2);
}
/**
* Changes the result so that the empty row is always at the end,
* regardless of the sort order.
*/
private int adjustSortOrder(int result) {
List sortKeys = tableRowSorter.getSortKeys();
for (Object sortKeyObject : sortKeys) {
SortKey sortKey = (SortKey) sortKeyObject;
if (sortKey.getColumn() == columnIndex) {
SortOrder sortOrder = sortKey.getSortOrder();
if (sortOrder == SortOrder.DESCENDING) {
result *= -1;
}
return result;
}
}
return result;
}
}
private static class EmptyValue {}
}
Now you can enable sorting in your table.
JTable table = ...;
TableRowSorter tableRowSorter = new EmptyTableRowSorter(table.getModel());
table.setRowSorter(tableRowSorter);