You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
426 lines
15 KiB
426 lines
15 KiB
/*
|
|
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
package javax.swing;
|
|
|
|
import javax.swing.SortOrder;
|
|
import javax.swing.event.*;
|
|
import java.util.*;
|
|
|
|
/**
|
|
* <code>RowSorter</code> provides the basis for sorting and filtering.
|
|
* Beyond creating and installing a <code>RowSorter</code>, you very rarely
|
|
* need to interact with one directly. Refer to
|
|
* {@link javax.swing.table.TableRowSorter TableRowSorter} for a concrete
|
|
* implementation of <code>RowSorter</code> for <code>JTable</code>.
|
|
* <p>
|
|
* <code>RowSorter</code>'s primary role is to provide a mapping between
|
|
* two coordinate systems: that of the view (for example a
|
|
* <code>JTable</code>) and that of the underlying data source, typically a
|
|
* model.
|
|
* <p>
|
|
* The view invokes the following methods on the <code>RowSorter</code>:
|
|
* <ul>
|
|
* <li><code>toggleSortOrder</code> — The view invokes this when the
|
|
* appropriate user gesture has occurred to trigger a sort. For example,
|
|
* the user clicked a column header in a table.
|
|
* <li>One of the model change methods — The view invokes a model
|
|
* change method when the underlying model
|
|
* has changed. There may be order dependencies in how the events are
|
|
* delivered, so a <code>RowSorter</code> should not update its mapping
|
|
* until one of these methods is invoked.
|
|
* </ul>
|
|
* Because the view makes extensive use of the
|
|
* <code>convertRowIndexToModel</code>,
|
|
* <code>convertRowIndexToView</code> and <code>getViewRowCount</code> methods,
|
|
* these methods need to be fast.
|
|
* <p>
|
|
* <code>RowSorter</code> provides notification of changes by way of
|
|
* <code>RowSorterListener</code>. Two types of notification are sent:
|
|
* <ul>
|
|
* <li><code>RowSorterEvent.Type.SORT_ORDER_CHANGED</code> — notifies
|
|
* listeners that the sort order has changed. This is typically followed
|
|
* by a notification that the sort has changed.
|
|
* <li><code>RowSorterEvent.Type.SORTED</code> — notifies listeners that
|
|
* the mapping maintained by the <code>RowSorter</code> has changed in
|
|
* some way.
|
|
* </ul>
|
|
* <code>RowSorter</code> implementations typically don't have a one-to-one
|
|
* mapping with the underlying model, but they can.
|
|
* For example, if a database does the sorting,
|
|
* <code>toggleSortOrder</code> might call through to the database
|
|
* (on a background thread), and override the mapping methods to return the
|
|
* argument that is passed in.
|
|
* <p>
|
|
* Concrete implementations of <code>RowSorter</code>
|
|
* need to reference a model such as <code>TableModel</code> or
|
|
* <code>ListModel</code>. The view classes, such as
|
|
* <code>JTable</code> and <code>JList</code>, will also have a
|
|
* reference to the model. To avoid ordering dependencies,
|
|
* <code>RowSorter</code> implementations should not install a
|
|
* listener on the model. Instead the view class will call into the
|
|
* <code>RowSorter</code> when the model changes. For
|
|
* example, if a row is updated in a <code>TableModel</code>
|
|
* <code>JTable</code> invokes <code>rowsUpdated</code>.
|
|
* When the model changes, the view may call into any of the following methods:
|
|
* <code>modelStructureChanged</code>, <code>allRowsChanged</code>,
|
|
* <code>rowsInserted</code>, <code>rowsDeleted</code> and
|
|
* <code>rowsUpdated</code>.
|
|
*
|
|
* @param <M> the type of the underlying model
|
|
* @see javax.swing.table.TableRowSorter
|
|
* @since 1.6
|
|
*/
|
|
public abstract class RowSorter<M> {
|
|
private EventListenerList listenerList = new EventListenerList();
|
|
|
|
/**
|
|
* Creates a <code>RowSorter</code>.
|
|
*/
|
|
public RowSorter() {
|
|
}
|
|
|
|
/**
|
|
* Returns the underlying model.
|
|
*
|
|
* @return the underlying model
|
|
*/
|
|
public abstract M getModel();
|
|
|
|
/**
|
|
* Reverses the sort order of the specified column. It is up to
|
|
* subclasses to provide the exact behavior when invoked. Typically
|
|
* this will reverse 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.
|
|
* <p>
|
|
* If this results in changing the sort order and sorting, the
|
|
* appropriate <code>RowSorterListener</code> notification will be
|
|
* sent.
|
|
*
|
|
* @param column the column to toggle the sort ordering of, in
|
|
* terms of the underlying model
|
|
* @throws IndexOutOfBoundsException if column is outside the range of
|
|
* the underlying model
|
|
*/
|
|
public abstract void toggleSortOrder(int column);
|
|
|
|
/**
|
|
* Returns the location of <code>index</code> in terms of the
|
|
* underlying model. That is, for the row <code>index</code> in
|
|
* the coordinates of the view this returns the row index in terms
|
|
* of the underlying model.
|
|
*
|
|
* @param index the row index in terms of the underlying view
|
|
* @return row index in terms of the view
|
|
* @throws IndexOutOfBoundsException if <code>index</code> is outside the
|
|
* range of the view
|
|
*/
|
|
public abstract int convertRowIndexToModel(int index);
|
|
|
|
/**
|
|
* Returns the location of <code>index</code> in terms of the
|
|
* view. That is, for the row <code>index</code> in the
|
|
* coordinates of the underlying model this returns the row index
|
|
* in terms of the view.
|
|
*
|
|
* @param index the row index in terms of the underlying model
|
|
* @return row index in terms of the view, or -1 if index has been
|
|
* filtered out of the view
|
|
* @throws IndexOutOfBoundsException if <code>index</code> is outside
|
|
* the range of the model
|
|
*/
|
|
public abstract int convertRowIndexToView(int index);
|
|
|
|
/**
|
|
* Sets the current sort keys.
|
|
*
|
|
* @param keys the new <code>SortKeys</code>; <code>null</code>
|
|
* is a shorthand for specifying an empty list,
|
|
* indicating that the view should be unsorted
|
|
*/
|
|
public abstract void setSortKeys(List<? extends SortKey> keys);
|
|
|
|
/**
|
|
* Returns the current sort keys. This must return a {@code
|
|
* non-null List} and may return an unmodifiable {@code 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 the current sort order
|
|
*/
|
|
public abstract List<? extends SortKey> getSortKeys();
|
|
|
|
/**
|
|
* Returns the number of rows in the view. If the contents have
|
|
* been filtered this might differ from the row count of the
|
|
* underlying model.
|
|
*
|
|
* @return number of rows in the view
|
|
* @see #getModelRowCount
|
|
*/
|
|
public abstract int getViewRowCount();
|
|
|
|
/**
|
|
* Returns the number of rows in the underlying model.
|
|
*
|
|
* @return number of rows in the underlying model
|
|
* @see #getViewRowCount
|
|
*/
|
|
public abstract int getModelRowCount();
|
|
|
|
/**
|
|
* Invoked when the underlying model structure has completely
|
|
* changed. For example, if the number of columns in a
|
|
* <code>TableModel</code> changed, this method would be invoked.
|
|
* <p>
|
|
* You normally do not call this method. This method is public
|
|
* to allow view classes to call it.
|
|
*/
|
|
public abstract void modelStructureChanged();
|
|
|
|
/**
|
|
* Invoked when the contents of the underlying model have
|
|
* completely changed. The structure of the table is the same,
|
|
* only the contents have changed. This is typically sent when it
|
|
* is too expensive to characterize the change in terms of the
|
|
* other methods.
|
|
* <p>
|
|
* You normally do not call this method. This method is public
|
|
* to allow view classes to call it.
|
|
*/
|
|
public abstract void allRowsChanged();
|
|
|
|
/**
|
|
* Invoked when rows have been inserted into the underlying model
|
|
* in the specified range (inclusive).
|
|
* <p>
|
|
* The arguments give the indices of the effected range.
|
|
* The first argument is in terms of the model before the change, and
|
|
* must be less than or equal to the size of the model before the change.
|
|
* The second argument is in terms of the model after the change and must
|
|
* be less than the size of the model after the change. For example,
|
|
* if you have a 5-row model and add 3 items to the end of the model
|
|
* the indices are 5, 7.
|
|
* <p>
|
|
* You normally do not call this method. This method is public
|
|
* to allow view classes to call it.
|
|
*
|
|
* @param firstRow the first row
|
|
* @param endRow the last row
|
|
* @throws IndexOutOfBoundsException if either argument is invalid, or
|
|
* <code>firstRow</code> > <code>endRow</code>
|
|
*/
|
|
public abstract void rowsInserted(int firstRow, int endRow);
|
|
|
|
/**
|
|
* Invoked when rows have been deleted from the underlying model
|
|
* in the specified range (inclusive).
|
|
* <p>
|
|
* The arguments give the indices of the effected range and
|
|
* are in terms of the model <b>before</b> the change.
|
|
* For example, if you have a 5-row model and delete 3 items from the end
|
|
* of the model the indices are 2, 4.
|
|
* <p>
|
|
* You normally do not call this method. This method is public
|
|
* to allow view classes to call it.
|
|
*
|
|
* @param firstRow the first row
|
|
* @param endRow the last row
|
|
* @throws IndexOutOfBoundsException if either argument is outside
|
|
* the range of the model before the change, or
|
|
* <code>firstRow</code> > <code>endRow</code>
|
|
*/
|
|
public abstract void rowsDeleted(int firstRow, int endRow);
|
|
|
|
/**
|
|
* Invoked when rows have been changed in the underlying model
|
|
* between the specified range (inclusive).
|
|
* <p>
|
|
* You normally do not call this method. This method is public
|
|
* to allow view classes to call it.
|
|
*
|
|
* @param firstRow the first row, in terms of the underlying model
|
|
* @param endRow the last row, in terms of the underlying model
|
|
* @throws IndexOutOfBoundsException if either argument is outside
|
|
* the range of the underlying model, or
|
|
* <code>firstRow</code> > <code>endRow</code>
|
|
*/
|
|
public abstract void rowsUpdated(int firstRow, int endRow);
|
|
|
|
/**
|
|
* Invoked when the column in the rows have been updated in
|
|
* the underlying model between the specified range.
|
|
* <p>
|
|
* You normally do not call this method. This method is public
|
|
* to allow view classes to call it.
|
|
*
|
|
* @param firstRow the first row, in terms of the underlying model
|
|
* @param endRow the last row, in terms of the underlying model
|
|
* @param column the column that has changed, in terms of the underlying
|
|
* model
|
|
* @throws IndexOutOfBoundsException if either argument is outside
|
|
* the range of the underlying model after the change,
|
|
* <code>firstRow</code> > <code>endRow</code>, or
|
|
* <code>column</code> is outside the range of the underlying
|
|
* model
|
|
*/
|
|
public abstract void rowsUpdated(int firstRow, int endRow, int column);
|
|
|
|
/**
|
|
* Adds a <code>RowSorterListener</code> to receive notification
|
|
* about this <code>RowSorter</code>. If the same
|
|
* listener is added more than once it will receive multiple
|
|
* notifications. If <code>l</code> is <code>null</code> nothing
|
|
* is done.
|
|
*
|
|
* @param l the <code>RowSorterListener</code>
|
|
*/
|
|
public void addRowSorterListener(RowSorterListener l) {
|
|
listenerList.add(RowSorterListener.class, l);
|
|
}
|
|
|
|
/**
|
|
* Removes a <code>RowSorterListener</code>. If
|
|
* <code>l</code> is <code>null</code> nothing is done.
|
|
*
|
|
* @param l the <code>RowSorterListener</code>
|
|
*/
|
|
public void removeRowSorterListener(RowSorterListener l) {
|
|
listenerList.remove(RowSorterListener.class, l);
|
|
}
|
|
|
|
/**
|
|
* Notifies listener that the sort order has changed.
|
|
*/
|
|
protected void fireSortOrderChanged() {
|
|
fireRowSorterChanged(new RowSorterEvent(this));
|
|
}
|
|
|
|
/**
|
|
* Notifies listener that the mapping has changed.
|
|
*
|
|
* @param lastRowIndexToModel the mapping from model indices to
|
|
* view indices prior to the sort, may be <code>null</code>
|
|
*/
|
|
protected void fireRowSorterChanged(int[] lastRowIndexToModel) {
|
|
fireRowSorterChanged(new RowSorterEvent(this,
|
|
RowSorterEvent.Type.SORTED, lastRowIndexToModel));
|
|
}
|
|
|
|
void fireRowSorterChanged(RowSorterEvent event) {
|
|
Object[] listeners = listenerList.getListenerList();
|
|
for (int i = listeners.length - 2; i >= 0; i -= 2) {
|
|
if (listeners[i] == RowSorterListener.class) {
|
|
((RowSorterListener)listeners[i + 1]).
|
|
sorterChanged(event);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SortKey describes the sort order for a particular column. The
|
|
* column index is in terms of the underlying model, which may differ
|
|
* from that of the view.
|
|
*
|
|
* @since 1.6
|
|
*/
|
|
public static class SortKey {
|
|
private int column;
|
|
private SortOrder sortOrder;
|
|
|
|
/**
|
|
* Creates a <code>SortKey</code> for the specified column with
|
|
* the specified sort order.
|
|
*
|
|
* @param column index of the column, in terms of the model
|
|
* @param sortOrder the sorter order
|
|
* @throws IllegalArgumentException if <code>sortOrder</code> is
|
|
* <code>null</code>
|
|
*/
|
|
public SortKey(int column, SortOrder sortOrder) {
|
|
if (sortOrder == null) {
|
|
throw new IllegalArgumentException(
|
|
"sort order must be non-null");
|
|
}
|
|
this.column = column;
|
|
this.sortOrder = sortOrder;
|
|
}
|
|
|
|
/**
|
|
* Returns the index of the column.
|
|
*
|
|
* @return index of column
|
|
*/
|
|
public final int getColumn() {
|
|
return column;
|
|
}
|
|
|
|
/**
|
|
* Returns the sort order of the column.
|
|
*
|
|
* @return the sort order of the column
|
|
*/
|
|
public final SortOrder getSortOrder() {
|
|
return sortOrder;
|
|
}
|
|
|
|
/**
|
|
* Returns the hash code for this <code>SortKey</code>.
|
|
*
|
|
* @return hash code
|
|
*/
|
|
public int hashCode() {
|
|
int result = 17;
|
|
result = 37 * result + column;
|
|
result = 37 * result + sortOrder.hashCode();
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Returns true if this object equals the specified object.
|
|
* If the specified object is a <code>SortKey</code> and
|
|
* references the same column and sort order, the two objects
|
|
* are equal.
|
|
*
|
|
* @param o the object to compare to
|
|
* @return true if <code>o</code> is equal to this <code>SortKey</code>
|
|
*/
|
|
public boolean equals(Object o) {
|
|
if (o == this) {
|
|
return true;
|
|
}
|
|
if (o instanceof SortKey) {
|
|
return (((SortKey)o).column == column &&
|
|
((SortKey)o).sortOrder == sortOrder);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|