< prev index next >
src/java.desktop/share/classes/javax/swing/DefaultRowSorter.java
Print this page
*** 31,111 ****
import java.util.Comparator;
import java.util.List;
import javax.swing.SortOrder;
/**
! * An implementation of <code>RowSorter</code> that provides sorting and
* filtering around a grid-based data model.
! * 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>
! * Sorting is done based on the current <code>SortKey</code>s, in order.
! * If two objects are equal (the <code>Comparator</code> for the
! * column returns 0) the next <code>SortKey</code> is used. If no
! * <code>SortKey</code>s remain or the order is <code>UNSORTED</code>, then
* the order of the rows in the model is used.
* <p>
! * Sorting of each column is done by way of a <code>Comparator</code>
! * that you can specify using the <code>setComparator</code> method.
! * If a <code>Comparator</code> has not been specified, the
! * <code>Comparator</code> returned by
! * <code>Collator.getInstance()</code> is used on the results of
! * calling <code>toString</code> on the underlying objects. The
! * <code>Comparator</code> is never passed <code>null</code>. A
! * <code>null</code> value is treated as occurring before a
! * non-<code>null</code> value, and two <code>null</code> values are
* considered equal.
* <p>
! * If you specify a <code>Comparator</code> that casts its argument to
* a type other than that provided by the model, a
! * <code>ClassCastException</code> will be thrown when the data is sorted.
* <p>
! * In addition to sorting, <code>DefaultRowSorter</code> provides the
* ability to filter rows. Filtering is done by way of a
! * <code>RowFilter</code> that is specified using the
! * <code>setRowFilter</code> method. If no filter has been specified all
* rows are included.
* <p>
* By default, rows are in unsorted order (the same as the model) and
! * every column is sortable. The default <code>Comparator</code>s are
* documented in the subclasses (for example, {@link
* javax.swing.table.TableRowSorter TableRowSorter}).
* <p>
* If the underlying model structure changes (the
! * <code>modelStructureChanged</code> method is invoked) the following
! * are reset to their default values: <code>Comparator</code>s by
* column, current sort order, and whether each column is sortable. To
! * find the default <code>Comparator</code>s, see the concrete
* implementation (for example, {@link
* javax.swing.table.TableRowSorter TableRowSorter}). The default
* sort order is unsorted (the same as the model), and columns are
* sortable by default.
* <p>
* If the underlying model structure changes (the
! * <code>modelStructureChanged</code> method is invoked) the following
! * are reset to their default values: <code>Comparator</code>s by column,
* current sort order and whether a column is sortable.
* <p>
! * <code>DefaultRowSorter</code> is an abstract class. Concrete
* subclasses must provide access to the underlying data by invoking
* {@code setModelWrapper}. The {@code setModelWrapper} method
* <b>must</b> be invoked soon after the constructor is
* called, ideally from within the subclass's constructor.
* Undefined behavior will result if you use a {@code
* DefaultRowSorter} without specifying a {@code ModelWrapper}.
* <p>
! * <code>DefaultRowSorter</code> has two formal type parameters. The
* first type parameter corresponds to the class of the model, for example
! * <code>DefaultTableModel</code>. The second type parameter
* corresponds to the class of the identifier passed to the
! * <code>RowFilter</code>. Refer to <code>TableRowSorter</code> and
! * <code>RowFilter</code> for more details on the type parameters.
*
* @param <M> the type of the model
! * @param <I> the type of the identifier passed to the <code>RowFilter</code>
* @see javax.swing.table.TableRowSorter
* @see javax.swing.table.DefaultTableModel
* @see java.text.Collator
* @since 1.6
*/
--- 31,111 ----
import java.util.Comparator;
import java.util.List;
import javax.swing.SortOrder;
/**
! * An implementation of {@code RowSorter} that provides sorting and
* filtering around a grid-based data model.
! * Beyond creating and installing a {@code RowSorter}, you very rarely
* need to interact with one directly. Refer to
* {@link javax.swing.table.TableRowSorter TableRowSorter} for a concrete
! * implementation of {@code RowSorter} for {@code JTable}.
* <p>
! * Sorting is done based on the current {@code SortKey}s, in order.
! * If two objects are equal (the {@code Comparator} for the
! * column returns 0) the next {@code SortKey} is used. If no
! * {@code SortKey}s remain or the order is {@code UNSORTED}, then
* the order of the rows in the model is used.
* <p>
! * Sorting of each column is done by way of a {@code Comparator}
! * that you can specify using the {@code setComparator} method.
! * If a {@code Comparator} has not been specified, the
! * {@code Comparator} returned by
! * {@code Collator.getInstance()} is used on the results of
! * calling {@code toString} on the underlying objects. The
! * {@code Comparator} is never passed {@code null}. A
! * {@code null} value is treated as occurring before a
! * non-{@code null} value, and two {@code null} values are
* considered equal.
* <p>
! * If you specify a {@code Comparator} that casts its argument to
* a type other than that provided by the model, a
! * {@code ClassCastException} will be thrown when the data is sorted.
* <p>
! * In addition to sorting, {@code DefaultRowSorter} provides the
* ability to filter rows. Filtering is done by way of a
! * {@code RowFilter} that is specified using the
! * {@code setRowFilter} method. If no filter has been specified all
* rows are included.
* <p>
* By default, rows are in unsorted order (the same as the model) and
! * every column is sortable. The default {@code Comparator}s are
* documented in the subclasses (for example, {@link
* javax.swing.table.TableRowSorter TableRowSorter}).
* <p>
* If the underlying model structure changes (the
! * {@code modelStructureChanged} method is invoked) the following
! * are reset to their default values: {@code Comparator}s by
* column, current sort order, and whether each column is sortable. To
! * find the default {@code Comparator}s, see the concrete
* implementation (for example, {@link
* javax.swing.table.TableRowSorter TableRowSorter}). The default
* sort order is unsorted (the same as the model), and columns are
* sortable by default.
* <p>
* If the underlying model structure changes (the
! * {@code modelStructureChanged} method is invoked) the following
! * are reset to their default values: {@code Comparator}s by column,
* current sort order and whether a column is sortable.
* <p>
! * {@code DefaultRowSorter} is an abstract class. Concrete
* subclasses must provide access to the underlying data by invoking
* {@code setModelWrapper}. The {@code setModelWrapper} method
* <b>must</b> be invoked soon after the constructor is
* called, ideally from within the subclass's constructor.
* Undefined behavior will result if you use a {@code
* DefaultRowSorter} without specifying a {@code ModelWrapper}.
* <p>
! * {@code DefaultRowSorter} has two formal type parameters. The
* first type parameter corresponds to the class of the model, for example
! * {@code DefaultTableModel}. The second type parameter
* corresponds to the class of the identifier passed to the
! * {@code RowFilter}. Refer to {@code TableRowSorter} and
! * {@code RowFilter} for more details on the type parameters.
*
* @param <M> the type of the model
! * @param <I> the type of the identifier passed to the {@code RowFilter}
* @see javax.swing.table.TableRowSorter
* @see javax.swing.table.DefaultTableModel
* @see java.text.Collator
* @since 1.6
*/
*** 188,198 ****
*/
private int modelRowCount;
/**
! * Creates an empty <code>DefaultRowSorter</code>.
*/
public DefaultRowSorter() {
sortKeys = Collections.emptyList();
maxSortKeys = 3;
}
--- 188,198 ----
*/
private int modelRowCount;
/**
! * Creates an empty {@code DefaultRowSorter}.
*/
public DefaultRowSorter() {
sortKeys = Collections.emptyList();
maxSortKeys = 3;
}
*** 242,260 ****
return getModelWrapper().getModel();
}
/**
* Sets whether or not the specified column is sortable. The specified
! * value is only checked when <code>toggleSortOrder</code> 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.
*
* @param column the column to enable or disable sorting on, in terms
* of the underlying model
* @param sortable whether or not the specified column is sortable
! * @throws IndexOutOfBoundsException if <code>column</code> is outside
* the range of the model
* @see #toggleSortOrder
* @see #setSortKeys
*/
public void setSortable(int column, boolean sortable) {
--- 242,260 ----
return getModelWrapper().getModel();
}
/**
* Sets whether or not the specified column is sortable. The specified
! * value is only checked when {@code 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.
*
* @param column the column to enable or disable sorting on, in terms
* of the underlying model
* @param sortable whether or not the specified column is sortable
! * @throws IndexOutOfBoundsException if {@code column} is outside
* the range of the model
* @see #toggleSortOrder
* @see #setSortKeys
*/
public void setSortable(int column, boolean sortable) {
*** 286,300 ****
* 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.
*
! * @param sortKeys the new <code>SortKeys</code>; <code>null</code>
* is a shorthand for specifying an empty list,
* indicating that the view should be unsorted
* @throws IllegalArgumentException if any of the values in
! * <code>sortKeys</code> are null or have a column index outside
* the range of the model
*/
public void setSortKeys(List<? extends SortKey> sortKeys) {
List<SortKey> old = this.sortKeys;
if (sortKeys != null && sortKeys.size() > 0) {
--- 286,300 ----
* 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.
*
! * @param sortKeys the new {@code SortKeys}; {@code null}
* is a shorthand for specifying an empty list,
* indicating that the view should be unsorted
* @throws IllegalArgumentException if any of the values in
! * {@code sortKeys} are null or have a column index outside
* the range of the model
*/
public void setSortKeys(List<? extends SortKey> sortKeys) {
List<SortKey> old = this.sortKeys;
if (sortKeys != null && sortKeys.size() > 0) {
*** 337,369 ****
/**
* 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
! * <code>setMaxSortKeys(2)</code> 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 <code>setMaxSortKeys</code>, column 1 no longer has an
* effect on the order.
* <p>
* The maximum number of sort keys is enforced by
! * <code>toggleSortOrder</code>. You can specify more sort
! * keys by invoking <code>setSortKeys</code> directly and they will
! * all be honored. However if <code>toggleSortOrder</code> is subsequently
* invoked the maximum number of sort keys will be enforced.
* The default value is 3.
*
* @param max the maximum number of sort keys
! * @throws IllegalArgumentException if <code>max</code> < 1
*/
public void setMaxSortKeys(int max) {
if (max < 1) {
throw new IllegalArgumentException("Invalid max");
}
--- 337,369 ----
/**
* 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
! * {@code 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 {@code setMaxSortKeys}, column 1 no longer has an
* effect on the order.
* <p>
* The maximum number of sort keys is enforced by
! * {@code toggleSortOrder}. You can specify more sort
! * keys by invoking {@code setSortKeys} directly and they will
! * all be honored. However if {@code toggleSortOrder} is subsequently
* invoked the maximum number of sort keys will be enforced.
* The default value is 3.
*
* @param max the maximum number of sort keys
! * @throws IllegalArgumentException if {@code max < 1}
*/
public void setMaxSortKeys(int max) {
if (max < 1) {
throw new IllegalArgumentException("Invalid max");
}
*** 379,389 ****
return maxSortKeys;
}
/**
* If true, specifies that a sort should happen when the underlying
! * model is updated (<code>rowsUpdated</code> 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.
*
* @param sortsOnUpdates whether or not to sort on update events
--- 379,389 ----
return maxSortKeys;
}
/**
* If true, specifies that a sort should happen when the underlying
! * model is updated ({@code 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.
*
* @param sortsOnUpdates whether or not to sort on update events
*** 403,420 ****
}
/**
* Sets the filter that determines which rows, if any, should be
* hidden from the view. The filter is applied before sorting. A value
! * of <code>null</code> indicates all values from the model should be
* included.
* <p>
! * <code>RowFilter</code>'s <code>include</code> method is passed an
! * <code>Entry</code> that wraps the underlying model. The number
! * of columns in the <code>Entry</code> corresponds to the
! * number of columns in the <code>ModelWrapper</code>. The identifier
! * comes from the <code>ModelWrapper</code> as well.
* <p>
* This method triggers a sort.
*
* @param filter the filter used to determine what entries should be
* included
--- 403,420 ----
}
/**
* Sets the filter that determines which rows, if any, should be
* hidden from the view. The filter is applied before sorting. A value
! * of {@code null} indicates all values from the model should be
* included.
* <p>
! * {@code RowFilter}'s {@code include} method is passed an
! * {@code Entry} that wraps the underlying model. The number
! * of columns in the {@code Entry} corresponds to the
! * number of columns in the {@code ModelWrapper}. The identifier
! * comes from the {@code ModelWrapper} as well.
* <p>
* This method triggers a sort.
*
* @param filter the filter used to determine what entries should be
* included
*** 559,569 ****
}
/**
* 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 <code>sortKeys</code> list
* indicates that the view should unsorted, the same as the model.
*
* @see #setRowFilter
* @see #setSortKeys
*/
--- 559,569 ----
}
/**
* 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 {@code sortKeys} list
* indicates that the view should unsorted, the same as the model.
*
* @see #setRowFilter
* @see #setSortKeys
*/
*** 703,730 ****
}
/**
* Returns whether or not to convert the value to a string before
* doing comparisons when sorting. If true
! * <code>ModelWrapper.getStringValueAt</code> will be used, otherwise
! * <code>ModelWrapper.getValueAt</code> will be used. It is up to
! * subclasses, such as <code>TableRowSorter</code>, to honor this value
! * in their <code>ModelWrapper</code> implementation.
*
* @param column the index of the column to test, in terms of the
* underlying model
* @return true if values are to be converted to strings before doing
* comparisons when sorting
! * @throws IndexOutOfBoundsException if <code>column</code> is not valid
*/
protected boolean useToString(int column) {
return (getComparator(column) == null);
}
/**
* Refreshes the modelToView mapping from that of viewToModel.
! * If <code>unsetFirst</code> is true, all indices in modelToView are
* first set to -1.
*/
private void setModelToViewFromViewToModel(boolean unsetFirst) {
int i;
if (unsetFirst) {
--- 703,730 ----
}
/**
* Returns whether or not to convert the value to a string before
* doing comparisons when sorting. If true
! * {@code ModelWrapper.getStringValueAt} will be used, otherwise
! * {@code ModelWrapper.getValueAt} will be used. It is up to
! * subclasses, such as {@code TableRowSorter}, to honor this value
! * in their {@code ModelWrapper} implementation.
*
* @param column the index of the column to test, in terms of the
* underlying model
* @return true if values are to be converted to strings before doing
* comparisons when sorting
! * @throws IndexOutOfBoundsException if {@code column} is not valid
*/
protected boolean useToString(int column) {
return (getComparator(column) == null);
}
/**
* Refreshes the modelToView mapping from that of viewToModel.
! * If {@code unsetFirst} is true, all indices in modelToView are
* first set to -1.
*/
private void setModelToViewFromViewToModel(boolean unsetFirst) {
int i;
if (unsetFirst) {
*** 747,764 ****
}
return new int[0];
}
/**
! * Sets the <code>Comparator</code> 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 <code>sort</code>.
*
! * @param column the index of the column the <code>Comparator</code> is
* to be used for, in terms of the underlying model
! * @param comparator the <code>Comparator</code> to use
! * @throws IndexOutOfBoundsException if <code>column</code> is outside
* the range of the underlying model
*/
public void setComparator(int column, Comparator<?> comparator) {
checkColumn(column);
if (comparators == null) {
--- 747,764 ----
}
return new int[0];
}
/**
! * Sets the {@code 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 {@code sort}.
*
! * @param column the index of the column the {@code Comparator} is
* to be used for, in terms of the underlying model
! * @param comparator the {@code Comparator} to use
! * @throws IndexOutOfBoundsException if {@code column} is outside
* the range of the underlying model
*/
public void setComparator(int column, Comparator<?> comparator) {
checkColumn(column);
if (comparators == null) {
*** 766,782 ****
}
comparators[column] = comparator;
}
/**
! * Returns the <code>Comparator</code> for the specified
! * column. This will return <code>null</code> if a <code>Comparator</code>
* has not been specified for the column.
*
! * @param column the column to fetch the <code>Comparator</code> for, in
* terms of the underlying model
! * @return the <code>Comparator</code> for the specified column
* @throws IndexOutOfBoundsException if column is outside
* the range of the underlying model
*/
public Comparator<?> getComparator(int column) {
checkColumn(column);
--- 766,782 ----
}
comparators[column] = comparator;
}
/**
! * Returns the {@code Comparator} for the specified
! * column. This will return {@code null} if a {@code Comparator}
* has not been specified for the column.
*
! * @param column the column to fetch the {@code Comparator} for, in
* terms of the underlying model
! * @return the {@code Comparator} for the specified column
* @throws IndexOutOfBoundsException if column is outside
* the range of the underlying model
*/
public Comparator<?> getComparator(int column) {
checkColumn(column);
*** 1014,1024 ****
current.length - last);
}
/**
* Returns true if we should try and optimize the processing of the
! * <code>TableModelEvent</code>. If this returns false, assume the
* event was dealt with and no further processing needs to happen.
*/
private boolean shouldOptimizeChange(int firstRow, int lastRow) {
if (!isTransformed()) {
// Not transformed, nothing to do.
--- 1014,1024 ----
current.length - last);
}
/**
* Returns true if we should try and optimize the processing of the
! * {@code TableModelEvent}. If this returns false, assume the
* event was dealt with and no further processing needs to happen.
*/
private boolean shouldOptimizeChange(int firstRow, int lastRow) {
if (!isTransformed()) {
// Not transformed, nothing to do.
*** 1225,1264 ****
}
}
/**
! * <code>DefaultRowSorter.ModelWrapper</code> is responsible for providing
! * the data that gets sorted by <code>DefaultRowSorter</code>. You
! * normally do not interact directly with <code>ModelWrapper</code>.
! * Subclasses of <code>DefaultRowSorter</code> provide an
! * implementation of <code>ModelWrapper</code> wrapping another model.
* For example,
! * <code>TableRowSorter</code> provides a <code>ModelWrapper</code> that
! * wraps a <code>TableModel</code>.
* <p>
! * <code>ModelWrapper</code> makes a distinction between values as
! * <code>Object</code>s and <code>String</code>s. This allows
* implementations to provide a custom string
! * converter to be used instead of invoking <code>toString</code> on the
* object.
*
* @param <M> the type of the underlying model
* @param <I> the identifier supplied to the filter
* @since 1.6
* @see RowFilter
* @see RowFilter.Entry
*/
protected abstract static class ModelWrapper<M,I> {
/**
! * Creates a new <code>ModelWrapper</code>.
*/
protected ModelWrapper() {
}
/**
! * Returns the underlying model that this <code>Model</code> is
* wrapping.
*
* @return the underlying model
*/
public abstract M getModel();
--- 1225,1264 ----
}
}
/**
! * {@code DefaultRowSorter.ModelWrapper} is responsible for providing
! * the data that gets sorted by {@code DefaultRowSorter}. You
! * normally do not interact directly with {@code ModelWrapper}.
! * Subclasses of {@code DefaultRowSorter} provide an
! * implementation of {@code ModelWrapper} wrapping another model.
* For example,
! * {@code TableRowSorter} provides a {@code ModelWrapper} that
! * wraps a {@code TableModel}.
* <p>
! * {@code ModelWrapper} makes a distinction between values as
! * {@code Object}s and {@code String}s. This allows
* implementations to provide a custom string
! * converter to be used instead of invoking {@code toString} on the
* object.
*
* @param <M> the type of the underlying model
* @param <I> the identifier supplied to the filter
* @since 1.6
* @see RowFilter
* @see RowFilter.Entry
*/
protected abstract static class ModelWrapper<M,I> {
/**
! * Creates a new {@code ModelWrapper}.
*/
protected ModelWrapper() {
}
/**
! * Returns the underlying model that this {@code Model} is
* wrapping.
*
* @return the underlying model
*/
public abstract M getModel();
*** 1287,1305 ****
* the range of the model
*/
public abstract Object getValueAt(int row, int column);
/**
! * Returns the value as a <code>String</code> at the specified
! * index. This implementation uses <code>toString</code> on
! * the result from <code>getValueAt</code> (making sure
* to return an empty string for null values). Subclasses that
* override this method should never return null.
*
* @param row the row index
* @param column the column index
! * @return the value at the specified index as a <code>String</code>
* @throws IndexOutOfBoundsException if the indices are outside
* the range of the model
*/
public String getStringValueAt(int row, int column) {
Object o = getValueAt(row, column);
--- 1287,1305 ----
* the range of the model
*/
public abstract Object getValueAt(int row, int column);
/**
! * Returns the value as a {@code String} at the specified
! * index. This implementation uses {@code toString} on
! * the result from {@code getValueAt} (making sure
* to return an empty string for null values). Subclasses that
* override this method should never return null.
*
* @param row the row index
* @param column the column index
! * @return the value at the specified index as a {@code String}
* @throws IndexOutOfBoundsException if the indices are outside
* the range of the model
*/
public String getStringValueAt(int row, int column) {
Object o = getValueAt(row, column);
*** 1314,1325 ****
}
/**
* Returns the identifier for the specified row. The return value
* of this is used as the identifier for the
! * <code>RowFilter.Entry</code> that is passed to the
! * <code>RowFilter</code>.
*
* @param row the row to return the identifier for, in terms of
* the underlying model
* @return the identifier
* @see RowFilter.Entry#getIdentifier
--- 1314,1325 ----
}
/**
* Returns the identifier for the specified row. The return value
* of this is used as the identifier for the
! * {@code RowFilter.Entry} that is passed to the
! * {@code RowFilter}.
*
* @param row the row to return the identifier for, in terms of
* the underlying model
* @return the identifier
* @see RowFilter.Entry#getIdentifier
< prev index next >