1 /*
   2  * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javax.swing;
  26 
  27 import 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 
  34 /**
  35  * An implementation of <code>RowSorter</code> that provides sorting and
  36  * filtering around a grid-based data model.
  37  * Beyond creating and installing a <code>RowSorter</code>, you very rarely
  38  * need to interact with one directly.  Refer to
  39  * {@link javax.swing.table.TableRowSorter TableRowSorter} for a concrete
  40  * implementation of <code>RowSorter</code> for <code>JTable</code>.
  41  * <p>
  42  * Sorting is done based on the current <code>SortKey</code>s, in order.
  43  * If two objects are equal (the <code>Comparator</code> for the
  44  * column returns 0) the next <code>SortKey</code> is used.  If no
  45  * <code>SortKey</code>s remain or the order is <code>UNSORTED</code>, then
  46  * the order of the rows in the model is used.
  47  * <p>
  48  * Sorting of each column is done by way of a <code>Comparator</code>
  49  * that you can specify using the <code>setComparator</code> method.
  50  * If a <code>Comparator</code> has not been specified, the
  51  * <code>Comparator</code> returned by
  52  * <code>Collator.getInstance()</code> is used on the results of
  53  * calling <code>toString</code> on the underlying objects.  The
  54  * <code>Comparator</code> is never passed <code>null</code>.  A
  55  * <code>null</code> value is treated as occurring before a
  56  * non-<code>null</code> value, and two <code>null</code> values are
  57  * considered equal.
  58  * <p>
  59  * If you specify a <code>Comparator</code> that casts its argument to
  60  * a type other than that provided by the model, a
  61  * <code>ClassCastException</code> will be thrown when the data is sorted.
  62  * <p>
  63  * In addition to sorting, <code>DefaultRowSorter</code> provides the
  64  * ability to filter rows.  Filtering is done by way of a
  65  * <code>RowFilter</code> that is specified using the
  66  * <code>setRowFilter</code> method.  If no filter has been specified all
  67  * rows are included.
  68  * <p>
  69  * By default, rows are in unsorted order (the same as the model) and
  70  * every column is sortable. The default <code>Comparator</code>s are
  71  * documented in the subclasses (for example, {@link
  72  * javax.swing.table.TableRowSorter TableRowSorter}).
  73  * <p>
  74  * If the underlying model structure changes (the
  75  * <code>modelStructureChanged</code> method is invoked) the following
  76  * are reset to their default values: <code>Comparator</code>s by
  77  * column, current sort order, and whether each column is sortable. To
  78  * find the default <code>Comparator</code>s, see the concrete
  79  * implementation (for example, {@link
  80  * javax.swing.table.TableRowSorter TableRowSorter}).  The default
  81  * sort order is unsorted (the same as the model), and columns are
  82  * sortable by default.
  83  * <p>
  84  * If the underlying model structure changes (the
  85  * <code>modelStructureChanged</code> method is invoked) the following
  86  * are reset to their default values: <code>Comparator</code>s by column,
  87  * current sort order and whether a column is sortable.
  88  * <p>
  89  * <code>DefaultRowSorter</code> is an abstract class.  Concrete
  90  * subclasses must provide access to the underlying data by invoking
  91  * {@code setModelWrapper}. The {@code setModelWrapper} method
  92  * <b>must</b> be invoked soon after the constructor is
  93  * called, ideally from within the subclass's constructor.
  94  * Undefined behavior will result if you use a {@code
  95  * DefaultRowSorter} without specifying a {@code ModelWrapper}.
  96  * <p>
  97  * <code>DefaultRowSorter</code> has two formal type parameters.  The
  98  * first type parameter corresponds to the class of the model, for example
  99  * <code>DefaultTableModel</code>.  The second type parameter
 100  * corresponds to the class of the identifier passed to the
 101  * <code>RowFilter</code>.  Refer to <code>TableRowSorter</code> and
 102  * <code>RowFilter</code> for more details on the type parameters.
 103  *
 104  * @param <M> the type of the model
 105  * @param <I> the type of the identifier passed to the <code>RowFilter</code>
 106  * @see javax.swing.table.TableRowSorter
 107  * @see javax.swing.table.DefaultTableModel
 108  * @see java.text.Collator
 109  * @since 1.6
 110  */
 111 public abstract class DefaultRowSorter<M, I> extends RowSorter<M> {
 112     /**
 113      * Whether or not we resort on TableModelEvent.UPDATEs.
 114      */
 115     private boolean sortsOnUpdates;
 116 
 117     /**
 118      * View (JTable) -> model.
 119      */
 120     private Row[] viewToModel;
 121 
 122     /**
 123      * model -> view (JTable)
 124      */
 125     private int[] modelToView;
 126 
 127     /**
 128      * Comparators specified by column.
 129      */
 130     private Comparator<?>[] comparators;
 131 
 132     /**
 133      * Whether or not the specified column is sortable, by column.
 134      */
 135     private boolean[] isSortable;
 136 
 137     /**
 138      * Cached SortKeys for the current sort.
 139      */
 140     private SortKey[] cachedSortKeys;
 141 
 142     /**
 143      * Cached comparators for the current sort
 144      */
 145     private Comparator<?>[] sortComparators;
 146 
 147     /**
 148      * Developer supplied Filter.
 149      */
 150     private RowFilter<? super M,? super I> filter;
 151 
 152     /**
 153      * Value passed to the filter.  The same instance is passed to the
 154      * filter for different rows.
 155      */
 156     private FilterEntry filterEntry;
 157 
 158     /**
 159      * The sort keys.
 160      */
 161     private List<SortKey> sortKeys;
 162 
 163     /**
 164      * Whether or not to use getStringValueAt.  This is indexed by column.
 165      */
 166     private boolean[] useToString;
 167 
 168     /**
 169      * Indicates the contents are sorted.  This is used if
 170      * getSortsOnUpdates is false and an update event is received.
 171      */
 172     private boolean sorted;
 173 
 174     /**
 175      * Maximum number of sort keys.
 176      */
 177     private int maxSortKeys;
 178 
 179     /**
 180      * Provides access to the data we're sorting/filtering.
 181      */
 182     private ModelWrapper<M,I> modelWrapper;
 183 
 184     /**
 185      * Size of the model. This is used to enforce error checking within
 186      * the table changed notification methods (such as rowsInserted).
 187      */
 188     private int modelRowCount;
 189 
 190 
 191     /**
 192      * Creates an empty <code>DefaultRowSorter</code>.
 193      */
 194     public DefaultRowSorter() {
 195         sortKeys = Collections.emptyList();
 196         maxSortKeys = 3;
 197     }
 198 
 199     /**
 200      * Sets the model wrapper providing the data that is being sorted and
 201      * filtered.
 202      *
 203      * @param modelWrapper the model wrapper responsible for providing the
 204      *         data that gets sorted and filtered
 205      * @throws IllegalArgumentException if {@code modelWrapper} is
 206      *         {@code null}
 207      */
 208     protected final void setModelWrapper(ModelWrapper<M,I> modelWrapper) {
 209         if (modelWrapper == null) {
 210             throw new IllegalArgumentException(
 211                 "modelWrapper most be non-null");
 212         }
 213         ModelWrapper<M,I> last = this.modelWrapper;
 214         this.modelWrapper = modelWrapper;
 215         if (last != null) {
 216             modelStructureChanged();
 217         } else {
 218             // If last is null, we're in the constructor. If we're in
 219             // the constructor we don't want to call to overridable methods.
 220             modelRowCount = getModelWrapper().getRowCount();
 221         }
 222     }
 223 
 224     /**
 225      * Returns the model wrapper providing the data that is being sorted and
 226      * filtered.
 227      *
 228      * @return the model wrapper responsible for providing the data that
 229      *         gets sorted and filtered
 230      */
 231     protected final ModelWrapper<M,I> getModelWrapper() {
 232         return modelWrapper;
 233     }
 234 
 235     /**
 236      * Returns the underlying model.
 237      *
 238      * @return the underlying model
 239      */
 240     public final M getModel() {
 241         return getModelWrapper().getModel();
 242     }
 243 
 244     /**
 245      * Sets whether or not the specified column is sortable.  The specified
 246      * value is only checked when <code>toggleSortOrder</code> is invoked.
 247      * It is still possible to sort on a column that has been marked as
 248      * unsortable by directly setting the sort keys.  The default is
 249      * true.
 250      *
 251      * @param column the column to enable or disable sorting on, in terms
 252      *        of the underlying model
 253      * @param sortable whether or not the specified column is sortable
 254      * @throws IndexOutOfBoundsException if <code>column</code> is outside
 255      *         the range of the model
 256      * @see #toggleSortOrder
 257      * @see #setSortKeys
 258      */
 259     public void setSortable(int column, boolean sortable) {
 260         checkColumn(column);
 261         if (isSortable == null) {
 262             isSortable = new boolean[getModelWrapper().getColumnCount()];
 263             for (int i = isSortable.length - 1; i >= 0; i--) {
 264                 isSortable[i] = true;
 265             }
 266         }
 267         isSortable[column] = sortable;
 268     }
 269 
 270     /**
 271      * Returns true if the specified column is sortable; otherwise, false.
 272      *
 273      * @param column the column to check sorting for, in terms of the
 274      *        underlying model
 275      * @return true if the column is sortable
 276      * @throws IndexOutOfBoundsException if column is outside
 277      *         the range of the underlying model
 278      */
 279     public boolean isSortable(int column) {
 280         checkColumn(column);
 281         return (isSortable == null) ? true : isSortable[column];
 282     }
 283 
 284     /**
 285      * Sets the sort keys. This creates a copy of the supplied
 286      * {@code List}; subsequent changes to the supplied
 287      * {@code List} do not effect this {@code DefaultRowSorter}.
 288      * If the sort keys have changed this triggers a sort.
 289      *
 290      * @param sortKeys the new <code>SortKeys</code>; <code>null</code>
 291      *        is a shorthand for specifying an empty list,
 292      *        indicating that the view should be unsorted
 293      * @throws IllegalArgumentException if any of the values in
 294      *         <code>sortKeys</code> are null or have a column index outside
 295      *         the range of the model
 296      */
 297     public void setSortKeys(List<? extends SortKey> sortKeys) {
 298         List<SortKey> old = this.sortKeys;
 299         if (sortKeys != null && sortKeys.size() > 0) {
 300             int max = getModelWrapper().getColumnCount();
 301             for (SortKey key : sortKeys) {
 302                 if (key == null || key.getColumn() < 0 ||
 303                         key.getColumn() >= max) {
 304                     throw new IllegalArgumentException("Invalid SortKey");
 305                 }
 306             }
 307             this.sortKeys = Collections.unmodifiableList(
 308                     new ArrayList<SortKey>(sortKeys));
 309         }
 310         else {
 311             this.sortKeys = Collections.emptyList();
 312         }
 313         if (!this.sortKeys.equals(old)) {
 314             fireSortOrderChanged();
 315             if (viewToModel == null) {
 316                 // Currently unsorted, use sort so that internal fields
 317                 // are correctly set.
 318                 sort();
 319             } else {
 320                 sortExistingData();
 321             }
 322         }
 323     }
 324 
 325     /**
 326      * Returns the current sort keys.  This returns an unmodifiable
 327      * {@code non-null List}. If you need to change the sort keys,
 328      * make a copy of the returned {@code List}, mutate the copy
 329      * and invoke {@code setSortKeys} with the new list.
 330      *
 331      * @return the current sort order
 332      */
 333     public List<? extends SortKey> getSortKeys() {
 334         return sortKeys;
 335     }
 336 
 337     /**
 338      * Sets the maximum number of sort keys.  The number of sort keys
 339      * determines how equal values are resolved when sorting.  For
 340      * example, assume a table row sorter is created and
 341      * <code>setMaxSortKeys(2)</code> is invoked on it. The user
 342      * clicks the header for column 1, causing the table rows to be
 343      * sorted based on the items in column 1.  Next, the user clicks
 344      * the header for column 2, causing the table to be sorted based
 345      * on the items in column 2; if any items in column 2 are equal,
 346      * then those particular rows are ordered based on the items in
 347      * column 1. In this case, we say that the rows are primarily
 348      * sorted on column 2, and secondarily on column 1.  If the user
 349      * then clicks the header for column 3, then the items are
 350      * primarily sorted on column 3 and secondarily sorted on column
 351      * 2.  Because the maximum number of sort keys has been set to 2
 352      * with <code>setMaxSortKeys</code>, column 1 no longer has an
 353      * effect on the order.
 354      * <p>
 355      * The maximum number of sort keys is enforced by
 356      * <code>toggleSortOrder</code>.  You can specify more sort
 357      * keys by invoking <code>setSortKeys</code> directly and they will
 358      * all be honored.  However if <code>toggleSortOrder</code> is subsequently
 359      * invoked the maximum number of sort keys will be enforced.
 360      * The default value is 3.
 361      *
 362      * @param max the maximum number of sort keys
 363      * @throws IllegalArgumentException if <code>max</code> &lt; 1
 364      */
 365     public void setMaxSortKeys(int max) {
 366         if (max < 1) {
 367             throw new IllegalArgumentException("Invalid max");
 368         }
 369         maxSortKeys = max;
 370     }
 371 
 372     /**
 373      * Returns the maximum number of sort keys.
 374      *
 375      * @return the maximum number of sort keys
 376      */
 377     public int getMaxSortKeys() {
 378         return maxSortKeys;
 379     }
 380 
 381     /**
 382      * If true, specifies that a sort should happen when the underlying
 383      * model is updated (<code>rowsUpdated</code> is invoked).  For
 384      * example, if this is true and the user edits an entry the
 385      * location of that item in the view may change.  The default is
 386      * false.
 387      *
 388      * @param sortsOnUpdates whether or not to sort on update events
 389      */
 390     public void setSortsOnUpdates(boolean sortsOnUpdates) {
 391         this.sortsOnUpdates = sortsOnUpdates;
 392     }
 393 
 394     /**
 395      * Returns true if  a sort should happen when the underlying
 396      * model is updated; otherwise, returns false.
 397      *
 398      * @return whether or not to sort when the model is updated
 399      */
 400     public boolean getSortsOnUpdates() {
 401         return sortsOnUpdates;
 402     }
 403 
 404     /**
 405      * Sets the filter that determines which rows, if any, should be
 406      * hidden from the view.  The filter is applied before sorting.  A value
 407      * of <code>null</code> indicates all values from the model should be
 408      * included.
 409      * <p>
 410      * <code>RowFilter</code>'s <code>include</code> method is passed an
 411      * <code>Entry</code> that wraps the underlying model.  The number
 412      * of columns in the <code>Entry</code> corresponds to the
 413      * number of columns in the <code>ModelWrapper</code>.  The identifier
 414      * comes from the <code>ModelWrapper</code> as well.
 415      * <p>
 416      * This method triggers a sort.
 417      *
 418      * @param filter the filter used to determine what entries should be
 419      *        included
 420      */
 421     public void setRowFilter(RowFilter<? super M,? super I> filter) {
 422         this.filter = filter;
 423         sort();
 424     }
 425 
 426     /**
 427      * Returns the filter that determines which rows, if any, should
 428      * be hidden from view.
 429      *
 430      * @return the filter
 431      */
 432     public RowFilter<? super M,? super I> getRowFilter() {
 433         return filter;
 434     }
 435 
 436     /**
 437      * Reverses the sort order from ascending to descending (or
 438      * descending to ascending) if the specified column is already the
 439      * primary sorted column; otherwise, makes the specified column
 440      * the primary sorted column, with an ascending sort order.  If
 441      * the specified column is not sortable, this method has no
 442      * effect.
 443      *
 444      * @param column index of the column to make the primary sorted column,
 445      *        in terms of the underlying model
 446      * @throws IndexOutOfBoundsException {@inheritDoc}
 447      * @see #setSortable(int,boolean)
 448      * @see #setMaxSortKeys(int)
 449      */
 450     public void toggleSortOrder(int column) {
 451         checkColumn(column);
 452         if (isSortable(column)) {
 453             List<SortKey> keys = new ArrayList<SortKey>(getSortKeys());
 454             SortKey sortKey;
 455             int sortIndex;
 456             for (sortIndex = keys.size() - 1; sortIndex >= 0; sortIndex--) {
 457                 if (keys.get(sortIndex).getColumn() == column) {
 458                     break;
 459                 }
 460             }
 461             if (sortIndex == -1) {
 462                 // Key doesn't exist
 463                 sortKey = new SortKey(column, SortOrder.ASCENDING);
 464                 keys.add(0, sortKey);
 465             }
 466             else if (sortIndex == 0) {
 467                 // It's the primary sorting key, toggle it
 468                 keys.set(0, toggle(keys.get(0)));
 469             }
 470             else {
 471                 // It's not the first, but was sorted on, remove old
 472                 // entry, insert as first with ascending.
 473                 keys.remove(sortIndex);
 474                 keys.add(0, new SortKey(column, SortOrder.ASCENDING));
 475             }
 476             if (keys.size() > getMaxSortKeys()) {
 477                 keys = keys.subList(0, getMaxSortKeys());
 478             }
 479             setSortKeys(keys);
 480         }
 481     }
 482 
 483     private SortKey toggle(SortKey key) {
 484         if (key.getSortOrder() == SortOrder.ASCENDING) {
 485             return new SortKey(key.getColumn(), SortOrder.DESCENDING);
 486         }
 487         return new SortKey(key.getColumn(), SortOrder.ASCENDING);
 488     }
 489 
 490     /**
 491      * {@inheritDoc}
 492      *
 493      * @throws IndexOutOfBoundsException {@inheritDoc}
 494      */
 495     public int convertRowIndexToView(int index) {
 496         if (modelToView == null) {
 497             if (index < 0 || index >= modelRowCount) {
 498                 throw new IndexOutOfBoundsException("Invalid index");
 499             }
 500             return index;
 501         }
 502         return modelToView[index];
 503     }
 504 
 505     /**
 506      * {@inheritDoc}
 507      *
 508      * @throws IndexOutOfBoundsException {@inheritDoc}
 509      */
 510     public int convertRowIndexToModel(int index) {
 511         if (viewToModel == null) {
 512             if (index < 0 || index >= modelRowCount) {
 513                 throw new IndexOutOfBoundsException("Invalid index"); 
 514             }
 515             return index;
 516         }
 517         return viewToModel[index].modelIndex;
 518     }
 519 
 520     private boolean isUnsorted() {
 521         List<? extends SortKey> keys = getSortKeys();
 522         int keySize = keys.size();
 523         return (keySize == 0 || keys.get(0).getSortOrder() ==
 524                 SortOrder.UNSORTED);
 525     }
 526 
 527     /**
 528      * Sorts the existing filtered data.  This should only be used if
 529      * the filter hasn't changed.
 530      */
 531     private void sortExistingData() {
 532         int[] lastViewToModel = getViewToModelAsInts(viewToModel);
 533 
 534         updateUseToString();
 535         cacheSortKeys(getSortKeys());
 536 
 537         if (isUnsorted()) {
 538             if (getRowFilter() == null) {
 539                 viewToModel = null;
 540                 modelToView = null;
 541             } else {
 542                 int included = 0;
 543                 for (int i = 0; i < modelToView.length; i++) {
 544                     if (modelToView[i] != -1) {
 545                         viewToModel[included].modelIndex = i;
 546                         modelToView[i] = included++;
 547                     }
 548                 }
 549             }
 550         } else {
 551             // sort the data
 552             Arrays.sort(viewToModel);
 553 
 554             // Update the modelToView array
 555             setModelToViewFromViewToModel(false);
 556         }
 557         fireRowSorterChanged(lastViewToModel);
 558     }
 559 
 560     /**
 561      * Sorts and filters the rows in the view based on the sort keys
 562      * of the columns currently being sorted and the filter, if any,
 563      * associated with this sorter.  An empty <code>sortKeys</code> list
 564      * indicates that the view should unsorted, the same as the model.
 565      *
 566      * @see #setRowFilter
 567      * @see #setSortKeys
 568      */
 569     public void sort() {
 570         sorted = true;
 571         int[] lastViewToModel = getViewToModelAsInts(viewToModel);
 572         updateUseToString();
 573         if (isUnsorted()) {
 574             // Unsorted
 575             cachedSortKeys = new SortKey[0];
 576             if (getRowFilter() == null) {
 577                 // No filter & unsorted
 578                 if (viewToModel != null) {
 579                     // sorted -> unsorted
 580                     viewToModel = null;
 581                     modelToView = null;
 582                 }
 583                 else {
 584                     // unsorted -> unsorted
 585                     // No need to do anything.
 586                     return;
 587                 }
 588             }
 589             else {
 590                 // There is filter, reset mappings
 591                 initializeFilteredMapping();
 592             }
 593         }
 594         else {
 595             cacheSortKeys(getSortKeys());
 596 
 597             if (getRowFilter() != null) {
 598                 initializeFilteredMapping();
 599             }
 600             else {
 601                 createModelToView(getModelWrapper().getRowCount());
 602                 createViewToModel(getModelWrapper().getRowCount());
 603             }
 604 
 605             // sort them
 606             Arrays.sort(viewToModel);
 607 
 608             // Update the modelToView array
 609             setModelToViewFromViewToModel(false);
 610         }
 611         fireRowSorterChanged(lastViewToModel);
 612     }
 613 
 614     /**
 615      * Updates the useToString mapping before a sort.
 616      */
 617     private void updateUseToString() {
 618         int i = getModelWrapper().getColumnCount();
 619         if (useToString == null || useToString.length != i) {
 620             useToString = new boolean[i];
 621         }
 622         for (--i; i >= 0; i--) {
 623             useToString[i] = useToString(i);
 624         }
 625     }
 626 
 627     /**
 628      * Resets the viewToModel and modelToView mappings based on
 629      * the current Filter.
 630      */
 631     private void initializeFilteredMapping() {
 632         int rowCount = getModelWrapper().getRowCount();
 633         int i, j;
 634         int excludedCount = 0;
 635 
 636         // Update model -> view
 637         createModelToView(rowCount);
 638         for (i = 0; i < rowCount; i++) {
 639             if (include(i)) {
 640                 modelToView[i] = i - excludedCount;
 641             }
 642             else {
 643                 modelToView[i] = -1;
 644                 excludedCount++;
 645             }
 646         }
 647 
 648         // Update view -> model
 649         createViewToModel(rowCount - excludedCount);
 650         for (i = 0, j = 0; i < rowCount; i++) {
 651             if (modelToView[i] != -1) {
 652                 viewToModel[j++].modelIndex = i;
 653             }
 654         }
 655     }
 656 
 657     /**
 658      * Makes sure the modelToView array is of size rowCount.
 659      */
 660     private void createModelToView(int rowCount) {
 661         if (modelToView == null || modelToView.length != rowCount) {
 662             modelToView = new int[rowCount];
 663         }
 664     }
 665 
 666     /**
 667      * Resets the viewToModel array to be of size rowCount.
 668      */
 669     private void createViewToModel(int rowCount) {
 670         int recreateFrom = 0;
 671         if (viewToModel != null) {
 672             recreateFrom = Math.min(rowCount, viewToModel.length);
 673             if (viewToModel.length != rowCount) {
 674                 Row[] oldViewToModel = viewToModel;
 675                 viewToModel = new Row[rowCount];
 676                 System.arraycopy(oldViewToModel, 0, viewToModel,
 677                                  0, recreateFrom);
 678             }
 679         }
 680         else {
 681             viewToModel = new Row[rowCount];
 682         }
 683         int i;
 684         for (i = 0; i < recreateFrom; i++) {
 685             viewToModel[i].modelIndex = i;
 686         }
 687         for (i = recreateFrom; i < rowCount; i++) {
 688             viewToModel[i] = new Row(this, i);
 689         }
 690     }
 691 
 692     /**
 693      * Caches the sort keys before a sort.
 694      */
 695     private void cacheSortKeys(List<? extends SortKey> keys) {
 696         int keySize = keys.size();
 697         sortComparators = new Comparator<?>[keySize];
 698         for (int i = 0; i < keySize; i++) {
 699             sortComparators[i] = getComparator0(keys.get(i).getColumn());
 700         }
 701         cachedSortKeys = keys.toArray(new SortKey[keySize]);
 702     }
 703 
 704     /**
 705      * Returns whether or not to convert the value to a string before
 706      * doing comparisons when sorting.  If true
 707      * <code>ModelWrapper.getStringValueAt</code> will be used, otherwise
 708      * <code>ModelWrapper.getValueAt</code> will be used.  It is up to
 709      * subclasses, such as <code>TableRowSorter</code>, to honor this value
 710      * in their <code>ModelWrapper</code> implementation.
 711      *
 712      * @param column the index of the column to test, in terms of the
 713      *        underlying model
 714      * @return true if values are to be converted to strings before doing
 715      *              comparisons when sorting
 716      * @throws IndexOutOfBoundsException if <code>column</code> is not valid
 717      */
 718     protected boolean useToString(int column) {
 719         return (getComparator(column) == null);
 720     }
 721 
 722     /**
 723      * Refreshes the modelToView mapping from that of viewToModel.
 724      * If <code>unsetFirst</code> is true, all indices in modelToView are
 725      * first set to -1.
 726      */
 727     private void setModelToViewFromViewToModel(boolean unsetFirst) {
 728         int i;
 729         if (unsetFirst) {
 730             for (i = modelToView.length - 1; i >= 0; i--) {
 731                 modelToView[i] = -1;
 732             }
 733         }
 734         for (i = viewToModel.length - 1; i >= 0; i--) {
 735             modelToView[viewToModel[i].modelIndex] = i;
 736         }
 737     }
 738 
 739     private int[] getViewToModelAsInts(Row[] viewToModel) {
 740         if (viewToModel != null) {
 741             int[] viewToModelI = new int[viewToModel.length];
 742             for (int i = viewToModel.length - 1; i >= 0; i--) {
 743                 viewToModelI[i] = viewToModel[i].modelIndex;
 744             }
 745             return viewToModelI;
 746         }
 747         return new int[0];
 748     }
 749 
 750     /**
 751      * Sets the <code>Comparator</code> to use when sorting the specified
 752      * column.  This does not trigger a sort.  If you want to sort after
 753      * setting the comparator you need to explicitly invoke <code>sort</code>.
 754      *
 755      * @param column the index of the column the <code>Comparator</code> is
 756      *        to be used for, in terms of the underlying model
 757      * @param comparator the <code>Comparator</code> to use
 758      * @throws IndexOutOfBoundsException if <code>column</code> is outside
 759      *         the range of the underlying model
 760      */
 761     public void setComparator(int column, Comparator<?> comparator) {
 762         checkColumn(column);
 763         if (comparators == null) {
 764             comparators = new Comparator<?>[getModelWrapper().getColumnCount()];
 765         }
 766         comparators[column] = comparator;
 767     }
 768 
 769     /**
 770      * Returns the <code>Comparator</code> for the specified
 771      * column.  This will return <code>null</code> if a <code>Comparator</code>
 772      * has not been specified for the column.
 773      *
 774      * @param column the column to fetch the <code>Comparator</code> for, in
 775      *        terms of the underlying model
 776      * @return the <code>Comparator</code> for the specified column
 777      * @throws IndexOutOfBoundsException if column is outside
 778      *         the range of the underlying model
 779      */
 780     public Comparator<?> getComparator(int column) {
 781         checkColumn(column);
 782         if (comparators != null) {
 783             return comparators[column];
 784         }
 785         return null;
 786     }
 787 
 788     // Returns the Comparator to use during sorting.  Where as
 789     // getComparator() may return null, this will never return null.
 790     private Comparator<?> getComparator0(int column) {
 791         Comparator<?> comparator = getComparator(column);
 792         if (comparator != null) {
 793             return comparator;
 794         }
 795         // This should be ok as useToString(column) should have returned
 796         // true in this case.
 797         return Collator.getInstance();
 798     }
 799 
 800     private RowFilter.Entry<M,I> getFilterEntry(int modelIndex) {
 801         if (filterEntry == null) {
 802             filterEntry = new FilterEntry();
 803         }
 804         filterEntry.modelIndex = modelIndex;
 805         return filterEntry;
 806     }
 807 
 808     /**
 809      * {@inheritDoc}
 810      */
 811     public int getViewRowCount() {
 812         if (viewToModel != null) {
 813             // When filtering this may differ from getModelWrapper().getRowCount()
 814             return viewToModel.length;
 815         }
 816         return Math.max(getModelWrapper().getRowCount(), modelRowCount);
 817     }
 818 
 819     /**
 820      * {@inheritDoc}
 821      */
 822     public int getModelRowCount() {
 823         return getModelWrapper().getRowCount();
 824     }
 825 
 826     private void allChanged() {
 827         modelToView = null;
 828         viewToModel = null;
 829         comparators = null;
 830         isSortable = null;
 831         if (isUnsorted()) {
 832             // Keys are already empty, to force a resort we have to
 833             // call sort
 834             sort();
 835         } else {
 836             setSortKeys(null);
 837         }
 838     }
 839 
 840     /**
 841      * {@inheritDoc}
 842      */
 843     public void modelStructureChanged() {
 844         allChanged();
 845         modelRowCount = getModelWrapper().getRowCount();
 846     }
 847 
 848     /**
 849      * {@inheritDoc}
 850      */
 851     public void allRowsChanged() {
 852         modelRowCount = getModelWrapper().getRowCount();
 853         sort();
 854     }
 855 
 856     /**
 857      * {@inheritDoc}
 858      *
 859      * @throws IndexOutOfBoundsException {@inheritDoc}
 860      */
 861     public void rowsInserted(int firstRow, int endRow) {
 862         checkAgainstModel(firstRow, endRow);
 863         int newModelRowCount = getModelWrapper().getRowCount();
 864         if (endRow >= newModelRowCount) {
 865             throw new IndexOutOfBoundsException("Invalid range");
 866         }
 867         modelRowCount = newModelRowCount;
 868         if (shouldOptimizeChange(firstRow, endRow)) {
 869             rowsInserted0(firstRow, endRow);
 870         }
 871     }
 872 
 873     /**
 874      * {@inheritDoc}
 875      *
 876      * @throws IndexOutOfBoundsException {@inheritDoc}
 877      */
 878     public void rowsDeleted(int firstRow, int endRow) {
 879         checkAgainstModel(firstRow, endRow);
 880         if (firstRow >= modelRowCount || endRow >= modelRowCount) {
 881             throw new IndexOutOfBoundsException("Invalid range");
 882         }
 883         modelRowCount = getModelWrapper().getRowCount();
 884         if (shouldOptimizeChange(firstRow, endRow)) {
 885             rowsDeleted0(firstRow, endRow);
 886         }
 887     }
 888 
 889     /**
 890      * {@inheritDoc}
 891      *
 892      * @throws IndexOutOfBoundsException {@inheritDoc}
 893      */
 894     public void rowsUpdated(int firstRow, int endRow) {
 895         checkAgainstModel(firstRow, endRow);
 896         if (firstRow >= modelRowCount || endRow >= modelRowCount) {
 897             throw new IndexOutOfBoundsException("Invalid range");
 898         }
 899         if (getSortsOnUpdates()) {
 900             if (shouldOptimizeChange(firstRow, endRow)) {
 901                 rowsUpdated0(firstRow, endRow);
 902             }
 903         }
 904         else {
 905             sorted = false;
 906         }
 907     }
 908 
 909     /**
 910      * {@inheritDoc}
 911      *
 912      * @throws IndexOutOfBoundsException {@inheritDoc}
 913      */
 914     public void rowsUpdated(int firstRow, int endRow, int column) {
 915         checkColumn(column);
 916         rowsUpdated(firstRow, endRow);
 917     }
 918 
 919     private void checkAgainstModel(int firstRow, int endRow) {
 920         if (firstRow > endRow || firstRow < 0 || endRow < 0 ||
 921                 firstRow > modelRowCount) {
 922             throw new IndexOutOfBoundsException("Invalid range");
 923         }
 924     }
 925 
 926     /**
 927      * Returns true if the specified row should be included.
 928      */
 929     private boolean include(int row) {
 930         RowFilter<? super M, ? super I> filter = getRowFilter();
 931         if (filter != null) {
 932             return filter.include(getFilterEntry(row));
 933         }
 934         // null filter, always include the row.
 935         return true;
 936     }
 937 
 938     @SuppressWarnings("unchecked")
 939     private int compare(int model1, int model2) {
 940         int column;
 941         SortOrder sortOrder;
 942         Object v1, v2;
 943         int result;
 944 
 945         for (int counter = 0; counter < cachedSortKeys.length; counter++) {
 946             column = cachedSortKeys[counter].getColumn();
 947             sortOrder = cachedSortKeys[counter].getSortOrder();
 948             if (sortOrder == SortOrder.UNSORTED) {
 949                 result = model1 - model2;
 950             } else {
 951                 // v1 != null && v2 != null
 952                 if (useToString[column]) {
 953                     v1 = getModelWrapper().getStringValueAt(model1, column);
 954                     v2 = getModelWrapper().getStringValueAt(model2, column);
 955                 } else {
 956                     v1 = getModelWrapper().getValueAt(model1, column);
 957                     v2 = getModelWrapper().getValueAt(model2, column);
 958                 }
 959                 // Treat nulls as < then non-null
 960                 if (v1 == null) {
 961                     if (v2 == null) {
 962                         result = 0;
 963                     } else {
 964                         result = -1;
 965                     }
 966                 } else if (v2 == null) {
 967                     result = 1;
 968                 } else {
 969                     Comparator<Object> c =
 970                         (Comparator<Object>)sortComparators[counter];
 971                     result = c.compare(v1, v2);
 972                 }
 973                 if (sortOrder == SortOrder.DESCENDING) {
 974                     result *= -1;
 975                 }
 976             }
 977             if (result != 0) {
 978                 return result;
 979             }
 980         }
 981         // If we get here, they're equal. Fallback to model order.
 982         return model1 - model2;
 983     }
 984 
 985     /**
 986      * Whether not we are filtering/sorting.
 987      */
 988     private boolean isTransformed() {
 989         return (viewToModel != null);
 990     }
 991 
 992     /**
 993      * Insets new set of entries.
 994      *
 995      * @param toAdd the Rows to add, sorted
 996      * @param current the array to insert the items into
 997      */
 998     private void insertInOrder(List<Row> toAdd, Row[] current) {
 999         int last = 0;
1000         int index;
1001         int max = toAdd.size();
1002         for (int i = 0; i < max; i++) {
1003             index = Arrays.binarySearch(current, toAdd.get(i));
1004             if (index < 0) {
1005                 index = -1 - index;
1006             }
1007             System.arraycopy(current, last,
1008                              viewToModel, last + i, index - last);
1009             viewToModel[index + i] = toAdd.get(i);
1010             last = index;
1011         }
1012         System.arraycopy(current, last, viewToModel, last + max,
1013                          current.length - last);
1014     }
1015 
1016     /**
1017      * Returns true if we should try and optimize the processing of the
1018      * <code>TableModelEvent</code>.  If this returns false, assume the
1019      * event was dealt with and no further processing needs to happen.
1020      */
1021     private boolean shouldOptimizeChange(int firstRow, int lastRow) {
1022         if (!isTransformed()) {
1023             // Not transformed, nothing to do.
1024             return false;
1025         }
1026         if (!sorted || (lastRow - firstRow) > viewToModel.length / 10) {
1027             // We either weren't sorted, or to much changed, sort it all
1028             sort();
1029             return false;
1030         }
1031         return true;
1032     }
1033 
1034     private void rowsInserted0(int firstRow, int lastRow) {
1035         int[] oldViewToModel = getViewToModelAsInts(viewToModel);
1036         int i;
1037         int delta = (lastRow - firstRow) + 1;
1038         List<Row> added = new ArrayList<Row>(delta);
1039 
1040         // Build the list of Rows to add into added
1041         for (i = firstRow; i <= lastRow; i++) {
1042             if (include(i)) {
1043                 added.add(new Row(this, i));
1044             }
1045         }
1046 
1047         // Adjust the model index of rows after the effected region
1048         int viewIndex;
1049         for (i = modelToView.length - 1; i >= firstRow; i--) {
1050             viewIndex = modelToView[i];
1051             if (viewIndex != -1) {
1052                 viewToModel[viewIndex].modelIndex += delta;
1053             }
1054         }
1055 
1056         // Insert newly added rows into viewToModel
1057         if (added.size() > 0) {
1058             Collections.sort(added);
1059             Row[] lastViewToModel = viewToModel;
1060             viewToModel = new Row[viewToModel.length + added.size()];
1061             insertInOrder(added, lastViewToModel);
1062         }
1063 
1064         // Update modelToView
1065         createModelToView(getModelWrapper().getRowCount());
1066         setModelToViewFromViewToModel(true);
1067 
1068         // Notify of change
1069         fireRowSorterChanged(oldViewToModel);
1070     }
1071 
1072     private void rowsDeleted0(int firstRow, int lastRow) {
1073         int[] oldViewToModel = getViewToModelAsInts(viewToModel);
1074         int removedFromView = 0;
1075         int i;
1076         int viewIndex;
1077 
1078         // Figure out how many visible rows are going to be effected.
1079         for (i = firstRow; i <= lastRow; i++) {
1080             viewIndex = modelToView[i];
1081             if (viewIndex != -1) {
1082                 removedFromView++;
1083                 viewToModel[viewIndex] = null;
1084             }
1085         }
1086 
1087         // Update the model index of rows after the effected region
1088         int delta = lastRow - firstRow + 1;
1089         for (i = modelToView.length - 1; i > lastRow; i--) {
1090             viewIndex = modelToView[i];
1091             if (viewIndex != -1) {
1092                 viewToModel[viewIndex].modelIndex -= delta;
1093             }
1094         }
1095 
1096         // Then patch up the viewToModel array
1097         if (removedFromView > 0) {
1098             Row[] newViewToModel = new Row[viewToModel.length -
1099                                            removedFromView];
1100             int newIndex = 0;
1101             int last = 0;
1102             for (i = 0; i < viewToModel.length; i++) {
1103                 if (viewToModel[i] == null) {
1104                     System.arraycopy(viewToModel, last,
1105                                      newViewToModel, newIndex, i - last);
1106                     newIndex += (i - last);
1107                     last = i + 1;
1108                 }
1109             }
1110             System.arraycopy(viewToModel, last,
1111                     newViewToModel, newIndex, viewToModel.length - last);
1112             viewToModel = newViewToModel;
1113         }
1114 
1115         // Update the modelToView mapping
1116         createModelToView(getModelWrapper().getRowCount());
1117         setModelToViewFromViewToModel(true);
1118 
1119         // And notify of change
1120         fireRowSorterChanged(oldViewToModel);
1121     }
1122 
1123     private void rowsUpdated0(int firstRow, int lastRow) {
1124         int[] oldViewToModel = getViewToModelAsInts(viewToModel);
1125         int i, j;
1126         int delta = lastRow - firstRow + 1;
1127         int modelIndex;
1128         int last;
1129         int index;
1130 
1131         if (getRowFilter() == null) {
1132             // Sorting only:
1133 
1134             // Remove the effected rows
1135             Row[] updated = new Row[delta];
1136             for (j = 0, i = firstRow; i <= lastRow; i++, j++) {
1137                 updated[j] = viewToModel[modelToView[i]];
1138             }
1139 
1140             // Sort the update rows
1141             Arrays.sort(updated);
1142 
1143             // Build the intermediary array: the array of
1144             // viewToModel without the effected rows.
1145             Row[] intermediary = new Row[viewToModel.length - delta];
1146             for (i = 0, j = 0; i < viewToModel.length; i++) {
1147                 modelIndex = viewToModel[i].modelIndex;
1148                 if (modelIndex < firstRow || modelIndex > lastRow) {
1149                     intermediary[j++] = viewToModel[i];
1150                 }
1151             }
1152 
1153             // Build the new viewToModel
1154             insertInOrder(Arrays.asList(updated), intermediary);
1155 
1156             // Update modelToView
1157             setModelToViewFromViewToModel(false);
1158         }
1159         else {
1160             // Sorting & filtering.
1161 
1162             // Remove the effected rows, adding them to updated and setting
1163             // modelToView to -2 for any rows that were not filtered out
1164             List<Row> updated = new ArrayList<Row>(delta);
1165             int newlyVisible = 0;
1166             int newlyHidden = 0;
1167             int effected = 0;
1168             for (i = firstRow; i <= lastRow; i++) {
1169                 if (modelToView[i] == -1) {
1170                     // This row was filtered out
1171                     if (include(i)) {
1172                         // No longer filtered
1173                         updated.add(new Row(this, i));
1174                         newlyVisible++;
1175                     }
1176                 }
1177                 else {
1178                     // This row was visible, make sure it should still be
1179                     // visible.
1180                     if (!include(i)) {
1181                         newlyHidden++;
1182                     }
1183                     else {
1184                         updated.add(viewToModel[modelToView[i]]);
1185                     }
1186                     modelToView[i] = -2;
1187                     effected++;
1188                 }
1189             }
1190 
1191             // Sort the updated rows
1192             Collections.sort(updated);
1193 
1194             // Build the intermediary array: the array of
1195             // viewToModel without the updated rows.
1196             Row[] intermediary = new Row[viewToModel.length - effected];
1197             for (i = 0, j = 0; i < viewToModel.length; i++) {
1198                 modelIndex = viewToModel[i].modelIndex;
1199                 if (modelToView[modelIndex] != -2) {
1200                     intermediary[j++] = viewToModel[i];
1201                 }
1202             }
1203 
1204             // Recreate viewToModel, if necessary
1205             if (newlyVisible != newlyHidden) {
1206                 viewToModel = new Row[viewToModel.length + newlyVisible -
1207                                       newlyHidden];
1208             }
1209 
1210             // Rebuild the new viewToModel array
1211             insertInOrder(updated, intermediary);
1212 
1213             // Update modelToView
1214             setModelToViewFromViewToModel(true);
1215         }
1216         // And finally fire a sort event.
1217         fireRowSorterChanged(oldViewToModel);
1218     }
1219 
1220     private void checkColumn(int column) {
1221         if (column < 0 || column >= getModelWrapper().getColumnCount()) {
1222             throw new IndexOutOfBoundsException(
1223                     "column beyond range of TableModel");
1224         }
1225     }
1226 
1227 
1228     /**
1229      * <code>DefaultRowSorter.ModelWrapper</code> is responsible for providing
1230      * the data that gets sorted by <code>DefaultRowSorter</code>.  You
1231      * normally do not interact directly with <code>ModelWrapper</code>.
1232      * Subclasses of <code>DefaultRowSorter</code> provide an
1233      * implementation of <code>ModelWrapper</code> wrapping another model.
1234      * For example,
1235      * <code>TableRowSorter</code> provides a <code>ModelWrapper</code> that
1236      * wraps a <code>TableModel</code>.
1237      * <p>
1238      * <code>ModelWrapper</code> makes a distinction between values as
1239      * <code>Object</code>s and <code>String</code>s.  This allows
1240      * implementations to provide a custom string
1241      * converter to be used instead of invoking <code>toString</code> on the
1242      * object.
1243      *
1244      * @param <M> the type of the underlying model
1245      * @param <I> the identifier supplied to the filter
1246      * @since 1.6
1247      * @see RowFilter
1248      * @see RowFilter.Entry
1249      */
1250     protected abstract static class ModelWrapper<M,I> {
1251         /**
1252          * Creates a new <code>ModelWrapper</code>.
1253          */
1254         protected ModelWrapper() {
1255         }
1256 
1257         /**
1258          * Returns the underlying model that this <code>Model</code> is
1259          * wrapping.
1260          *
1261          * @return the underlying model
1262          */
1263         public abstract M getModel();
1264 
1265         /**
1266          * Returns the number of columns in the model.
1267          *
1268          * @return the number of columns in the model
1269          */
1270         public abstract int getColumnCount();
1271 
1272         /**
1273          * Returns the number of rows in the model.
1274          *
1275          * @return the number of rows in the model
1276          */
1277         public abstract int getRowCount();
1278 
1279         /**
1280          * Returns the value at the specified index.
1281          *
1282          * @param row the row index
1283          * @param column the column index
1284          * @return the value at the specified index
1285          * @throws IndexOutOfBoundsException if the indices are outside
1286          *         the range of the model
1287          */
1288         public abstract Object getValueAt(int row, int column);
1289 
1290         /**
1291          * Returns the value as a <code>String</code> at the specified
1292          * index.  This implementation uses <code>toString</code> on
1293          * the result from <code>getValueAt</code> (making sure
1294          * to return an empty string for null values).  Subclasses that
1295          * override this method should never return null.
1296          *
1297          * @param row the row index
1298          * @param column the column index
1299          * @return the value at the specified index as a <code>String</code>
1300          * @throws IndexOutOfBoundsException if the indices are outside
1301          *         the range of the model
1302          */
1303         public String getStringValueAt(int row, int column) {
1304             Object o = getValueAt(row, column);
1305             if (o == null) {
1306                 return "";
1307             }
1308             String string = o.toString();
1309             if (string == null) {
1310                 return "";
1311             }
1312             return string;
1313         }
1314 
1315         /**
1316          * Returns the identifier for the specified row.  The return value
1317          * of this is used as the identifier for the
1318          * <code>RowFilter.Entry</code> that is passed to the
1319          * <code>RowFilter</code>.
1320          *
1321          * @param row the row to return the identifier for, in terms of
1322          *            the underlying model
1323          * @return the identifier
1324          * @see RowFilter.Entry#getIdentifier
1325          */
1326         public abstract I getIdentifier(int row);
1327     }
1328 
1329 
1330     /**
1331      * RowFilter.Entry implementation that delegates to the ModelWrapper.
1332      * getFilterEntry(int) creates the single instance of this that is
1333      * passed to the Filter.  Only call getFilterEntry(int) to get
1334      * the instance.
1335      */
1336     private class FilterEntry extends RowFilter.Entry<M,I> {
1337         /**
1338          * The index into the model, set in getFilterEntry
1339          */
1340         int modelIndex;
1341 
1342         public M getModel() {
1343             return getModelWrapper().getModel();
1344         }
1345 
1346         public int getValueCount() {
1347             return getModelWrapper().getColumnCount();
1348         }
1349 
1350         public Object getValue(int index) {
1351             return getModelWrapper().getValueAt(modelIndex, index);
1352         }
1353 
1354         public String getStringValue(int index) {
1355             return getModelWrapper().getStringValueAt(modelIndex, index);
1356         }
1357 
1358         public I getIdentifier() {
1359             return getModelWrapper().getIdentifier(modelIndex);
1360         }
1361     }
1362 
1363 
1364     /**
1365      * Row is used to handle the actual sorting by way of Comparable.  It
1366      * will use the sortKeys to do the actual comparison.
1367      */
1368     // NOTE: this class is static so that it can be placed in an array
1369     private static class Row implements Comparable<Row> {
1370         private DefaultRowSorter<?, ?> sorter;
1371         int modelIndex;
1372 
1373         public Row(DefaultRowSorter<?, ?> sorter, int index) {
1374             this.sorter = sorter;
1375             modelIndex = index;
1376         }
1377 
1378         public int compareTo(Row o) {
1379             return sorter.compare(modelIndex, o.modelIndex);
1380         }
1381     }
1382 }