blob: 29e8db8e159275c44b7530853c3c1e9ff2f226ab [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25package javax.swing;
26
27import javax.swing.SortOrder;
28import javax.swing.event.*;
29import 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 */
95public abstract class RowSorter<M> {
96 private EventListenerList listenerList = new EventListenerList();
97
98 /**
99 * Creates a <code>RowSorter</code>.
100 */
101 public RowSorter() {
102 }
103
104 /**
105 * Returns the underlying model.
106 *
107 * @return the underlying model
108 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
331 protected void fireRowSorterChanged(int[] lastRowIndexToModel) {
332 fireRowSorterChanged(new RowSorterEvent(this,
333 RowSorterEvent.Type.SORTED, lastRowIndexToModel));
334 }
335
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 */
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 */
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 */
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 */
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 */
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}