1 /*
2  * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 package javax.swing;
26 
27 import javax.swing.SortOrder;
28 import javax.swing.event.*;
29 import java.util.*;
30 
31 /**
32  * <code>RowSorter</code> provides the basis for sorting and filtering.
33  * Beyond creating and installing a <code>RowSorter</code>, you very rarely
34  * need to interact with one directly.  Refer to
35  * {@link javax.swing.table.TableRowSorter TableRowSorter} for a concrete
36  * implementation of <code>RowSorter</code> for <code>JTable</code>.
37  * <p>
38  * <code>RowSorter</code>'s primary role is to provide a mapping between
39  * two coordinate systems: that of the view (for example a
40  * <code>JTable</code>) and that of the underlying data source, typically a
41  * model.
42  * <p>
43  * The view invokes the following methods on the <code>RowSorter</code>:
44  * <ul>
45  * <li><code>toggleSortOrder</code> &#151; The view invokes this when the
46  *     appropriate user gesture has occurred to trigger a sort.  For example,
47  *     the user clicked a column header in a table.
48  * <li>One of the model change methods &#151; The view invokes a model
49  *     change method when the underlying model
50  *     has changed.  There may be order dependencies in how the events are
51  *     delivered, so a <code>RowSorter</code> should not update its mapping
52  *     until one of these methods is invoked.
53  * </ul>
54  * Because the view makes extensive use of  the
55  * <code>convertRowIndexToModel</code>,
56  * <code>convertRowIndexToView</code> and <code>getViewRowCount</code> methods,
57  * these methods need to be fast.
58  * <p>
59  * <code>RowSorter</code> provides notification of changes by way of
60  * <code>RowSorterListener</code>.  Two types of notification are sent:
61  * <ul>
62  * <li><code>RowSorterEvent.Type.SORT_ORDER_CHANGED</code> &#151; notifies
63  *     listeners that the sort order has changed.  This is typically followed
64  *     by a notification that the sort has changed.
65  * <li><code>RowSorterEvent.Type.SORTED</code> &#151; notifies listeners that
66  *     the mapping maintained by the <code>RowSorter</code> has changed in
67  *     some way.
68  * </ul>
69  * <code>RowSorter</code> implementations typically don't have a one-to-one
70  * mapping with the underlying model, but they can.
71  * For example, if a database does the sorting,
72  * <code>toggleSortOrder</code> might call through to the database
73  * (on a background thread), and override the mapping methods to return the
74  * argument that is passed in.
75  * <p>
76  * Concrete implementations of <code>RowSorter</code>
77  * need to reference a model such as <code>TableModel</code> or
78  * <code>ListModel</code>.  The view classes, such as
79  * <code>JTable</code> and <code>JList</code>, will also have a
80  * reference to the model.  To avoid ordering dependencies,
81  * <code>RowSorter</code> implementations should not install a
82  * listener on the model.  Instead the view class will call into the
83  * <code>RowSorter</code> when the model changes.  For
84  * example, if a row is updated in a <code>TableModel</code>
85  * <code>JTable</code> invokes <code>rowsUpdated</code>.
86  * When the model changes, the view may call into any of the following methods:
87  * <code>modelStructureChanged</code>, <code>allRowsChanged</code>,
88  * <code>rowsInserted</code>, <code>rowsDeleted</code> and
89  * <code>rowsUpdated</code>.
90  *
91  * @param <M> the type of the underlying model
92  * @see javax.swing.table.TableRowSorter
93  * @since 1.6
94  */
95 public abstract class RowSorter<M> {
96     private EventListenerList listenerList = new EventListenerList();
97 
98     /**
99      * Creates a <code>RowSorter</code>.
100      */
RowSorter()101     public RowSorter() {
102     }
103 
104     /**
105      * Returns the underlying model.
106      *
107      * @return the underlying model
108      */
getModel()109     public abstract M getModel();
110 
111     /**
112      * Reverses the sort order of the specified column.  It is up to
113      * subclasses to provide the exact behavior when invoked.  Typically
114      * this will reverse the sort order from ascending to descending (or
115      * descending to ascending) if the specified column is already the
116      * primary sorted column; otherwise, makes the specified column
117      * the primary sorted column, with an ascending sort order.  If
118      * the specified column is not sortable, this method has no
119      * effect.
120      * <p>
121      * If this results in changing the sort order and sorting, the
122      * appropriate <code>RowSorterListener</code> notification will be
123      * sent.
124      *
125      * @param column the column to toggle the sort ordering of, in
126      *        terms of the underlying model
127      * @throws IndexOutOfBoundsException if column is outside the range of
128      *         the underlying model
129      */
toggleSortOrder(int column)130     public abstract void toggleSortOrder(int column);
131 
132     /**
133      * Returns the location of <code>index</code> in terms of the
134      * underlying model.  That is, for the row <code>index</code> in
135      * the coordinates of the view this returns the row index in terms
136      * of the underlying model.
137      *
138      * @param index the row index in terms of the underlying view
139      * @return row index in terms of the view
140      * @throws IndexOutOfBoundsException if <code>index</code> is outside the
141      *         range of the view
142      */
convertRowIndexToModel(int index)143     public abstract int convertRowIndexToModel(int index);
144 
145     /**
146      * Returns the location of <code>index</code> in terms of the
147      * view.  That is, for the row <code>index</code> in the
148      * coordinates of the underlying model this returns the row index
149      * in terms of the view.
150      *
151      * @param index the row index in terms of the underlying model
152      * @return row index in terms of the view, or -1 if index has been
153      *         filtered out of the view
154      * @throws IndexOutOfBoundsException if <code>index</code> is outside
155      *         the range of the model
156      */
convertRowIndexToView(int index)157     public abstract int convertRowIndexToView(int index);
158 
159     /**
160      * Sets the current sort keys.
161      *
162      * @param keys the new <code>SortKeys</code>; <code>null</code>
163      *        is a shorthand for specifying an empty list,
164      *        indicating that the view should be unsorted
165      */
setSortKeys(List<? extends SortKey> keys)166     public abstract void setSortKeys(List<? extends SortKey> keys);
167 
168     /**
169      * Returns the current sort keys.  This must return a {@code
170      * non-null List} and may return an unmodifiable {@code List}. If
171      * you need to change the sort keys, make a copy of the returned
172      * {@code List}, mutate the copy and invoke {@code setSortKeys}
173      * with the new list.
174      *
175      * @return the current sort order
176      */
getSortKeys()177     public abstract List<? extends SortKey> getSortKeys();
178 
179     /**
180      * Returns the number of rows in the view.  If the contents have
181      * been filtered this might differ from the row count of the
182      * underlying model.
183      *
184      * @return number of rows in the view
185      * @see #getModelRowCount
186      */
getViewRowCount()187     public abstract int getViewRowCount();
188 
189     /**
190      * Returns the number of rows in the underlying model.
191      *
192      * @return number of rows in the underlying model
193      * @see #getViewRowCount
194      */
getModelRowCount()195     public abstract int getModelRowCount();
196 
197     /**
198      * Invoked when the underlying model structure has completely
199      * changed.  For example, if the number of columns in a
200      * <code>TableModel</code> changed, this method would be invoked.
201      * <p>
202      * You normally do not call this method.  This method is public
203      * to allow view classes to call it.
204      */
modelStructureChanged()205     public abstract void modelStructureChanged();
206 
207     /**
208      * Invoked when the contents of the underlying model have
209      * completely changed. The structure of the table is the same,
210      * only the contents have changed. This is typically sent when it
211      * is too expensive to characterize the change in terms of the
212      * other methods.
213      * <p>
214      * You normally do not call this method.  This method is public
215      * to allow view classes to call it.
216      */
allRowsChanged()217     public abstract void allRowsChanged();
218 
219     /**
220      * Invoked when rows have been inserted into the underlying model
221      * in the specified range (inclusive).
222      * <p>
223      * The arguments give the indices of the effected range.
224      * The first argument is in terms of the model before the change, and
225      * must be less than or equal to the size of the model before the change.
226      * The second argument is in terms of the model after the change and must
227      * be less than the size of the model after the change. For example,
228      * if you have a 5-row model and add 3 items to the end of the model
229      * the indices are 5, 7.
230      * <p>
231      * You normally do not call this method.  This method is public
232      * to allow view classes to call it.
233      *
234      * @param firstRow the first row
235      * @param endRow the last row
236      * @throws IndexOutOfBoundsException if either argument is invalid, or
237      *         <code>firstRow</code> &gt; <code>endRow</code>
238      */
rowsInserted(int firstRow, int endRow)239     public abstract void rowsInserted(int firstRow, int endRow);
240 
241     /**
242      * Invoked when rows have been deleted from the underlying model
243      * in the specified range (inclusive).
244      * <p>
245      * The arguments give the indices of the effected range and
246      * are in terms of the model <b>before</b> the change.
247      * For example, if you have a 5-row model and delete 3 items from the end
248      * of the model the indices are 2, 4.
249      * <p>
250      * You normally do not call this method.  This method is public
251      * to allow view classes to call it.
252      *
253      * @param firstRow the first row
254      * @param endRow the last row
255      * @throws IndexOutOfBoundsException if either argument is outside
256      *         the range of the model before the change, or
257      *         <code>firstRow</code> &gt; <code>endRow</code>
258      */
rowsDeleted(int firstRow, int endRow)259     public abstract void rowsDeleted(int firstRow, int endRow);
260 
261     /**
262      * Invoked when rows have been changed in the underlying model
263      * between the specified range (inclusive).
264      * <p>
265      * You normally do not call this method.  This method is public
266      * to allow view classes to call it.
267      *
268      * @param firstRow the first row, in terms of the underlying model
269      * @param endRow the last row, in terms of the underlying model
270      * @throws IndexOutOfBoundsException if either argument is outside
271      *         the range of the underlying model, or
272      *         <code>firstRow</code> &gt; <code>endRow</code>
273      */
rowsUpdated(int firstRow, int endRow)274     public abstract void rowsUpdated(int firstRow, int endRow);
275 
276     /**
277      * Invoked when the column in the rows have been updated in
278      * the underlying model between the specified range.
279      * <p>
280      * You normally do not call this method.  This method is public
281      * to allow view classes to call it.
282      *
283      * @param firstRow the first row, in terms of the underlying model
284      * @param endRow the last row, in terms of the underlying model
285      * @param column the column that has changed, in terms of the underlying
286      *        model
287      * @throws IndexOutOfBoundsException if either argument is outside
288      *         the range of the underlying model after the change,
289      *         <code>firstRow</code> &gt; <code>endRow</code>, or
290      *         <code>column</code> is outside the range of the underlying
291      *          model
292      */
rowsUpdated(int firstRow, int endRow, int column)293     public abstract void rowsUpdated(int firstRow, int endRow, int column);
294 
295     /**
296      * Adds a <code>RowSorterListener</code> to receive notification
297      * about this <code>RowSorter</code>.  If the same
298      * listener is added more than once it will receive multiple
299      * notifications.  If <code>l</code> is <code>null</code> nothing
300      * is done.
301      *
302      * @param l the <code>RowSorterListener</code>
303      */
addRowSorterListener(RowSorterListener l)304     public void addRowSorterListener(RowSorterListener l) {
305         listenerList.add(RowSorterListener.class, l);
306     }
307 
308     /**
309      * Removes a <code>RowSorterListener</code>.  If
310      * <code>l</code> is <code>null</code> nothing is done.
311      *
312      * @param l the <code>RowSorterListener</code>
313      */
removeRowSorterListener(RowSorterListener l)314     public void removeRowSorterListener(RowSorterListener l) {
315         listenerList.remove(RowSorterListener.class, l);
316     }
317 
318     /**
319      * Notifies listener that the sort order has changed.
320      */
fireSortOrderChanged()321     protected void fireSortOrderChanged() {
322         fireRowSorterChanged(new RowSorterEvent(this));
323     }
324 
325     /**
326      * Notifies listener that the mapping has changed.
327      *
328      * @param lastRowIndexToModel the mapping from model indices to
329      *        view indices prior to the sort, may be <code>null</code>
330      */
fireRowSorterChanged(int[] lastRowIndexToModel)331     protected void fireRowSorterChanged(int[] lastRowIndexToModel) {
332         fireRowSorterChanged(new RowSorterEvent(this,
333                 RowSorterEvent.Type.SORTED, lastRowIndexToModel));
334     }
335 
fireRowSorterChanged(RowSorterEvent event)336     void fireRowSorterChanged(RowSorterEvent event) {
337         Object[] listeners = listenerList.getListenerList();
338         for (int i = listeners.length - 2; i >= 0; i -= 2) {
339             if (listeners[i] == RowSorterListener.class) {
340                 ((RowSorterListener)listeners[i + 1]).
341                         sorterChanged(event);
342             }
343         }
344     }
345 
346     /**
347      * SortKey describes the sort order for a particular column.  The
348      * column index is in terms of the underlying model, which may differ
349      * from that of the view.
350      *
351      * @since 1.6
352      */
353     public static class SortKey {
354         private int column;
355         private SortOrder sortOrder;
356 
357         /**
358          * Creates a <code>SortKey</code> for the specified column with
359          * the specified sort order.
360          *
361          * @param column index of the column, in terms of the model
362          * @param sortOrder the sorter order
363          * @throws IllegalArgumentException if <code>sortOrder</code> is
364          *         <code>null</code>
365          */
SortKey(int column, SortOrder sortOrder)366         public SortKey(int column, SortOrder sortOrder) {
367             if (sortOrder == null) {
368                 throw new IllegalArgumentException(
369                         "sort order must be non-null");
370             }
371             this.column = column;
372             this.sortOrder = sortOrder;
373         }
374 
375         /**
376          * Returns the index of the column.
377          *
378          * @return index of column
379          */
getColumn()380         public final int getColumn() {
381             return column;
382         }
383 
384         /**
385          * Returns the sort order of the column.
386          *
387          * @return the sort order of the column
388          */
getSortOrder()389         public final SortOrder getSortOrder() {
390             return sortOrder;
391         }
392 
393         /**
394          * Returns the hash code for this <code>SortKey</code>.
395          *
396          * @return hash code
397          */
hashCode()398         public int hashCode() {
399             int result = 17;
400             result = 37 * result + column;
401             result = 37 * result + sortOrder.hashCode();
402             return result;
403         }
404 
405         /**
406          * Returns true if this object equals the specified object.
407          * If the specified object is a <code>SortKey</code> and
408          * references the same column and sort order, the two objects
409          * are equal.
410          *
411          * @param o the object to compare to
412          * @return true if <code>o</code> is equal to this <code>SortKey</code>
413          */
equals(Object o)414         public boolean equals(Object o) {
415             if (o == this) {
416                 return true;
417             }
418             if (o instanceof SortKey) {
419                 return (((SortKey)o).column == column &&
420                         ((SortKey)o).sortOrder == sortOrder);
421             }
422             return false;
423         }
424     }
425 }
426