/* |
|
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
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; |
|
} |
|
} |
|
} |