Methods Summary |
---|
private void | allChanged()
modelToView = null;
viewToModel = null;
comparators = null;
isSortable = null;
if (isUnsorted()) {
// Keys are already empty, to force a resort we have to
// call sort
sort();
} else {
setSortKeys(null);
}
|
public void | allRowsChanged(){@inheritDoc}
modelRowCount = getModelWrapper().getRowCount();
sort();
|
private void | cacheSortKeys(java.util.List keys)Caches the sort keys before a sort.
int keySize = keys.size();
sortComparators = new Comparator[keySize];
for (int i = 0; i < keySize; i++) {
sortComparators[i] = getComparator0(keys.get(i).getColumn());
}
cachedSortKeys = keys.toArray(new SortKey[keySize]);
|
private void | checkAgainstModel(int firstRow, int endRow)
if (firstRow > endRow || firstRow < 0 || endRow < 0 ||
firstRow > modelRowCount) {
throw new IndexOutOfBoundsException("Invalid range");
}
|
private void | checkColumn(int column)
if (column < 0 || column >= getModelWrapper().getColumnCount()) {
throw new IndexOutOfBoundsException(
"column beyond range of TableModel");
}
|
private int | compare(int model1, int model2)
int column;
SortOrder sortOrder;
Object v1, v2;
int result;
for (int counter = 0; counter < cachedSortKeys.length; counter++) {
column = cachedSortKeys[counter].getColumn();
sortOrder = cachedSortKeys[counter].getSortOrder();
if (sortOrder == SortOrder.UNSORTED) {
result = model1 - model2;
} else {
// v1 != null && v2 != null
if (useToString[column]) {
v1 = getModelWrapper().getStringValueAt(model1, column);
v2 = getModelWrapper().getStringValueAt(model2, column);
} else {
v1 = getModelWrapper().getValueAt(model1, column);
v2 = getModelWrapper().getValueAt(model2, column);
}
// Treat nulls as < then non-null
if (v1 == null) {
if (v2 == null) {
result = 0;
} else {
result = -1;
}
} else if (v2 == null) {
result = 1;
} else {
result = sortComparators[counter].compare(v1, v2);
}
if (sortOrder == SortOrder.DESCENDING) {
result *= -1;
}
}
if (result != 0) {
return result;
}
}
// If we get here, they're equal. Fallback to model order.
return model1 - model2;
|
public int | convertRowIndexToModel(int index){@inheritDoc}
if (viewToModel == null) {
if (index < 0 || index >= getModelWrapper().getRowCount()) {
throw new IndexOutOfBoundsException("Invalid index");
}
return index;
}
return viewToModel[index].modelIndex;
|
public int | convertRowIndexToView(int index){@inheritDoc}
if (modelToView == null) {
if (index < 0 || index >= getModelWrapper().getRowCount()) {
throw new IndexOutOfBoundsException("Invalid index");
}
return index;
}
return modelToView[index];
|
private void | createModelToView(int rowCount)Makes sure the modelToView array is of size rowCount.
if (modelToView == null || modelToView.length != rowCount) {
modelToView = new int[rowCount];
}
|
private void | createViewToModel(int rowCount)Resets the viewToModel array to be of size rowCount.
int recreateFrom = 0;
if (viewToModel != null) {
recreateFrom = Math.min(rowCount, viewToModel.length);
if (viewToModel.length != rowCount) {
Row[] oldViewToModel = viewToModel;
viewToModel = new Row[rowCount];
System.arraycopy(oldViewToModel, 0, viewToModel,
0, recreateFrom);
}
}
else {
viewToModel = new Row[rowCount];
}
int i;
for (i = 0; i < recreateFrom; i++) {
viewToModel[i].modelIndex = i;
}
for (i = recreateFrom; i < rowCount; i++) {
viewToModel[i] = new Row(this, i);
}
|
public java.util.Comparator | getComparator(int column)Returns the Comparator for the specified
column. This will return null if a Comparator
has not been specified for the column.
checkColumn(column);
if (comparators != null) {
return comparators[column];
}
return null;
|
private java.util.Comparator | getComparator0(int column)
Comparator comparator = getComparator(column);
if (comparator != null) {
return comparator;
}
// This should be ok as useToString(column) should have returned
// true in this case.
return Collator.getInstance();
|
private javax.swing.RowFilter$Entry | getFilterEntry(int modelIndex)
if (filterEntry == null) {
filterEntry = new FilterEntry();
}
filterEntry.modelIndex = modelIndex;
return filterEntry;
|
public int | getMaxSortKeys()Returns the maximum number of sort keys.
return maxSortKeys;
|
public final M | getModel()Returns the underlying model.
return getModelWrapper().getModel();
|
public int | getModelRowCount(){@inheritDoc}
return getModelWrapper().getRowCount();
|
protected final javax.swing.DefaultRowSorter$ModelWrapper | getModelWrapper()Returns the model wrapper providing the data that is being sorted and
filtered.
return modelWrapper;
|
public javax.swing.RowFilter | getRowFilter()Returns the filter that determines which rows, if any, should
be hidden from view.
return filter;
|
public java.util.List | getSortKeys()Returns the current sort keys. This returns an unmodifiable
{@code non-null List}. If you need to change the sort keys,
make a copy of the returned {@code List}, mutate the copy
and invoke {@code setSortKeys} with the new list.
return sortKeys;
|
public boolean | getSortsOnUpdates()Returns true if a sort should happen when the underlying
model is updated; otherwise, returns false.
return sortsOnUpdates;
|
public int | getViewRowCount(){@inheritDoc}
if (viewToModel != null) {
// When filtering this may differ from getModelWrapper().getRowCount()
return viewToModel.length;
}
return getModelWrapper().getRowCount();
|
private int[] | getViewToModelAsInts(javax.swing.DefaultRowSorter$Row[] viewToModel)
if (viewToModel != null) {
int[] viewToModelI = new int[viewToModel.length];
for (int i = viewToModel.length - 1; i >= 0; i--) {
viewToModelI[i] = viewToModel[i].modelIndex;
}
return viewToModelI;
}
return new int[0];
|
private boolean | include(int row)Returns true if the specified row should be included.
RowFilter<? super M, ? super I> filter = getRowFilter();
if (filter != null) {
return filter.include(getFilterEntry(row));
}
// null filter, always include the row.
return true;
|
private void | initializeFilteredMapping()Resets the viewToModel and modelToView mappings based on
the current Filter.
int rowCount = getModelWrapper().getRowCount();
int i, j;
int excludedCount = 0;
// Update model -> view
createModelToView(rowCount);
for (i = 0; i < rowCount; i++) {
if (include(i)) {
modelToView[i] = i - excludedCount;
}
else {
modelToView[i] = -1;
excludedCount++;
}
}
// Update view -> model
createViewToModel(rowCount - excludedCount);
for (i = 0, j = 0; i < rowCount; i++) {
if (modelToView[i] != -1) {
viewToModel[j++].modelIndex = i;
}
}
|
private void | insertInOrder(java.util.List toAdd, javax.swing.DefaultRowSorter$Row[] current)Insets new set of entries.
int last = 0;
int index;
int max = toAdd.size();
for (int i = 0; i < max; i++) {
index = Arrays.binarySearch(current, toAdd.get(i));
if (index < 0) {
index = -1 - index;
}
System.arraycopy(current, last,
viewToModel, last + i, index - last);
viewToModel[index + i] = toAdd.get(i);
last = index;
}
System.arraycopy(current, last, viewToModel, last + max,
current.length - last);
|
public boolean | isSortable(int column)Returns true if the specified column is sortable; otherwise, false.
checkColumn(column);
return (isSortable == null) ? true : isSortable[column];
|
private boolean | isTransformed()Whether not we are filtering/sorting.
return (viewToModel != null);
|
private boolean | isUnsorted()
List<? extends SortKey> keys = getSortKeys();
int keySize = keys.size();
return (keySize == 0 || keys.get(0).getSortOrder() ==
SortOrder.UNSORTED);
|
public void | modelStructureChanged(){@inheritDoc}
allChanged();
modelRowCount = getModelWrapper().getRowCount();
|
public void | rowsDeleted(int firstRow, int endRow){@inheritDoc}
checkAgainstModel(firstRow, endRow);
if (firstRow >= modelRowCount || endRow >= modelRowCount) {
throw new IndexOutOfBoundsException("Invalid range");
}
modelRowCount = getModelWrapper().getRowCount();
if (shouldOptimizeChange(firstRow, endRow)) {
rowsDeleted0(firstRow, endRow);
}
|
private void | rowsDeleted0(int firstRow, int lastRow)
int[] oldViewToModel = getViewToModelAsInts(viewToModel);
int removedFromView = 0;
int i;
int viewIndex;
// Figure out how many visible rows are going to be effected.
for (i = firstRow; i <= lastRow; i++) {
viewIndex = modelToView[i];
if (viewIndex != -1) {
removedFromView++;
viewToModel[viewIndex] = null;
}
}
// Update the model index of rows after the effected region
int delta = lastRow - firstRow + 1;
for (i = modelToView.length - 1; i > lastRow; i--) {
viewIndex = modelToView[i];
if (viewIndex != -1) {
viewToModel[viewIndex].modelIndex -= delta;
}
}
// Then patch up the viewToModel array
if (removedFromView > 0) {
Row[] newViewToModel = new Row[viewToModel.length -
removedFromView];
int newIndex = 0;
int last = 0;
for (i = 0; i < viewToModel.length; i++) {
if (viewToModel[i] == null) {
System.arraycopy(viewToModel, last,
newViewToModel, newIndex, i - last);
newIndex += (i - last);
last = i + 1;
}
}
System.arraycopy(viewToModel, last,
newViewToModel, newIndex, viewToModel.length - last);
viewToModel = newViewToModel;
}
// Update the modelToView mapping
createModelToView(getModelWrapper().getRowCount());
setModelToViewFromViewToModel(true);
// And notify of change
fireRowSorterChanged(oldViewToModel);
|
public void | rowsInserted(int firstRow, int endRow){@inheritDoc}
checkAgainstModel(firstRow, endRow);
int newModelRowCount = getModelWrapper().getRowCount();
if (endRow >= newModelRowCount) {
throw new IndexOutOfBoundsException("Invalid range");
}
modelRowCount = newModelRowCount;
if (shouldOptimizeChange(firstRow, endRow)) {
rowsInserted0(firstRow, endRow);
}
|
private void | rowsInserted0(int firstRow, int lastRow)
int[] oldViewToModel = getViewToModelAsInts(viewToModel);
int i;
int delta = (lastRow - firstRow) + 1;
List<Row> added = new ArrayList<Row>(delta);
// Build the list of Rows to add into added
for (i = firstRow; i <= lastRow; i++) {
if (include(i)) {
added.add(new Row(this, i));
}
}
// Adjust the model index of rows after the effected region
int viewIndex;
for (i = modelToView.length - 1; i >= firstRow; i--) {
viewIndex = modelToView[i];
if (viewIndex != -1) {
viewToModel[viewIndex].modelIndex += delta;
}
}
// Insert newly added rows into viewToModel
if (added.size() > 0) {
Collections.sort(added);
Row[] lastViewToModel = viewToModel;
viewToModel = new Row[viewToModel.length + added.size()];
insertInOrder(added, lastViewToModel);
}
// Update modelToView
createModelToView(getModelWrapper().getRowCount());
setModelToViewFromViewToModel(true);
// Notify of change
fireRowSorterChanged(oldViewToModel);
|
public void | rowsUpdated(int firstRow, int endRow){@inheritDoc}
checkAgainstModel(firstRow, endRow);
if (firstRow >= modelRowCount || endRow >= modelRowCount) {
throw new IndexOutOfBoundsException("Invalid range");
}
if (getSortsOnUpdates()) {
if (shouldOptimizeChange(firstRow, endRow)) {
rowsUpdated0(firstRow, endRow);
}
}
else {
sorted = false;
}
|
public void | rowsUpdated(int firstRow, int endRow, int column){@inheritDoc}
checkColumn(column);
rowsUpdated(firstRow, endRow);
|
private void | rowsUpdated0(int firstRow, int lastRow)
int[] oldViewToModel = getViewToModelAsInts(viewToModel);
int i, j;
int delta = lastRow - firstRow + 1;
int modelIndex;
int last;
int index;
if (getRowFilter() == null) {
// Sorting only:
// Remove the effected rows
Row[] updated = new Row[delta];
for (j = 0, i = firstRow; i <= lastRow; i++, j++) {
updated[j] = viewToModel[modelToView[i]];
}
// Sort the update rows
Arrays.sort(updated);
// Build the intermediary array: the array of
// viewToModel without the effected rows.
Row[] intermediary = new Row[viewToModel.length - delta];
for (i = 0, j = 0; i < viewToModel.length; i++) {
modelIndex = viewToModel[i].modelIndex;
if (modelIndex < firstRow || modelIndex > lastRow) {
intermediary[j++] = viewToModel[i];
}
}
// Build the new viewToModel
insertInOrder(Arrays.asList(updated), intermediary);
// Update modelToView
setModelToViewFromViewToModel(false);
}
else {
// Sorting & filtering.
// Remove the effected rows, adding them to updated and setting
// modelToView to -2 for any rows that were not filtered out
List<Row> updated = new ArrayList<Row>(delta);
int newlyVisible = 0;
int newlyHidden = 0;
int effected = 0;
for (i = firstRow; i <= lastRow; i++) {
if (modelToView[i] == -1) {
// This row was filtered out
if (include(i)) {
// No longer filtered
updated.add(new Row(this, i));
newlyVisible++;
}
}
else {
// This row was visible, make sure it should still be
// visible.
if (!include(i)) {
newlyHidden++;
}
else {
updated.add(viewToModel[modelToView[i]]);
}
modelToView[i] = -2;
effected++;
}
}
// Sort the updated rows
Collections.sort(updated);
// Build the intermediary array: the array of
// viewToModel without the updated rows.
Row[] intermediary = new Row[viewToModel.length - effected];
for (i = 0, j = 0; i < viewToModel.length; i++) {
modelIndex = viewToModel[i].modelIndex;
if (modelToView[modelIndex] != -2) {
intermediary[j++] = viewToModel[i];
}
}
// Recreate viewToModel, if necessary
if (newlyVisible != newlyHidden) {
viewToModel = new Row[viewToModel.length + newlyVisible -
newlyHidden];
}
// Rebuild the new viewToModel array
insertInOrder(updated, intermediary);
// Update modelToView
setModelToViewFromViewToModel(true);
}
// And finally fire a sort event.
fireRowSorterChanged(oldViewToModel);
|
public void | setComparator(int column, java.util.Comparator comparator)Sets the Comparator to use when sorting the specified
column. This does not trigger a sort. If you want to sort after
setting the comparator you need to explicitly invoke sort .
checkColumn(column);
if (comparators == null) {
comparators = new Comparator[getModelWrapper().getColumnCount()];
}
comparators[column] = comparator;
|
public void | setMaxSortKeys(int max)Sets the maximum number of sort keys. The number of sort keys
determines how equal values are resolved when sorting. For
example, assume a table row sorter is created and
setMaxSortKeys(2) is invoked on it. The user
clicks the header for column 1, causing the table rows to be
sorted based on the items in column 1. Next, the user clicks
the header for column 2, causing the table to be sorted based
on the items in column 2; if any items in column 2 are equal,
then those particular rows are ordered based on the items in
column 1. In this case, we say that the rows are primarily
sorted on column 2, and secondarily on column 1. If the user
then clicks the header for column 3, then the items are
primarily sorted on column 3 and secondarily sorted on column
2. Because the maximum number of sort keys has been set to 2
with setMaxSortKeys , column 1 no longer has an
effect on the order.
The maximum number of sort keys is enforced by
toggleSortOrder . You can specify more sort
keys by invoking setSortKeys directly and they will
all be honored. However if toggleSortOrder is subsequently
invoked the maximum number of sort keys will be enforced.
The default value is 3.
if (max < 1) {
throw new IllegalArgumentException("Invalid max");
}
maxSortKeys = max;
|
private void | setModelToViewFromViewToModel(boolean unsetFirst)Refreshes the modelToView mapping from that of viewToModel.
If unsetFirst is true, all indices in modelToView are
first set to -1.
int i;
if (unsetFirst) {
for (i = modelToView.length - 1; i >= 0; i--) {
modelToView[i] = -1;
}
}
for (i = viewToModel.length - 1; i >= 0; i--) {
modelToView[viewToModel[i].modelIndex] = i;
}
|
protected final void | setModelWrapper(javax.swing.DefaultRowSorter$ModelWrapper modelWrapper)Sets the model wrapper providing the data that is being sorted and
filtered.
if (modelWrapper == null) {
throw new IllegalArgumentException(
"modelWrapper most be non-null");
}
ModelWrapper<M,I> last = this.modelWrapper;
this.modelWrapper = modelWrapper;
if (last != null) {
modelStructureChanged();
} else {
// If last is null, we're in the constructor. If we're in
// the constructor we don't want to call to overridable methods.
modelRowCount = getModelWrapper().getRowCount();
}
|
public void | setRowFilter(javax.swing.RowFilter filter)Sets the filter that determines which rows, if any, should be
hidden from the view. The filter is applied before sorting. A value
of null indicates all values from the model should be
included.
RowFilter 's include method is passed an
Entry that wraps the underlying model. The number
of columns in the Entry corresponds to the
number of columns in the ModelWrapper . The identifier
comes from the ModelWrapper as well.
This method triggers a sort.
this.filter = filter;
sort();
|
public void | setSortKeys(java.util.List sortKeys)Sets the sort keys. This creates a copy of the supplied
{@code List}; subsequent changes to the supplied
{@code List} do not effect this {@code DefaultRowSorter}.
If the sort keys have changed this triggers a sort.
List<SortKey> old = this.sortKeys;
if (sortKeys != null && sortKeys.size() > 0) {
int max = getModelWrapper().getColumnCount();
for (SortKey key : sortKeys) {
if (key == null || key.getColumn() < 0 ||
key.getColumn() >= max) {
throw new IllegalArgumentException("Invalid SortKey");
}
}
this.sortKeys = Collections.unmodifiableList(
new ArrayList<SortKey>(sortKeys));
}
else {
this.sortKeys = Collections.emptyList();
}
if (!this.sortKeys.equals(old)) {
fireSortOrderChanged();
if (viewToModel == null) {
// Currently unsorted, use sort so that internal fields
// are correctly set.
sort();
} else {
sortExistingData();
}
}
|
public void | setSortable(int column, boolean sortable)Sets whether or not the specified column is sortable. The specified
value is only checked when toggleSortOrder is invoked.
It is still possible to sort on a column that has been marked as
unsortable by directly setting the sort keys. The default is
true.
checkColumn(column);
if (isSortable == null) {
isSortable = new boolean[getModelWrapper().getColumnCount()];
for (int i = isSortable.length - 1; i >= 0; i--) {
isSortable[i] = true;
}
}
isSortable[column] = sortable;
|
public void | setSortsOnUpdates(boolean sortsOnUpdates)If true, specifies that a sort should happen when the underlying
model is updated (rowsUpdated is invoked). For
example, if this is true and the user edits an entry the
location of that item in the view may change. The default is
false.
this.sortsOnUpdates = sortsOnUpdates;
|
private boolean | shouldOptimizeChange(int firstRow, int lastRow)Returns true if we should try and optimize the processing of the
TableModelEvent . If this returns false, assume the
event was dealt with and no further processing needs to happen.
if (!isTransformed()) {
// Not transformed, nothing to do.
return false;
}
if (!sorted || (lastRow - firstRow) > viewToModel.length / 10) {
// We either weren't sorted, or to much changed, sort it all
sort();
return false;
}
return true;
|
public void | sort()Sorts and filters the rows in the view based on the sort keys
of the columns currently being sorted and the filter, if any,
associated with this sorter. An empty sortKeys list
indicates that the view should unsorted, the same as the model.
sorted = true;
int[] lastViewToModel = getViewToModelAsInts(viewToModel);
updateUseToString();
if (isUnsorted()) {
// Unsorted
cachedSortKeys = new SortKey[0];
if (getRowFilter() == null) {
// No filter & unsorted
if (viewToModel != null) {
// sorted -> unsorted
viewToModel = null;
modelToView = null;
}
else {
// unsorted -> unsorted
// No need to do anything.
return;
}
}
else {
// There is filter, reset mappings
initializeFilteredMapping();
}
}
else {
cacheSortKeys(getSortKeys());
if (getRowFilter() != null) {
initializeFilteredMapping();
}
else {
createModelToView(getModelWrapper().getRowCount());
createViewToModel(getModelWrapper().getRowCount());
}
// sort them
Arrays.sort(viewToModel);
// Update the modelToView array
setModelToViewFromViewToModel(false);
}
fireRowSorterChanged(lastViewToModel);
|
private void | sortExistingData()Sorts the existing filtered data. This should only be used if
the filter hasn't changed.
int[] lastViewToModel = getViewToModelAsInts(viewToModel);
updateUseToString();
cacheSortKeys(getSortKeys());
if (isUnsorted()) {
if (getRowFilter() == null) {
viewToModel = null;
modelToView = null;
} else {
int included = 0;
for (int i = 0; i < modelToView.length; i++) {
if (modelToView[i] != -1) {
viewToModel[included].modelIndex = i;
modelToView[i] = included++;
}
}
}
} else {
// sort the data
Arrays.sort(viewToModel);
// Update the modelToView array
setModelToViewFromViewToModel(false);
}
fireRowSorterChanged(lastViewToModel);
|
private SortKey | toggle(SortKey key)
if (key.getSortOrder() == SortOrder.ASCENDING) {
return new SortKey(key.getColumn(), SortOrder.DESCENDING);
}
return new SortKey(key.getColumn(), SortOrder.ASCENDING);
|
public void | toggleSortOrder(int column)Reverses the sort order from ascending to descending (or
descending to ascending) if the specified column is already the
primary sorted column; otherwise, makes the specified column
the primary sorted column, with an ascending sort order. If
the specified column is not sortable, this method has no
effect.
checkColumn(column);
if (isSortable(column)) {
List<SortKey> keys = new ArrayList<SortKey>(getSortKeys());
SortKey sortKey;
int sortIndex;
for (sortIndex = keys.size() - 1; sortIndex >= 0; sortIndex--) {
if (keys.get(sortIndex).getColumn() == column) {
break;
}
}
if (sortIndex == -1) {
// Key doesn't exist
sortKey = new SortKey(column, SortOrder.ASCENDING);
keys.add(0, sortKey);
}
else if (sortIndex == 0) {
// It's the primary sorting key, toggle it
keys.set(0, toggle(keys.get(0)));
}
else {
// It's not the first, but was sorted on, remove old
// entry, insert as first with ascending.
keys.remove(sortIndex);
keys.add(0, new SortKey(column, SortOrder.ASCENDING));
}
if (keys.size() > getMaxSortKeys()) {
keys = keys.subList(0, getMaxSortKeys());
}
setSortKeys(keys);
}
|
private void | updateUseToString()Updates the useToString mapping before a sort.
int i = getModelWrapper().getColumnCount();
if (useToString == null || useToString.length != i) {
useToString = new boolean[i];
}
for (--i; i >= 0; i--) {
useToString[i] = useToString(i);
}
|
protected boolean | useToString(int column)Returns whether or not to convert the value to a string before
doing comparisons when sorting. If true
ModelWrapper.getStringValueAt will be used, otherwise
ModelWrapper.getValueAt will be used. It is up to
subclasses, such as TableRowSorter , to honor this value
in their ModelWrapper implementation.
return (getComparator(column) == null);
|