425 lines · 16.2 KB · Java
RawDownload
/* * 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> &#151; 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 &#151; 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> &#151; 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> &#151; 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> &gt; <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> &gt; <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> &gt; <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> &gt; <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;        }    }}