001 /*
002 * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025 package javax.swing;
026
027 import javax.swing.SortOrder;
028 import javax.swing.event.*;
029 import java.util.*;
030
031 /**
032 * <code>RowSorter</code> provides the basis for sorting and filtering.
033 * Beyond creating and installing a <code>RowSorter</code>, you very rarely
034 * need to interact with one directly. Refer to
035 * {@link javax.swing.table.TableRowSorter TableRowSorter} for a concrete
036 * implementation of <code>RowSorter</code> for <code>JTable</code>.
037 * <p>
038 * <code>RowSorter</code>'s primary role is to provide a mapping between
039 * two coordinate systems: that of the view (for example a
040 * <code>JTable</code>) and that of the underlying data source, typically a
041 * model.
042 * <p>
043 * The view invokes the following methods on the <code>RowSorter</code>:
044 * <ul>
045 * <li><code>toggleSortOrder</code> — The view invokes this when the
046 * appropriate user gesture has occurred to trigger a sort. For example,
047 * the user clicked a column header in a table.
048 * <li>One of the model change methods — The view invokes a model
049 * change method when the underlying model
050 * has changed. There may be order dependencies in how the events are
051 * delivered, so a <code>RowSorter</code> should not update its mapping
052 * until one of these methods is invoked.
053 * </ul>
054 * Because the view makes extensive use of the
055 * <code>convertRowIndexToModel</code>,
056 * <code>convertRowIndexToView</code> and <code>getViewRowCount</code> methods,
057 * these methods need to be fast.
058 * <p>
059 * <code>RowSorter</code> provides notification of changes by way of
060 * <code>RowSorterListener</code>. Two types of notification are sent:
061 * <ul>
062 * <li><code>RowSorterEvent.Type.SORT_ORDER_CHANGED</code> — notifies
063 * listeners that the sort order has changed. This is typically followed
064 * by a notification that the sort has changed.
065 * <li><code>RowSorterEvent.Type.SORTED</code> — notifies listeners that
066 * the mapping maintained by the <code>RowSorter</code> has changed in
067 * some way.
068 * </ul>
069 * <code>RowSorter</code> implementations typically don't have a one-to-one
070 * mapping with the underlying model, but they can.
071 * For example, if a database does the sorting,
072 * <code>toggleSortOrder</code> might call through to the database
073 * (on a background thread), and override the mapping methods to return the
074 * argument that is passed in.
075 * <p>
076 * Concrete implementations of <code>RowSorter</code>
077 * need to reference a model such as <code>TableModel</code> or
078 * <code>ListModel</code>. The view classes, such as
079 * <code>JTable</code> and <code>JList</code>, will also have a
080 * reference to the model. To avoid ordering dependencies,
081 * <code>RowSorter</code> implementations should not install a
082 * listener on the model. Instead the view class will call into the
083 * <code>RowSorter</code> when the model changes. For
084 * example, if a row is updated in a <code>TableModel</code>
085 * <code>JTable</code> invokes <code>rowsUpdated</code>.
086 * When the model changes, the view may call into any of the following methods:
087 * <code>modelStructureChanged</code>, <code>allRowsChanged</code>,
088 * <code>rowsInserted</code>, <code>rowsDeleted</code> and
089 * <code>rowsUpdated</code>.
090 *
091 * @param <M> the type of the underlying model
092 * @version 1.13 05/05/07
093 * @see javax.swing.table.TableRowSorter
094 * @since 1.6
095 */
096 public abstract class RowSorter<M> {
097 private EventListenerList listenerList = new EventListenerList();
098
099 /**
100 * Creates a <code>RowSorter</code>.
101 */
102 public RowSorter() {
103 }
104
105 /**
106 * Returns the underlying model.
107 *
108 * @return the underlying model
109 */
110 public abstract M getModel();
111
112 /**
113 * Reverses the sort order of the specified column. It is up to
114 * subclasses to provide the exact behavior when invoked. Typically
115 * this will reverse the sort order from ascending to descending (or
116 * descending to ascending) if the specified column is already the
117 * primary sorted column; otherwise, makes the specified column
118 * the primary sorted column, with an ascending sort order. If
119 * the specified column is not sortable, this method has no
120 * effect.
121 * <p>
122 * If this results in changing the sort order and sorting, the
123 * appropriate <code>RowSorterListener</code> notification will be
124 * sent.
125 *
126 * @param column the column to toggle the sort ordering of, in
127 * terms of the underlying model
128 * @throws IndexOutOfBoundsException if column is outside the range of
129 * the underlying model
130 */
131 public abstract void toggleSortOrder(int column);
132
133 /**
134 * Returns the location of <code>index</code> in terms of the
135 * underlying model. That is, for the row <code>index</code> in
136 * the coordinates of the view this returns the row index in terms
137 * of the underlying model.
138 *
139 * @param index the row index in terms of the underlying view
140 * @return row index in terms of the view
141 * @throws IndexOutOfBoundsException if <code>index</code> is outside the
142 * range of the view
143 */
144 public abstract int convertRowIndexToModel(int index);
145
146 /**
147 * Returns the location of <code>index</code> in terms of the
148 * view. That is, for the row <code>index</code> in the
149 * coordinates of the underlying model this returns the row index
150 * in terms of the view.
151 *
152 * @param index the row index in terms of the underlying model
153 * @return row index in terms of the view, or -1 if index has been
154 * filtered out of the view
155 * @throws IndexOutOfBoundsException if <code>index</code> is outside
156 * the range of the model
157 */
158 public abstract int convertRowIndexToView(int index);
159
160 /**
161 * Sets the current sort keys.
162 *
163 * @param keys the new <code>SortKeys</code>; <code>null</code>
164 * is a shorthand for specifying an empty list,
165 * indicating that the view should be unsorted
166 */
167 public abstract void setSortKeys(List<? extends SortKey> keys);
168
169 /**
170 * Returns the current sort keys. This must return a {@code
171 * non-null List} and may return an unmodifiable {@code List}. If
172 * you need to change the sort keys, make a copy of the returned
173 * {@code List}, mutate the copy and invoke {@code setSortKeys}
174 * with the new list.
175 *
176 * @return the current sort order
177 */
178 public abstract List<? extends SortKey> getSortKeys();
179
180 /**
181 * Returns the number of rows in the view. If the contents have
182 * been filtered this might differ from the row count of the
183 * underlying model.
184 *
185 * @return number of rows in the view
186 * @see #getModelRowCount
187 */
188 public abstract int getViewRowCount();
189
190 /**
191 * Returns the number of rows in the underlying model.
192 *
193 * @return number of rows in the underlying model
194 * @see #getViewRowCount
195 */
196 public abstract int getModelRowCount();
197
198 /**
199 * Invoked when the underlying model structure has completely
200 * changed. For example, if the number of columns in a
201 * <code>TableModel</code> changed, this method would be invoked.
202 * <p>
203 * You normally do not call this method. This method is public
204 * to allow view classes to call it.
205 */
206 public abstract void modelStructureChanged();
207
208 /**
209 * Invoked when the contents of the underlying model have
210 * completely changed. The structure of the table is the same,
211 * only the contents have changed. This is typically sent when it
212 * is too expensive to characterize the change in terms of the
213 * other methods.
214 * <p>
215 * You normally do not call this method. This method is public
216 * to allow view classes to call it.
217 */
218 public abstract void allRowsChanged();
219
220 /**
221 * Invoked when rows have been inserted into the underlying model
222 * in the specified range (inclusive).
223 * <p>
224 * The arguments give the indices of the effected range.
225 * The first argument is in terms of the model before the change, and
226 * must be less than or equal to the size of the model before the change.
227 * The second argument is in terms of the model after the change and must
228 * be less than the size of the model after the change. For example,
229 * if you have a 5-row model and add 3 items to the end of the model
230 * the indices are 5, 7.
231 * <p>
232 * You normally do not call this method. This method is public
233 * to allow view classes to call it.
234 *
235 * @param firstRow the first row
236 * @param endRow the last row
237 * @throws IndexOutOfBoundsException if either argument is invalid, or
238 * <code>firstRow</code> > <code>endRow</code>
239 */
240 public abstract void rowsInserted(int firstRow, int endRow);
241
242 /**
243 * Invoked when rows have been deleted from the underlying model
244 * in the specified range (inclusive).
245 * <p>
246 * The arguments give the indices of the effected range and
247 * are in terms of the model <b>before</b> the change.
248 * For example, if you have a 5-row model and delete 3 items from the end
249 * of the model the indices are 2, 4.
250 * <p>
251 * You normally do not call this method. This method is public
252 * to allow view classes to call it.
253 *
254 * @param firstRow the first row
255 * @param endRow the last row
256 * @throws IndexOutOfBoundsException if either argument is outside
257 * the range of the model before the change, or
258 * <code>firstRow</code> > <code>endRow</code>
259 */
260 public abstract void rowsDeleted(int firstRow, int endRow);
261
262 /**
263 * Invoked when rows have been changed in the underlying model
264 * between the specified range (inclusive).
265 * <p>
266 * You normally do not call this method. This method is public
267 * to allow view classes to call it.
268 *
269 * @param firstRow the first row, in terms of the underlying model
270 * @param endRow the last row, in terms of the underlying model
271 * @throws IndexOutOfBoundsException if either argument is outside
272 * the range of the underlying model, or
273 * <code>firstRow</code> > <code>endRow</code>
274 */
275 public abstract void rowsUpdated(int firstRow, int endRow);
276
277 /**
278 * Invoked when the column in the rows have been updated in
279 * the underlying model between the specified range.
280 * <p>
281 * You normally do not call this method. This method is public
282 * to allow view classes to call it.
283 *
284 * @param firstRow the first row, in terms of the underlying model
285 * @param endRow the last row, in terms of the underlying model
286 * @param column the column that has changed, in terms of the underlying
287 * model
288 * @throws IndexOutOfBoundsException if either argument is outside
289 * the range of the underlying model after the change,
290 * <code>firstRow</code> > <code>endRow</code>, or
291 * <code>column</code> is outside the range of the underlying
292 * model
293 */
294 public abstract void rowsUpdated(int firstRow, int endRow,
295 int column);
296
297 /**
298 * Adds a <code>RowSorterListener</code> to receive notification
299 * about this <code>RowSorter</code>. If the same
300 * listener is added more than once it will receive multiple
301 * notifications. If <code>l</code> is <code>null</code> nothing
302 * is done.
303 *
304 * @param l the <code>RowSorterListener</code>
305 */
306 public void addRowSorterListener(RowSorterListener l) {
307 listenerList.add(RowSorterListener.class, l);
308 }
309
310 /**
311 * Removes a <code>RowSorterListener</code>. If
312 * <code>l</code> is <code>null</code> nothing is done.
313 *
314 * @param l the <code>RowSorterListener</code>
315 */
316 public void removeRowSorterListener(RowSorterListener l) {
317 listenerList.remove(RowSorterListener.class, l);
318 }
319
320 /**
321 * Notifies listener that the sort order has changed.
322 */
323 protected void fireSortOrderChanged() {
324 fireRowSorterChanged(new RowSorterEvent(this ));
325 }
326
327 /**
328 * Notifies listener that the mapping has changed.
329 *
330 * @param lastRowIndexToModel the mapping from model indices to
331 * view indices prior to the sort, may be <code>null</code>
332 */
333 protected void fireRowSorterChanged(int[] lastRowIndexToModel) {
334 fireRowSorterChanged(new RowSorterEvent(this ,
335 RowSorterEvent.Type.SORTED, lastRowIndexToModel));
336 }
337
338 void fireRowSorterChanged(RowSorterEvent event) {
339 Object[] listeners = listenerList.getListenerList();
340 for (int i = listeners.length - 2; i >= 0; i -= 2) {
341 if (listeners[i] == RowSorterListener.class) {
342 ((RowSorterListener) listeners[i + 1])
343 .sorterChanged(event);
344 }
345 }
346 }
347
348 /**
349 * SortKey describes the sort order for a particular column. The
350 * column index is in terms of the underlying model, which may differ
351 * from that of the view.
352 *
353 * @since 1.6
354 */
355 public static class SortKey {
356 private int column;
357 private SortOrder sortOrder;
358
359 /**
360 * Creates a <code>SortKey</code> for the specified column with
361 * the specified sort order.
362 *
363 * @param column index of the column, in terms of the model
364 * @param sortOrder the sorter order
365 * @throws IllegalArgumentException if <code>sortOrder</code> is
366 * <code>null</code>
367 */
368 public SortKey(int column, SortOrder sortOrder) {
369 if (sortOrder == null) {
370 throw new IllegalArgumentException(
371 "sort order must be non-null");
372 }
373 this .column = column;
374 this .sortOrder = sortOrder;
375 }
376
377 /**
378 * Returns the index of the column.
379 *
380 * @return index of column
381 */
382 public final int getColumn() {
383 return column;
384 }
385
386 /**
387 * Returns the sort order of the column.
388 *
389 * @return the sort order of the column
390 */
391 public final SortOrder getSortOrder() {
392 return sortOrder;
393 }
394
395 /**
396 * Returns the hash code for this <code>SortKey</code>.
397 *
398 * @return hash code
399 */
400 public int hashCode() {
401 int result = 17;
402 result = 37 * result + column;
403 result = 37 * result + sortOrder.hashCode();
404 return result;
405 }
406
407 /**
408 * Returns true if this object equals the specified object.
409 * If the specified object is a <code>SortKey</code> and
410 * references the same column and sort order, the two objects
411 * are equal.
412 *
413 * @param o the object to compare to
414 * @return true if <code>o</code> is equal to this <code>SortKey</code>
415 */
416 public boolean equals(Object o) {
417 if (o == this ) {
418 return true;
419 }
420 if (o instanceof SortKey) {
421 return (((SortKey) o).column == column && ((SortKey) o).sortOrder == sortOrder);
422 }
423 return false;
424 }
425 }
426 }
|