< prev index next >

src/java.desktop/share/classes/javax/swing/DefaultRowSorter.java

Print this page




  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> &lt; 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          */


< prev index next >