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 java.text.Collator;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.Comparator;
32 import java.util.List;
33 import javax.swing.SortOrder;
34
35 /**
36 * An implementation of <code>RowSorter</code> that provides sorting and
37 * filtering around a grid-based data model.
38 * Beyond creating and installing a <code>RowSorter</code>, you very rarely
39 * need to interact with one directly. Refer to
40 * {@link javax.swing.table.TableRowSorter TableRowSorter} for a concrete
41 * implementation of <code>RowSorter</code> for <code>JTable</code>.
42 * <p>
43 * Sorting is done based on the current <code>SortKey</code>s, in order.
44 * If two objects are equal (the <code>Comparator</code> for the
45 * column returns 0) the next <code>SortKey</code> is used. If no
46 * <code>SortKey</code>s remain or the order is <code>UNSORTED</code>, then
47 * the order of the rows in the model is used.
48 * <p>
49 * Sorting of each column is done by way of a <code>Comparator</code>
50 * that you can specify using the <code>setComparator</code> method.
51 * If a <code>Comparator</code> has not been specified, the
52 * <code>Comparator</code> returned by
53 * <code>Collator.getInstance()</code> is used on the results of
54 * calling <code>toString</code> on the underlying objects. The
55 * <code>Comparator</code> is never passed <code>null</code>. A
56 * <code>null</code> value is treated as occurring before a
57 * non-<code>null</code> value, and two <code>null</code> values are
58 * considered equal.
59 * <p>
60 * If you specify a <code>Comparator</code> that casts its argument to
61 * a type other than that provided by the model, a
62 * <code>ClassCastException</code> will be thrown when the data is sorted.
63 * <p>
64 * In addition to sorting, <code>DefaultRowSorter</code> provides the
65 * ability to filter rows. Filtering is done by way of a
66 * <code>RowFilter</code> that is specified using the
67 * <code>setRowFilter</code> method. If no filter has been specified all
68 * rows are included.
69 * <p>
70 * By default, rows are in unsorted order (the same as the model) and
71 * every column is sortable. The default <code>Comparator</code>s are
72 * documented in the subclasses (for example, {@link
73 * javax.swing.table.TableRowSorter TableRowSorter}).
74 * <p>
75 * If the underlying model structure changes (the
76 * <code>modelStructureChanged</code> method is invoked) the following
77 * are reset to their default values: <code>Comparator</code>s by
78 * column, current sort order, and whether each column is sortable. To
79 * find the default <code>Comparator</code>s, see the concrete
80 * implementation (for example, {@link
81 * javax.swing.table.TableRowSorter TableRowSorter}). The default
82 * sort order is unsorted (the same as the model), and columns are
83 * sortable by default.
84 * <p>
85 * If the underlying model structure changes (the
86 * <code>modelStructureChanged</code> method is invoked) the following
87 * are reset to their default values: <code>Comparator</code>s by column,
88 * current sort order and whether a column is sortable.
89 * <p>
90 * <code>DefaultRowSorter</code> is an abstract class. Concrete
91 * subclasses must provide access to the underlying data by invoking
92 * {@code setModelWrapper}. The {@code setModelWrapper} method
93 * <b>must</b> be invoked soon after the constructor is
94 * called, ideally from within the subclass's constructor.
95 * Undefined behavior will result if you use a {@code
96 * DefaultRowSorter} without specifying a {@code ModelWrapper}.
97 * <p>
98 * <code>DefaultRowSorter</code> has two formal type parameters. The
99 * first type parameter corresponds to the class of the model, for example
100 * <code>DefaultTableModel</code>. The second type parameter
101 * corresponds to the class of the identifier passed to the
102 * <code>RowFilter</code>. Refer to <code>TableRowSorter</code> and
103 * <code>RowFilter</code> for more details on the type parameters.
104 *
105 * @param <M> the type of the model
106 * @param <I> the type of the identifier passed to the <code>RowFilter</code>
107 * @see javax.swing.table.TableRowSorter
108 * @see javax.swing.table.DefaultTableModel
109 * @see java.text.Collator
110 * @since 1.6
111 */
112 public abstract class DefaultRowSorter<M, I> extends RowSorter<M> {
113 /**
114 * Whether or not we resort on TableModelEvent.UPDATEs.
115 */
116 private boolean sortsOnUpdates;
117
118 /**
119 * View (JTable) -> model.
120 */
121 private Row[] viewToModel;
122
123 /**
124 * model -> view (JTable)
125 */
126 private int[] modelToView;
173 private boolean sorted;
174
175 /**
176 * Maximum number of sort keys.
177 */
178 private int maxSortKeys;
179
180 /**
181 * Provides access to the data we're sorting/filtering.
182 */
183 private ModelWrapper<M,I> modelWrapper;
184
185 /**
186 * Size of the model. This is used to enforce error checking within
187 * the table changed notification methods (such as rowsInserted).
188 */
189 private int modelRowCount;
190
191
192 /**
193 * Creates an empty <code>DefaultRowSorter</code>.
194 */
195 public DefaultRowSorter() {
196 sortKeys = Collections.emptyList();
197 maxSortKeys = 3;
198 }
199
200 /**
201 * Sets the model wrapper providing the data that is being sorted and
202 * filtered.
203 *
204 * @param modelWrapper the model wrapper responsible for providing the
205 * data that gets sorted and filtered
206 * @throws IllegalArgumentException if {@code modelWrapper} is
207 * {@code null}
208 */
209 protected final void setModelWrapper(ModelWrapper<M,I> modelWrapper) {
210 if (modelWrapper == null) {
211 throw new IllegalArgumentException(
212 "modelWrapper most be non-null");
213 }
227 * filtered.
228 *
229 * @return the model wrapper responsible for providing the data that
230 * gets sorted and filtered
231 */
232 protected final ModelWrapper<M,I> getModelWrapper() {
233 return modelWrapper;
234 }
235
236 /**
237 * Returns the underlying model.
238 *
239 * @return the underlying model
240 */
241 public final M getModel() {
242 return getModelWrapper().getModel();
243 }
244
245 /**
246 * Sets whether or not the specified column is sortable. The specified
247 * value is only checked when <code>toggleSortOrder</code> is invoked.
248 * It is still possible to sort on a column that has been marked as
249 * unsortable by directly setting the sort keys. The default is
250 * true.
251 *
252 * @param column the column to enable or disable sorting on, in terms
253 * of the underlying model
254 * @param sortable whether or not the specified column is sortable
255 * @throws IndexOutOfBoundsException if <code>column</code> is outside
256 * the range of the model
257 * @see #toggleSortOrder
258 * @see #setSortKeys
259 */
260 public void setSortable(int column, boolean sortable) {
261 checkColumn(column);
262 if (isSortable == null) {
263 isSortable = new boolean[getModelWrapper().getColumnCount()];
264 for (int i = isSortable.length - 1; i >= 0; i--) {
265 isSortable[i] = true;
266 }
267 }
268 isSortable[column] = sortable;
269 }
270
271 /**
272 * Returns true if the specified column is sortable; otherwise, false.
273 *
274 * @param column the column to check sorting for, in terms of the
275 * underlying model
276 * @return true if the column is sortable
277 * @throws IndexOutOfBoundsException if column is outside
278 * the range of the underlying model
279 */
280 public boolean isSortable(int column) {
281 checkColumn(column);
282 return (isSortable == null) ? true : isSortable[column];
283 }
284
285 /**
286 * Sets the sort keys. This creates a copy of the supplied
287 * {@code List}; subsequent changes to the supplied
288 * {@code List} do not effect this {@code DefaultRowSorter}.
289 * If the sort keys have changed this triggers a sort.
290 *
291 * @param sortKeys the new <code>SortKeys</code>; <code>null</code>
292 * is a shorthand for specifying an empty list,
293 * indicating that the view should be unsorted
294 * @throws IllegalArgumentException if any of the values in
295 * <code>sortKeys</code> are null or have a column index outside
296 * the range of the model
297 */
298 public void setSortKeys(List<? extends SortKey> sortKeys) {
299 List<SortKey> old = this.sortKeys;
300 if (sortKeys != null && sortKeys.size() > 0) {
301 int max = getModelWrapper().getColumnCount();
302 for (SortKey key : sortKeys) {
303 if (key == null || key.getColumn() < 0 ||
304 key.getColumn() >= max) {
305 throw new IllegalArgumentException("Invalid SortKey");
306 }
307 }
308 this.sortKeys = Collections.unmodifiableList(
309 new ArrayList<SortKey>(sortKeys));
310 }
311 else {
312 this.sortKeys = Collections.emptyList();
313 }
314 if (!this.sortKeys.equals(old)) {
315 fireSortOrderChanged();
322 }
323 }
324 }
325
326 /**
327 * Returns the current sort keys. This returns an unmodifiable
328 * {@code non-null List}. If you need to change the sort keys,
329 * make a copy of the returned {@code List}, mutate the copy
330 * and invoke {@code setSortKeys} with the new list.
331 *
332 * @return the current sort order
333 */
334 public List<? extends SortKey> getSortKeys() {
335 return sortKeys;
336 }
337
338 /**
339 * Sets the maximum number of sort keys. The number of sort keys
340 * determines how equal values are resolved when sorting. For
341 * example, assume a table row sorter is created and
342 * <code>setMaxSortKeys(2)</code> is invoked on it. The user
343 * clicks the header for column 1, causing the table rows to be
344 * sorted based on the items in column 1. Next, the user clicks
345 * the header for column 2, causing the table to be sorted based
346 * on the items in column 2; if any items in column 2 are equal,
347 * then those particular rows are ordered based on the items in
348 * column 1. In this case, we say that the rows are primarily
349 * sorted on column 2, and secondarily on column 1. If the user
350 * then clicks the header for column 3, then the items are
351 * primarily sorted on column 3 and secondarily sorted on column
352 * 2. Because the maximum number of sort keys has been set to 2
353 * with <code>setMaxSortKeys</code>, column 1 no longer has an
354 * effect on the order.
355 * <p>
356 * The maximum number of sort keys is enforced by
357 * <code>toggleSortOrder</code>. You can specify more sort
358 * keys by invoking <code>setSortKeys</code> directly and they will
359 * all be honored. However if <code>toggleSortOrder</code> is subsequently
360 * invoked the maximum number of sort keys will be enforced.
361 * The default value is 3.
362 *
363 * @param max the maximum number of sort keys
364 * @throws IllegalArgumentException if <code>max</code> < 1
365 */
366 public void setMaxSortKeys(int max) {
367 if (max < 1) {
368 throw new IllegalArgumentException("Invalid max");
369 }
370 maxSortKeys = max;
371 }
372
373 /**
374 * Returns the maximum number of sort keys.
375 *
376 * @return the maximum number of sort keys
377 */
378 public int getMaxSortKeys() {
379 return maxSortKeys;
380 }
381
382 /**
383 * If true, specifies that a sort should happen when the underlying
384 * model is updated (<code>rowsUpdated</code> is invoked). For
385 * example, if this is true and the user edits an entry the
386 * location of that item in the view may change. The default is
387 * false.
388 *
389 * @param sortsOnUpdates whether or not to sort on update events
390 */
391 public void setSortsOnUpdates(boolean sortsOnUpdates) {
392 this.sortsOnUpdates = sortsOnUpdates;
393 }
394
395 /**
396 * Returns true if a sort should happen when the underlying
397 * model is updated; otherwise, returns false.
398 *
399 * @return whether or not to sort when the model is updated
400 */
401 public boolean getSortsOnUpdates() {
402 return sortsOnUpdates;
403 }
404
405 /**
406 * Sets the filter that determines which rows, if any, should be
407 * hidden from the view. The filter is applied before sorting. A value
408 * of <code>null</code> indicates all values from the model should be
409 * included.
410 * <p>
411 * <code>RowFilter</code>'s <code>include</code> method is passed an
412 * <code>Entry</code> that wraps the underlying model. The number
413 * of columns in the <code>Entry</code> corresponds to the
414 * number of columns in the <code>ModelWrapper</code>. The identifier
415 * comes from the <code>ModelWrapper</code> as well.
416 * <p>
417 * This method triggers a sort.
418 *
419 * @param filter the filter used to determine what entries should be
420 * included
421 */
422 public void setRowFilter(RowFilter<? super M,? super I> filter) {
423 this.filter = filter;
424 sort();
425 }
426
427 /**
428 * Returns the filter that determines which rows, if any, should
429 * be hidden from view.
430 *
431 * @return the filter
432 */
433 public RowFilter<? super M,? super I> getRowFilter() {
434 return filter;
435 }
544 for (int i = 0; i < modelToView.length; i++) {
545 if (modelToView[i] != -1) {
546 viewToModel[included].modelIndex = i;
547 modelToView[i] = included++;
548 }
549 }
550 }
551 } else {
552 // sort the data
553 Arrays.sort(viewToModel);
554
555 // Update the modelToView array
556 setModelToViewFromViewToModel(false);
557 }
558 fireRowSorterChanged(lastViewToModel);
559 }
560
561 /**
562 * Sorts and filters the rows in the view based on the sort keys
563 * of the columns currently being sorted and the filter, if any,
564 * associated with this sorter. An empty <code>sortKeys</code> list
565 * indicates that the view should unsorted, the same as the model.
566 *
567 * @see #setRowFilter
568 * @see #setSortKeys
569 */
570 public void sort() {
571 sorted = true;
572 int[] lastViewToModel = getViewToModelAsInts(viewToModel);
573 updateUseToString();
574 if (isUnsorted()) {
575 // Unsorted
576 cachedSortKeys = new SortKey[0];
577 if (getRowFilter() == null) {
578 // No filter & unsorted
579 if (viewToModel != null) {
580 // sorted -> unsorted
581 viewToModel = null;
582 modelToView = null;
583 }
584 else {
688 for (i = recreateFrom; i < rowCount; i++) {
689 viewToModel[i] = new Row(this, i);
690 }
691 }
692
693 /**
694 * Caches the sort keys before a sort.
695 */
696 private void cacheSortKeys(List<? extends SortKey> keys) {
697 int keySize = keys.size();
698 sortComparators = new Comparator<?>[keySize];
699 for (int i = 0; i < keySize; i++) {
700 sortComparators[i] = getComparator0(keys.get(i).getColumn());
701 }
702 cachedSortKeys = keys.toArray(new SortKey[keySize]);
703 }
704
705 /**
706 * Returns whether or not to convert the value to a string before
707 * doing comparisons when sorting. If true
708 * <code>ModelWrapper.getStringValueAt</code> will be used, otherwise
709 * <code>ModelWrapper.getValueAt</code> will be used. It is up to
710 * subclasses, such as <code>TableRowSorter</code>, to honor this value
711 * in their <code>ModelWrapper</code> implementation.
712 *
713 * @param column the index of the column to test, in terms of the
714 * underlying model
715 * @return true if values are to be converted to strings before doing
716 * comparisons when sorting
717 * @throws IndexOutOfBoundsException if <code>column</code> is not valid
718 */
719 protected boolean useToString(int column) {
720 return (getComparator(column) == null);
721 }
722
723 /**
724 * Refreshes the modelToView mapping from that of viewToModel.
725 * If <code>unsetFirst</code> is true, all indices in modelToView are
726 * first set to -1.
727 */
728 private void setModelToViewFromViewToModel(boolean unsetFirst) {
729 int i;
730 if (unsetFirst) {
731 for (i = modelToView.length - 1; i >= 0; i--) {
732 modelToView[i] = -1;
733 }
734 }
735 for (i = viewToModel.length - 1; i >= 0; i--) {
736 modelToView[viewToModel[i].modelIndex] = i;
737 }
738 }
739
740 private int[] getViewToModelAsInts(Row[] viewToModel) {
741 if (viewToModel != null) {
742 int[] viewToModelI = new int[viewToModel.length];
743 for (int i = viewToModel.length - 1; i >= 0; i--) {
744 viewToModelI[i] = viewToModel[i].modelIndex;
745 }
746 return viewToModelI;
747 }
748 return new int[0];
749 }
750
751 /**
752 * Sets the <code>Comparator</code> to use when sorting the specified
753 * column. This does not trigger a sort. If you want to sort after
754 * setting the comparator you need to explicitly invoke <code>sort</code>.
755 *
756 * @param column the index of the column the <code>Comparator</code> is
757 * to be used for, in terms of the underlying model
758 * @param comparator the <code>Comparator</code> to use
759 * @throws IndexOutOfBoundsException if <code>column</code> is outside
760 * the range of the underlying model
761 */
762 public void setComparator(int column, Comparator<?> comparator) {
763 checkColumn(column);
764 if (comparators == null) {
765 comparators = new Comparator<?>[getModelWrapper().getColumnCount()];
766 }
767 comparators[column] = comparator;
768 }
769
770 /**
771 * Returns the <code>Comparator</code> for the specified
772 * column. This will return <code>null</code> if a <code>Comparator</code>
773 * has not been specified for the column.
774 *
775 * @param column the column to fetch the <code>Comparator</code> for, in
776 * terms of the underlying model
777 * @return the <code>Comparator</code> for the specified column
778 * @throws IndexOutOfBoundsException if column is outside
779 * the range of the underlying model
780 */
781 public Comparator<?> getComparator(int column) {
782 checkColumn(column);
783 if (comparators != null) {
784 return comparators[column];
785 }
786 return null;
787 }
788
789 // Returns the Comparator to use during sorting. Where as
790 // getComparator() may return null, this will never return null.
791 private Comparator<?> getComparator0(int column) {
792 Comparator<?> comparator = getComparator(column);
793 if (comparator != null) {
794 return comparator;
795 }
796 // This should be ok as useToString(column) should have returned
797 // true in this case.
999 private void insertInOrder(List<Row> toAdd, Row[] current) {
1000 int last = 0;
1001 int index;
1002 int max = toAdd.size();
1003 for (int i = 0; i < max; i++) {
1004 index = Arrays.binarySearch(current, toAdd.get(i));
1005 if (index < 0) {
1006 index = -1 - index;
1007 }
1008 System.arraycopy(current, last,
1009 viewToModel, last + i, index - last);
1010 viewToModel[index + i] = toAdd.get(i);
1011 last = index;
1012 }
1013 System.arraycopy(current, last, viewToModel, last + max,
1014 current.length - last);
1015 }
1016
1017 /**
1018 * Returns true if we should try and optimize the processing of the
1019 * <code>TableModelEvent</code>. If this returns false, assume the
1020 * event was dealt with and no further processing needs to happen.
1021 */
1022 private boolean shouldOptimizeChange(int firstRow, int lastRow) {
1023 if (!isTransformed()) {
1024 // Not transformed, nothing to do.
1025 return false;
1026 }
1027 if (!sorted || (lastRow - firstRow) > viewToModel.length / 10) {
1028 // We either weren't sorted, or to much changed, sort it all
1029 sort();
1030 return false;
1031 }
1032 return true;
1033 }
1034
1035 private void rowsInserted0(int firstRow, int lastRow) {
1036 int[] oldViewToModel = getViewToModelAsInts(viewToModel);
1037 int i;
1038 int delta = (lastRow - firstRow) + 1;
1039 List<Row> added = new ArrayList<Row>(delta);
1210
1211 // Rebuild the new viewToModel array
1212 insertInOrder(updated, intermediary);
1213
1214 // Update modelToView
1215 setModelToViewFromViewToModel(true);
1216 }
1217 // And finally fire a sort event.
1218 fireRowSorterChanged(oldViewToModel);
1219 }
1220
1221 private void checkColumn(int column) {
1222 if (column < 0 || column >= getModelWrapper().getColumnCount()) {
1223 throw new IndexOutOfBoundsException(
1224 "column beyond range of TableModel");
1225 }
1226 }
1227
1228
1229 /**
1230 * <code>DefaultRowSorter.ModelWrapper</code> is responsible for providing
1231 * the data that gets sorted by <code>DefaultRowSorter</code>. You
1232 * normally do not interact directly with <code>ModelWrapper</code>.
1233 * Subclasses of <code>DefaultRowSorter</code> provide an
1234 * implementation of <code>ModelWrapper</code> wrapping another model.
1235 * For example,
1236 * <code>TableRowSorter</code> provides a <code>ModelWrapper</code> that
1237 * wraps a <code>TableModel</code>.
1238 * <p>
1239 * <code>ModelWrapper</code> makes a distinction between values as
1240 * <code>Object</code>s and <code>String</code>s. This allows
1241 * implementations to provide a custom string
1242 * converter to be used instead of invoking <code>toString</code> on the
1243 * object.
1244 *
1245 * @param <M> the type of the underlying model
1246 * @param <I> the identifier supplied to the filter
1247 * @since 1.6
1248 * @see RowFilter
1249 * @see RowFilter.Entry
1250 */
1251 protected abstract static class ModelWrapper<M,I> {
1252 /**
1253 * Creates a new <code>ModelWrapper</code>.
1254 */
1255 protected ModelWrapper() {
1256 }
1257
1258 /**
1259 * Returns the underlying model that this <code>Model</code> is
1260 * wrapping.
1261 *
1262 * @return the underlying model
1263 */
1264 public abstract M getModel();
1265
1266 /**
1267 * Returns the number of columns in the model.
1268 *
1269 * @return the number of columns in the model
1270 */
1271 public abstract int getColumnCount();
1272
1273 /**
1274 * Returns the number of rows in the model.
1275 *
1276 * @return the number of rows in the model
1277 */
1278 public abstract int getRowCount();
1279
1280 /**
1281 * Returns the value at the specified index.
1282 *
1283 * @param row the row index
1284 * @param column the column index
1285 * @return the value at the specified index
1286 * @throws IndexOutOfBoundsException if the indices are outside
1287 * the range of the model
1288 */
1289 public abstract Object getValueAt(int row, int column);
1290
1291 /**
1292 * Returns the value as a <code>String</code> at the specified
1293 * index. This implementation uses <code>toString</code> on
1294 * the result from <code>getValueAt</code> (making sure
1295 * to return an empty string for null values). Subclasses that
1296 * override this method should never return null.
1297 *
1298 * @param row the row index
1299 * @param column the column index
1300 * @return the value at the specified index as a <code>String</code>
1301 * @throws IndexOutOfBoundsException if the indices are outside
1302 * the range of the model
1303 */
1304 public String getStringValueAt(int row, int column) {
1305 Object o = getValueAt(row, column);
1306 if (o == null) {
1307 return "";
1308 }
1309 String string = o.toString();
1310 if (string == null) {
1311 return "";
1312 }
1313 return string;
1314 }
1315
1316 /**
1317 * Returns the identifier for the specified row. The return value
1318 * of this is used as the identifier for the
1319 * <code>RowFilter.Entry</code> that is passed to the
1320 * <code>RowFilter</code>.
1321 *
1322 * @param row the row to return the identifier for, in terms of
1323 * the underlying model
1324 * @return the identifier
1325 * @see RowFilter.Entry#getIdentifier
1326 */
1327 public abstract I getIdentifier(int row);
1328 }
1329
1330
1331 /**
1332 * RowFilter.Entry implementation that delegates to the ModelWrapper.
1333 * getFilterEntry(int) creates the single instance of this that is
1334 * passed to the Filter. Only call getFilterEntry(int) to get
1335 * the instance.
1336 */
1337 private class FilterEntry extends RowFilter.Entry<M,I> {
1338 /**
1339 * The index into the model, set in getFilterEntry
1340 */
|
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 java.text.Collator;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.Comparator;
32 import java.util.List;
33 import javax.swing.SortOrder;
34
35 /**
36 * An implementation of {@code RowSorter} that provides sorting and
37 * filtering around a grid-based data model.
38 * Beyond creating and installing a {@code RowSorter}, you very rarely
39 * need to interact with one directly. Refer to
40 * {@link javax.swing.table.TableRowSorter TableRowSorter} for a concrete
41 * implementation of {@code RowSorter} for {@code JTable}.
42 * <p>
43 * Sorting is done based on the current {@code SortKey}s, in order.
44 * If two objects are equal (the {@code Comparator} for the
45 * column returns 0) the next {@code SortKey} is used. If no
46 * {@code SortKey}s remain or the order is {@code UNSORTED}, then
47 * the order of the rows in the model is used.
48 * <p>
49 * Sorting of each column is done by way of a {@code Comparator}
50 * that you can specify using the {@code setComparator} method.
51 * If a {@code Comparator} has not been specified, the
52 * {@code Comparator} returned by
53 * {@code Collator.getInstance()} is used on the results of
54 * calling {@code toString} on the underlying objects. The
55 * {@code Comparator} is never passed {@code null}. A
56 * {@code null} value is treated as occurring before a
57 * non-{@code null} value, and two {@code null} values are
58 * considered equal.
59 * <p>
60 * If you specify a {@code Comparator} that casts its argument to
61 * a type other than that provided by the model, a
62 * {@code ClassCastException} will be thrown when the data is sorted.
63 * <p>
64 * In addition to sorting, {@code DefaultRowSorter} provides the
65 * ability to filter rows. Filtering is done by way of a
66 * {@code RowFilter} that is specified using the
67 * {@code setRowFilter} method. If no filter has been specified all
68 * rows are included.
69 * <p>
70 * By default, rows are in unsorted order (the same as the model) and
71 * every column is sortable. The default {@code Comparator}s are
72 * documented in the subclasses (for example, {@link
73 * javax.swing.table.TableRowSorter TableRowSorter}).
74 * <p>
75 * If the underlying model structure changes (the
76 * {@code modelStructureChanged} method is invoked) the following
77 * are reset to their default values: {@code Comparator}s by
78 * column, current sort order, and whether each column is sortable. To
79 * find the default {@code Comparator}s, see the concrete
80 * implementation (for example, {@link
81 * javax.swing.table.TableRowSorter TableRowSorter}). The default
82 * sort order is unsorted (the same as the model), and columns are
83 * sortable by default.
84 * <p>
85 * If the underlying model structure changes (the
86 * {@code modelStructureChanged} method is invoked) the following
87 * are reset to their default values: {@code Comparator}s by column,
88 * current sort order and whether a column is sortable.
89 * <p>
90 * {@code DefaultRowSorter} is an abstract class. Concrete
91 * subclasses must provide access to the underlying data by invoking
92 * {@code setModelWrapper}. The {@code setModelWrapper} method
93 * <b>must</b> be invoked soon after the constructor is
94 * called, ideally from within the subclass's constructor.
95 * Undefined behavior will result if you use a {@code
96 * DefaultRowSorter} without specifying a {@code ModelWrapper}.
97 * <p>
98 * {@code DefaultRowSorter} has two formal type parameters. The
99 * first type parameter corresponds to the class of the model, for example
100 * {@code DefaultTableModel}. The second type parameter
101 * corresponds to the class of the identifier passed to the
102 * {@code RowFilter}. Refer to {@code TableRowSorter} and
103 * {@code RowFilter} for more details on the type parameters.
104 *
105 * @param <M> the type of the model
106 * @param <I> the type of the identifier passed to the {@code RowFilter}
107 * @see javax.swing.table.TableRowSorter
108 * @see javax.swing.table.DefaultTableModel
109 * @see java.text.Collator
110 * @since 1.6
111 */
112 public abstract class DefaultRowSorter<M, I> extends RowSorter<M> {
113 /**
114 * Whether or not we resort on TableModelEvent.UPDATEs.
115 */
116 private boolean sortsOnUpdates;
117
118 /**
119 * View (JTable) -> model.
120 */
121 private Row[] viewToModel;
122
123 /**
124 * model -> view (JTable)
125 */
126 private int[] modelToView;
173 private boolean sorted;
174
175 /**
176 * Maximum number of sort keys.
177 */
178 private int maxSortKeys;
179
180 /**
181 * Provides access to the data we're sorting/filtering.
182 */
183 private ModelWrapper<M,I> modelWrapper;
184
185 /**
186 * Size of the model. This is used to enforce error checking within
187 * the table changed notification methods (such as rowsInserted).
188 */
189 private int modelRowCount;
190
191
192 /**
193 * Creates an empty {@code DefaultRowSorter}.
194 */
195 public DefaultRowSorter() {
196 sortKeys = Collections.emptyList();
197 maxSortKeys = 3;
198 }
199
200 /**
201 * Sets the model wrapper providing the data that is being sorted and
202 * filtered.
203 *
204 * @param modelWrapper the model wrapper responsible for providing the
205 * data that gets sorted and filtered
206 * @throws IllegalArgumentException if {@code modelWrapper} is
207 * {@code null}
208 */
209 protected final void setModelWrapper(ModelWrapper<M,I> modelWrapper) {
210 if (modelWrapper == null) {
211 throw new IllegalArgumentException(
212 "modelWrapper most be non-null");
213 }
227 * filtered.
228 *
229 * @return the model wrapper responsible for providing the data that
230 * gets sorted and filtered
231 */
232 protected final ModelWrapper<M,I> getModelWrapper() {
233 return modelWrapper;
234 }
235
236 /**
237 * Returns the underlying model.
238 *
239 * @return the underlying model
240 */
241 public final M getModel() {
242 return getModelWrapper().getModel();
243 }
244
245 /**
246 * Sets whether or not the specified column is sortable. The specified
247 * value is only checked when {@code toggleSortOrder} is invoked.
248 * It is still possible to sort on a column that has been marked as
249 * unsortable by directly setting the sort keys. The default is
250 * true.
251 *
252 * @param column the column to enable or disable sorting on, in terms
253 * of the underlying model
254 * @param sortable whether or not the specified column is sortable
255 * @throws IndexOutOfBoundsException if {@code column} is outside
256 * the range of the model
257 * @see #toggleSortOrder
258 * @see #setSortKeys
259 */
260 public void setSortable(int column, boolean sortable) {
261 checkColumn(column);
262 if (isSortable == null) {
263 isSortable = new boolean[getModelWrapper().getColumnCount()];
264 for (int i = isSortable.length - 1; i >= 0; i--) {
265 isSortable[i] = true;
266 }
267 }
268 isSortable[column] = sortable;
269 }
270
271 /**
272 * Returns true if the specified column is sortable; otherwise, false.
273 *
274 * @param column the column to check sorting for, in terms of the
275 * underlying model
276 * @return true if the column is sortable
277 * @throws IndexOutOfBoundsException if column is outside
278 * the range of the underlying model
279 */
280 public boolean isSortable(int column) {
281 checkColumn(column);
282 return (isSortable == null) ? true : isSortable[column];
283 }
284
285 /**
286 * Sets the sort keys. This creates a copy of the supplied
287 * {@code List}; subsequent changes to the supplied
288 * {@code List} do not effect this {@code DefaultRowSorter}.
289 * If the sort keys have changed this triggers a sort.
290 *
291 * @param sortKeys the new {@code SortKeys}; {@code null}
292 * is a shorthand for specifying an empty list,
293 * indicating that the view should be unsorted
294 * @throws IllegalArgumentException if any of the values in
295 * {@code sortKeys} are null or have a column index outside
296 * the range of the model
297 */
298 public void setSortKeys(List<? extends SortKey> sortKeys) {
299 List<SortKey> old = this.sortKeys;
300 if (sortKeys != null && sortKeys.size() > 0) {
301 int max = getModelWrapper().getColumnCount();
302 for (SortKey key : sortKeys) {
303 if (key == null || key.getColumn() < 0 ||
304 key.getColumn() >= max) {
305 throw new IllegalArgumentException("Invalid SortKey");
306 }
307 }
308 this.sortKeys = Collections.unmodifiableList(
309 new ArrayList<SortKey>(sortKeys));
310 }
311 else {
312 this.sortKeys = Collections.emptyList();
313 }
314 if (!this.sortKeys.equals(old)) {
315 fireSortOrderChanged();
322 }
323 }
324 }
325
326 /**
327 * Returns the current sort keys. This returns an unmodifiable
328 * {@code non-null List}. If you need to change the sort keys,
329 * make a copy of the returned {@code List}, mutate the copy
330 * and invoke {@code setSortKeys} with the new list.
331 *
332 * @return the current sort order
333 */
334 public List<? extends SortKey> getSortKeys() {
335 return sortKeys;
336 }
337
338 /**
339 * Sets the maximum number of sort keys. The number of sort keys
340 * determines how equal values are resolved when sorting. For
341 * example, assume a table row sorter is created and
342 * {@code setMaxSortKeys(2)} is invoked on it. The user
343 * clicks the header for column 1, causing the table rows to be
344 * sorted based on the items in column 1. Next, the user clicks
345 * the header for column 2, causing the table to be sorted based
346 * on the items in column 2; if any items in column 2 are equal,
347 * then those particular rows are ordered based on the items in
348 * column 1. In this case, we say that the rows are primarily
349 * sorted on column 2, and secondarily on column 1. If the user
350 * then clicks the header for column 3, then the items are
351 * primarily sorted on column 3 and secondarily sorted on column
352 * 2. Because the maximum number of sort keys has been set to 2
353 * with {@code setMaxSortKeys}, column 1 no longer has an
354 * effect on the order.
355 * <p>
356 * The maximum number of sort keys is enforced by
357 * {@code toggleSortOrder}. You can specify more sort
358 * keys by invoking {@code setSortKeys} directly and they will
359 * all be honored. However if {@code toggleSortOrder} is subsequently
360 * invoked the maximum number of sort keys will be enforced.
361 * The default value is 3.
362 *
363 * @param max the maximum number of sort keys
364 * @throws IllegalArgumentException if {@code max < 1}
365 */
366 public void setMaxSortKeys(int max) {
367 if (max < 1) {
368 throw new IllegalArgumentException("Invalid max");
369 }
370 maxSortKeys = max;
371 }
372
373 /**
374 * Returns the maximum number of sort keys.
375 *
376 * @return the maximum number of sort keys
377 */
378 public int getMaxSortKeys() {
379 return maxSortKeys;
380 }
381
382 /**
383 * If true, specifies that a sort should happen when the underlying
384 * model is updated ({@code rowsUpdated} is invoked). For
385 * example, if this is true and the user edits an entry the
386 * location of that item in the view may change. The default is
387 * false.
388 *
389 * @param sortsOnUpdates whether or not to sort on update events
390 */
391 public void setSortsOnUpdates(boolean sortsOnUpdates) {
392 this.sortsOnUpdates = sortsOnUpdates;
393 }
394
395 /**
396 * Returns true if a sort should happen when the underlying
397 * model is updated; otherwise, returns false.
398 *
399 * @return whether or not to sort when the model is updated
400 */
401 public boolean getSortsOnUpdates() {
402 return sortsOnUpdates;
403 }
404
405 /**
406 * Sets the filter that determines which rows, if any, should be
407 * hidden from the view. The filter is applied before sorting. A value
408 * of {@code null} indicates all values from the model should be
409 * included.
410 * <p>
411 * {@code RowFilter}'s {@code include} method is passed an
412 * {@code Entry} that wraps the underlying model. The number
413 * of columns in the {@code Entry} corresponds to the
414 * number of columns in the {@code ModelWrapper}. The identifier
415 * comes from the {@code ModelWrapper} as well.
416 * <p>
417 * This method triggers a sort.
418 *
419 * @param filter the filter used to determine what entries should be
420 * included
421 */
422 public void setRowFilter(RowFilter<? super M,? super I> filter) {
423 this.filter = filter;
424 sort();
425 }
426
427 /**
428 * Returns the filter that determines which rows, if any, should
429 * be hidden from view.
430 *
431 * @return the filter
432 */
433 public RowFilter<? super M,? super I> getRowFilter() {
434 return filter;
435 }
544 for (int i = 0; i < modelToView.length; i++) {
545 if (modelToView[i] != -1) {
546 viewToModel[included].modelIndex = i;
547 modelToView[i] = included++;
548 }
549 }
550 }
551 } else {
552 // sort the data
553 Arrays.sort(viewToModel);
554
555 // Update the modelToView array
556 setModelToViewFromViewToModel(false);
557 }
558 fireRowSorterChanged(lastViewToModel);
559 }
560
561 /**
562 * Sorts and filters the rows in the view based on the sort keys
563 * of the columns currently being sorted and the filter, if any,
564 * associated with this sorter. An empty {@code sortKeys} list
565 * indicates that the view should unsorted, the same as the model.
566 *
567 * @see #setRowFilter
568 * @see #setSortKeys
569 */
570 public void sort() {
571 sorted = true;
572 int[] lastViewToModel = getViewToModelAsInts(viewToModel);
573 updateUseToString();
574 if (isUnsorted()) {
575 // Unsorted
576 cachedSortKeys = new SortKey[0];
577 if (getRowFilter() == null) {
578 // No filter & unsorted
579 if (viewToModel != null) {
580 // sorted -> unsorted
581 viewToModel = null;
582 modelToView = null;
583 }
584 else {
688 for (i = recreateFrom; i < rowCount; i++) {
689 viewToModel[i] = new Row(this, i);
690 }
691 }
692
693 /**
694 * Caches the sort keys before a sort.
695 */
696 private void cacheSortKeys(List<? extends SortKey> keys) {
697 int keySize = keys.size();
698 sortComparators = new Comparator<?>[keySize];
699 for (int i = 0; i < keySize; i++) {
700 sortComparators[i] = getComparator0(keys.get(i).getColumn());
701 }
702 cachedSortKeys = keys.toArray(new SortKey[keySize]);
703 }
704
705 /**
706 * Returns whether or not to convert the value to a string before
707 * doing comparisons when sorting. If true
708 * {@code ModelWrapper.getStringValueAt} will be used, otherwise
709 * {@code ModelWrapper.getValueAt} will be used. It is up to
710 * subclasses, such as {@code TableRowSorter}, to honor this value
711 * in their {@code ModelWrapper} implementation.
712 *
713 * @param column the index of the column to test, in terms of the
714 * underlying model
715 * @return true if values are to be converted to strings before doing
716 * comparisons when sorting
717 * @throws IndexOutOfBoundsException if {@code column} is not valid
718 */
719 protected boolean useToString(int column) {
720 return (getComparator(column) == null);
721 }
722
723 /**
724 * Refreshes the modelToView mapping from that of viewToModel.
725 * If {@code unsetFirst} is true, all indices in modelToView are
726 * first set to -1.
727 */
728 private void setModelToViewFromViewToModel(boolean unsetFirst) {
729 int i;
730 if (unsetFirst) {
731 for (i = modelToView.length - 1; i >= 0; i--) {
732 modelToView[i] = -1;
733 }
734 }
735 for (i = viewToModel.length - 1; i >= 0; i--) {
736 modelToView[viewToModel[i].modelIndex] = i;
737 }
738 }
739
740 private int[] getViewToModelAsInts(Row[] viewToModel) {
741 if (viewToModel != null) {
742 int[] viewToModelI = new int[viewToModel.length];
743 for (int i = viewToModel.length - 1; i >= 0; i--) {
744 viewToModelI[i] = viewToModel[i].modelIndex;
745 }
746 return viewToModelI;
747 }
748 return new int[0];
749 }
750
751 /**
752 * Sets the {@code Comparator} to use when sorting the specified
753 * column. This does not trigger a sort. If you want to sort after
754 * setting the comparator you need to explicitly invoke {@code sort}.
755 *
756 * @param column the index of the column the {@code Comparator} is
757 * to be used for, in terms of the underlying model
758 * @param comparator the {@code Comparator} to use
759 * @throws IndexOutOfBoundsException if {@code column} is outside
760 * the range of the underlying model
761 */
762 public void setComparator(int column, Comparator<?> comparator) {
763 checkColumn(column);
764 if (comparators == null) {
765 comparators = new Comparator<?>[getModelWrapper().getColumnCount()];
766 }
767 comparators[column] = comparator;
768 }
769
770 /**
771 * Returns the {@code Comparator} for the specified
772 * column. This will return {@code null} if a {@code Comparator}
773 * has not been specified for the column.
774 *
775 * @param column the column to fetch the {@code Comparator} for, in
776 * terms of the underlying model
777 * @return the {@code Comparator} for the specified column
778 * @throws IndexOutOfBoundsException if column is outside
779 * the range of the underlying model
780 */
781 public Comparator<?> getComparator(int column) {
782 checkColumn(column);
783 if (comparators != null) {
784 return comparators[column];
785 }
786 return null;
787 }
788
789 // Returns the Comparator to use during sorting. Where as
790 // getComparator() may return null, this will never return null.
791 private Comparator<?> getComparator0(int column) {
792 Comparator<?> comparator = getComparator(column);
793 if (comparator != null) {
794 return comparator;
795 }
796 // This should be ok as useToString(column) should have returned
797 // true in this case.
999 private void insertInOrder(List<Row> toAdd, Row[] current) {
1000 int last = 0;
1001 int index;
1002 int max = toAdd.size();
1003 for (int i = 0; i < max; i++) {
1004 index = Arrays.binarySearch(current, toAdd.get(i));
1005 if (index < 0) {
1006 index = -1 - index;
1007 }
1008 System.arraycopy(current, last,
1009 viewToModel, last + i, index - last);
1010 viewToModel[index + i] = toAdd.get(i);
1011 last = index;
1012 }
1013 System.arraycopy(current, last, viewToModel, last + max,
1014 current.length - last);
1015 }
1016
1017 /**
1018 * Returns true if we should try and optimize the processing of the
1019 * {@code TableModelEvent}. If this returns false, assume the
1020 * event was dealt with and no further processing needs to happen.
1021 */
1022 private boolean shouldOptimizeChange(int firstRow, int lastRow) {
1023 if (!isTransformed()) {
1024 // Not transformed, nothing to do.
1025 return false;
1026 }
1027 if (!sorted || (lastRow - firstRow) > viewToModel.length / 10) {
1028 // We either weren't sorted, or to much changed, sort it all
1029 sort();
1030 return false;
1031 }
1032 return true;
1033 }
1034
1035 private void rowsInserted0(int firstRow, int lastRow) {
1036 int[] oldViewToModel = getViewToModelAsInts(viewToModel);
1037 int i;
1038 int delta = (lastRow - firstRow) + 1;
1039 List<Row> added = new ArrayList<Row>(delta);
1210
1211 // Rebuild the new viewToModel array
1212 insertInOrder(updated, intermediary);
1213
1214 // Update modelToView
1215 setModelToViewFromViewToModel(true);
1216 }
1217 // And finally fire a sort event.
1218 fireRowSorterChanged(oldViewToModel);
1219 }
1220
1221 private void checkColumn(int column) {
1222 if (column < 0 || column >= getModelWrapper().getColumnCount()) {
1223 throw new IndexOutOfBoundsException(
1224 "column beyond range of TableModel");
1225 }
1226 }
1227
1228
1229 /**
1230 * {@code DefaultRowSorter.ModelWrapper} is responsible for providing
1231 * the data that gets sorted by {@code DefaultRowSorter}. You
1232 * normally do not interact directly with {@code ModelWrapper}.
1233 * Subclasses of {@code DefaultRowSorter} provide an
1234 * implementation of {@code ModelWrapper} wrapping another model.
1235 * For example,
1236 * {@code TableRowSorter} provides a {@code ModelWrapper} that
1237 * wraps a {@code TableModel}.
1238 * <p>
1239 * {@code ModelWrapper} makes a distinction between values as
1240 * {@code Object}s and {@code String}s. This allows
1241 * implementations to provide a custom string
1242 * converter to be used instead of invoking {@code toString} on the
1243 * object.
1244 *
1245 * @param <M> the type of the underlying model
1246 * @param <I> the identifier supplied to the filter
1247 * @since 1.6
1248 * @see RowFilter
1249 * @see RowFilter.Entry
1250 */
1251 protected abstract static class ModelWrapper<M,I> {
1252 /**
1253 * Creates a new {@code ModelWrapper}.
1254 */
1255 protected ModelWrapper() {
1256 }
1257
1258 /**
1259 * Returns the underlying model that this {@code Model} is
1260 * wrapping.
1261 *
1262 * @return the underlying model
1263 */
1264 public abstract M getModel();
1265
1266 /**
1267 * Returns the number of columns in the model.
1268 *
1269 * @return the number of columns in the model
1270 */
1271 public abstract int getColumnCount();
1272
1273 /**
1274 * Returns the number of rows in the model.
1275 *
1276 * @return the number of rows in the model
1277 */
1278 public abstract int getRowCount();
1279
1280 /**
1281 * Returns the value at the specified index.
1282 *
1283 * @param row the row index
1284 * @param column the column index
1285 * @return the value at the specified index
1286 * @throws IndexOutOfBoundsException if the indices are outside
1287 * the range of the model
1288 */
1289 public abstract Object getValueAt(int row, int column);
1290
1291 /**
1292 * Returns the value as a {@code String} at the specified
1293 * index. This implementation uses {@code toString} on
1294 * the result from {@code getValueAt} (making sure
1295 * to return an empty string for null values). Subclasses that
1296 * override this method should never return null.
1297 *
1298 * @param row the row index
1299 * @param column the column index
1300 * @return the value at the specified index as a {@code String}
1301 * @throws IndexOutOfBoundsException if the indices are outside
1302 * the range of the model
1303 */
1304 public String getStringValueAt(int row, int column) {
1305 Object o = getValueAt(row, column);
1306 if (o == null) {
1307 return "";
1308 }
1309 String string = o.toString();
1310 if (string == null) {
1311 return "";
1312 }
1313 return string;
1314 }
1315
1316 /**
1317 * Returns the identifier for the specified row. The return value
1318 * of this is used as the identifier for the
1319 * {@code RowFilter.Entry} that is passed to the
1320 * {@code RowFilter}.
1321 *
1322 * @param row the row to return the identifier for, in terms of
1323 * the underlying model
1324 * @return the identifier
1325 * @see RowFilter.Entry#getIdentifier
1326 */
1327 public abstract I getIdentifier(int row);
1328 }
1329
1330
1331 /**
1332 * RowFilter.Entry implementation that delegates to the ModelWrapper.
1333 * getFilterEntry(int) creates the single instance of this that is
1334 * passed to the Filter. Only call getFilterEntry(int) to get
1335 * the instance.
1336 */
1337 private class FilterEntry extends RowFilter.Entry<M,I> {
1338 /**
1339 * The index into the model, set in getFilterEntry
1340 */
|