1 /*
   2  * Copyright (c) 2005, 2013, 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 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;
 127 
 128     /**
 129      * Comparators specified by column.
 130      */
 131     private Comparator<?>[] comparators;
 132 
 133     /**
 134      * Whether or not the specified column is sortable, by column.
 135      */
 136     private boolean[] isSortable;
 137 
 138     /**
 139      * Cached SortKeys for the current sort.
 140      */
 141     private SortKey[] cachedSortKeys;
 142 
 143     /**
 144      * Cached comparators for the current sort
 145      */
 146     private Comparator<?>[] sortComparators;
 147 
 148     /**
 149      * Developer supplied Filter.
 150      */
 151     private RowFilter<? super M,? super I> filter;
 152 
 153     /**
 154      * Value passed to the filter.  The same instance is passed to the
 155      * filter for different rows.
 156      */
 157     private FilterEntry filterEntry;
 158 
 159     /**
 160      * The sort keys.
 161      */
 162     private List<SortKey> sortKeys;
 163 
 164     /**
 165      * Whether or not to use getStringValueAt.  This is indexed by column.
 166      */
 167     private boolean[] useToString;
 168 
 169     /**
 170      * Indicates the contents are sorted.  This is used if
 171      * getSortsOnUpdates is false and an update event is received.
 172      */
 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         }
 214         ModelWrapper<M,I> last = this.modelWrapper;
 215         this.modelWrapper = modelWrapper;
 216         if (last != null) {
 217             modelStructureChanged();
 218         } else {
 219             // If last is null, we're in the constructor. If we're in
 220             // the constructor we don't want to call to overridable methods.
 221             modelRowCount = getModelWrapper().getRowCount();
 222         }
 223     }
 224 
 225     /**
 226      * Returns the model wrapper providing the data that is being sorted and
 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();
 316             if (viewToModel == null) {
 317                 // Currently unsorted, use sort so that internal fields
 318                 // are correctly set.
 319                 sort();
 320             } else {
 321                 sortExistingData();
 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     }
 436 
 437     /**
 438      * Reverses the sort order from ascending to descending (or
 439      * descending to ascending) if the specified column is already the
 440      * primary sorted column; otherwise, makes the specified column
 441      * the primary sorted column, with an ascending sort order.  If
 442      * the specified column is not sortable, this method has no
 443      * effect.
 444      *
 445      * @param column index of the column to make the primary sorted column,
 446      *        in terms of the underlying model
 447      * @throws IndexOutOfBoundsException {@inheritDoc}
 448      * @see #setSortable(int,boolean)
 449      * @see #setMaxSortKeys(int)
 450      */
 451     public void toggleSortOrder(int column) {
 452         checkColumn(column);
 453         if (isSortable(column)) {
 454             List<SortKey> keys = new ArrayList<SortKey>(getSortKeys());
 455             SortKey sortKey;
 456             int sortIndex;
 457             for (sortIndex = keys.size() - 1; sortIndex >= 0; sortIndex--) {
 458                 if (keys.get(sortIndex).getColumn() == column) {
 459                     break;
 460                 }
 461             }
 462             if (sortIndex == -1) {
 463                 // Key doesn't exist
 464                 sortKey = new SortKey(column, SortOrder.ASCENDING);
 465                 keys.add(0, sortKey);
 466             }
 467             else if (sortIndex == 0) {
 468                 // It's the primary sorting key, toggle it
 469                 keys.set(0, toggle(keys.get(0)));
 470             }
 471             else {
 472                 // It's not the first, but was sorted on, remove old
 473                 // entry, insert as first with ascending.
 474                 keys.remove(sortIndex);
 475                 keys.add(0, new SortKey(column, SortOrder.ASCENDING));
 476             }
 477             if (keys.size() > getMaxSortKeys()) {
 478                 keys = keys.subList(0, getMaxSortKeys());
 479             }
 480             setSortKeys(keys);
 481         }
 482     }
 483 
 484     private SortKey toggle(SortKey key) {
 485         if (key.getSortOrder() == SortOrder.ASCENDING) {
 486             return new SortKey(key.getColumn(), SortOrder.DESCENDING);
 487         }
 488         return new SortKey(key.getColumn(), SortOrder.ASCENDING);
 489     }
 490 
 491     /**
 492      * {@inheritDoc}
 493      *
 494      * @throws IndexOutOfBoundsException {@inheritDoc}
 495      */
 496     public int convertRowIndexToView(int index) {
 497         if (modelToView == null) {
 498             if (index < 0 || index >= getModelWrapper().getRowCount()) {
 499                 throw new IndexOutOfBoundsException("Invalid index");
 500             }
 501             return index;
 502         }
 503         return modelToView[index];
 504     }
 505 
 506     /**
 507      * {@inheritDoc}
 508      *
 509      * @throws IndexOutOfBoundsException {@inheritDoc}
 510      */
 511     public int convertRowIndexToModel(int index) {
 512         if (viewToModel == null) {
 513             if (index < 0 || index >= getModelWrapper().getRowCount()) {
 514                 throw new IndexOutOfBoundsException("Invalid index");
 515             }
 516             return index;
 517         }
 518         return viewToModel[index].modelIndex;
 519     }
 520 
 521     private boolean isUnsorted() {
 522         List<? extends SortKey> keys = getSortKeys();
 523         int keySize = keys.size();
 524         return (keySize == 0 || keys.get(0).getSortOrder() ==
 525                 SortOrder.UNSORTED);
 526     }
 527 
 528     /**
 529      * Sorts the existing filtered data.  This should only be used if
 530      * the filter hasn't changed.
 531      */
 532     private void sortExistingData() {
 533         int[] lastViewToModel = getViewToModelAsInts(viewToModel);
 534 
 535         updateUseToString();
 536         cacheSortKeys(getSortKeys());
 537 
 538         if (isUnsorted()) {
 539             if (getRowFilter() == null) {
 540                 viewToModel = null;
 541                 modelToView = null;
 542             } else {
 543                 int included = 0;
 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 {
 585                     // unsorted -> unsorted
 586                     // No need to do anything.
 587                     return;
 588                 }
 589             }
 590             else {
 591                 // There is filter, reset mappings
 592                 initializeFilteredMapping();
 593             }
 594         }
 595         else {
 596             cacheSortKeys(getSortKeys());
 597 
 598             if (getRowFilter() != null) {
 599                 initializeFilteredMapping();
 600             }
 601             else {
 602                 createModelToView(getModelWrapper().getRowCount());
 603                 createViewToModel(getModelWrapper().getRowCount());
 604             }
 605 
 606             // sort them
 607             Arrays.sort(viewToModel);
 608 
 609             // Update the modelToView array
 610             setModelToViewFromViewToModel(false);
 611         }
 612         fireRowSorterChanged(lastViewToModel);
 613     }
 614 
 615     /**
 616      * Updates the useToString mapping before a sort.
 617      */
 618     private void updateUseToString() {
 619         int i = getModelWrapper().getColumnCount();
 620         if (useToString == null || useToString.length != i) {
 621             useToString = new boolean[i];
 622         }
 623         for (--i; i >= 0; i--) {
 624             useToString[i] = useToString(i);
 625         }
 626     }
 627 
 628     /**
 629      * Resets the viewToModel and modelToView mappings based on
 630      * the current Filter.
 631      */
 632     private void initializeFilteredMapping() {
 633         int rowCount = getModelWrapper().getRowCount();
 634         int i, j;
 635         int excludedCount = 0;
 636 
 637         // Update model -> view
 638         createModelToView(rowCount);
 639         for (i = 0; i < rowCount; i++) {
 640             if (include(i)) {
 641                 modelToView[i] = i - excludedCount;
 642             }
 643             else {
 644                 modelToView[i] = -1;
 645                 excludedCount++;
 646             }
 647         }
 648 
 649         // Update view -> model
 650         createViewToModel(rowCount - excludedCount);
 651         for (i = 0, j = 0; i < rowCount; i++) {
 652             if (modelToView[i] != -1) {
 653                 viewToModel[j++].modelIndex = i;
 654             }
 655         }
 656     }
 657 
 658     /**
 659      * Makes sure the modelToView array is of size rowCount.
 660      */
 661     private void createModelToView(int rowCount) {
 662         if (modelToView == null || modelToView.length != rowCount) {
 663             modelToView = new int[rowCount];
 664         }
 665     }
 666 
 667     /**
 668      * Resets the viewToModel array to be of size rowCount.
 669      */
 670     private void createViewToModel(int rowCount) {
 671         int recreateFrom = 0;
 672         if (viewToModel != null) {
 673             recreateFrom = Math.min(rowCount, viewToModel.length);
 674             if (viewToModel.length != rowCount) {
 675                 Row[] oldViewToModel = viewToModel;
 676                 viewToModel = new Row[rowCount];
 677                 System.arraycopy(oldViewToModel, 0, viewToModel,
 678                                  0, recreateFrom);
 679             }
 680         }
 681         else {
 682             viewToModel = new Row[rowCount];
 683         }
 684         int i;
 685         for (i = 0; i < recreateFrom; i++) {
 686             viewToModel[i].modelIndex = i;
 687         }
 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      * @throws IndexOutOfBoundsException if <code>column</code> is not valid
 716      */
 717     protected boolean useToString(int column) {
 718         return (getComparator(column) == null);
 719     }
 720 
 721     /**
 722      * Refreshes the modelToView mapping from that of viewToModel.
 723      * If <code>unsetFirst</code> is true, all indices in modelToView are
 724      * first set to -1.
 725      */
 726     private void setModelToViewFromViewToModel(boolean unsetFirst) {
 727         int i;
 728         if (unsetFirst) {
 729             for (i = modelToView.length - 1; i >= 0; i--) {
 730                 modelToView[i] = -1;
 731             }
 732         }
 733         for (i = viewToModel.length - 1; i >= 0; i--) {
 734             modelToView[viewToModel[i].modelIndex] = i;
 735         }
 736     }
 737 
 738     private int[] getViewToModelAsInts(Row[] viewToModel) {
 739         if (viewToModel != null) {
 740             int[] viewToModelI = new int[viewToModel.length];
 741             for (int i = viewToModel.length - 1; i >= 0; i--) {
 742                 viewToModelI[i] = viewToModel[i].modelIndex;
 743             }
 744             return viewToModelI;
 745         }
 746         return new int[0];
 747     }
 748 
 749     /**
 750      * Sets the <code>Comparator</code> to use when sorting the specified
 751      * column.  This does not trigger a sort.  If you want to sort after
 752      * setting the comparator you need to explicitly invoke <code>sort</code>.
 753      *
 754      * @param column the index of the column the <code>Comparator</code> is
 755      *        to be used for, in terms of the underlying model
 756      * @param comparator the <code>Comparator</code> to use
 757      * @throws IndexOutOfBoundsException if <code>column</code> is outside
 758      *         the range of the underlying model
 759      */
 760     public void setComparator(int column, Comparator<?> comparator) {
 761         checkColumn(column);
 762         if (comparators == null) {
 763             comparators = new Comparator<?>[getModelWrapper().getColumnCount()];
 764         }
 765         comparators[column] = comparator;
 766     }
 767 
 768     /**
 769      * Returns the <code>Comparator</code> for the specified
 770      * column.  This will return <code>null</code> if a <code>Comparator</code>
 771      * has not been specified for the column.
 772      *
 773      * @param column the column to fetch the <code>Comparator</code> for, in
 774      *        terms of the underlying model
 775      * @return the <code>Comparator</code> for the specified column
 776      * @throws IndexOutOfBoundsException if column is outside
 777      *         the range of the underlying model
 778      */
 779     public Comparator<?> getComparator(int column) {
 780         checkColumn(column);
 781         if (comparators != null) {
 782             return comparators[column];
 783         }
 784         return null;
 785     }
 786 
 787     // Returns the Comparator to use during sorting.  Where as
 788     // getComparator() may return null, this will never return null.
 789     private Comparator<?> getComparator0(int column) {
 790         Comparator<?> comparator = getComparator(column);
 791         if (comparator != null) {
 792             return comparator;
 793         }
 794         // This should be ok as useToString(column) should have returned
 795         // true in this case.
 796         return Collator.getInstance();
 797     }
 798 
 799     private RowFilter.Entry<M,I> getFilterEntry(int modelIndex) {
 800         if (filterEntry == null) {
 801             filterEntry = new FilterEntry();
 802         }
 803         filterEntry.modelIndex = modelIndex;
 804         return filterEntry;
 805     }
 806 
 807     /**
 808      * {@inheritDoc}
 809      */
 810     public int getViewRowCount() {
 811         if (viewToModel != null) {
 812             // When filtering this may differ from getModelWrapper().getRowCount()
 813             return viewToModel.length;
 814         }
 815         return getModelWrapper().getRowCount();
 816     }
 817 
 818     /**
 819      * {@inheritDoc}
 820      */
 821     public int getModelRowCount() {
 822         return getModelWrapper().getRowCount();
 823     }
 824 
 825     private void allChanged() {
 826         modelToView = null;
 827         viewToModel = null;
 828         comparators = null;
 829         isSortable = null;
 830         if (isUnsorted()) {
 831             // Keys are already empty, to force a resort we have to
 832             // call sort
 833             sort();
 834         } else {
 835             setSortKeys(null);
 836         }
 837     }
 838 
 839     /**
 840      * {@inheritDoc}
 841      */
 842     public void modelStructureChanged() {
 843         allChanged();
 844         modelRowCount = getModelWrapper().getRowCount();
 845     }
 846 
 847     /**
 848      * {@inheritDoc}
 849      */
 850     public void allRowsChanged() {
 851         modelRowCount = getModelWrapper().getRowCount();
 852         sort();
 853     }
 854 
 855     /**
 856      * {@inheritDoc}
 857      *
 858      * @throws IndexOutOfBoundsException {@inheritDoc}
 859      */
 860     public void rowsInserted(int firstRow, int endRow) {
 861         checkAgainstModel(firstRow, endRow);
 862         int newModelRowCount = getModelWrapper().getRowCount();
 863         if (endRow >= newModelRowCount) {
 864             throw new IndexOutOfBoundsException("Invalid range");
 865         }
 866         modelRowCount = newModelRowCount;
 867         if (shouldOptimizeChange(firstRow, endRow)) {
 868             rowsInserted0(firstRow, endRow);
 869         }
 870     }
 871 
 872     /**
 873      * {@inheritDoc}
 874      *
 875      * @throws IndexOutOfBoundsException {@inheritDoc}
 876      */
 877     public void rowsDeleted(int firstRow, int endRow) {
 878         checkAgainstModel(firstRow, endRow);
 879         if (firstRow >= modelRowCount || endRow >= modelRowCount) {
 880             throw new IndexOutOfBoundsException("Invalid range");
 881         }
 882         modelRowCount = getModelWrapper().getRowCount();
 883         if (shouldOptimizeChange(firstRow, endRow)) {
 884             rowsDeleted0(firstRow, endRow);
 885         }
 886     }
 887 
 888     /**
 889      * {@inheritDoc}
 890      *
 891      * @throws IndexOutOfBoundsException {@inheritDoc}
 892      */
 893     public void rowsUpdated(int firstRow, int endRow) {
 894         checkAgainstModel(firstRow, endRow);
 895         if (firstRow >= modelRowCount || endRow >= modelRowCount) {
 896             throw new IndexOutOfBoundsException("Invalid range");
 897         }
 898         if (getSortsOnUpdates()) {
 899             if (shouldOptimizeChange(firstRow, endRow)) {
 900                 rowsUpdated0(firstRow, endRow);
 901             }
 902         }
 903         else {
 904             sorted = false;
 905         }
 906     }
 907 
 908     /**
 909      * {@inheritDoc}
 910      *
 911      * @throws IndexOutOfBoundsException {@inheritDoc}
 912      */
 913     public void rowsUpdated(int firstRow, int endRow, int column) {
 914         checkColumn(column);
 915         rowsUpdated(firstRow, endRow);
 916     }
 917 
 918     private void checkAgainstModel(int firstRow, int endRow) {
 919         if (firstRow > endRow || firstRow < 0 || endRow < 0 ||
 920                 firstRow > modelRowCount) {
 921             throw new IndexOutOfBoundsException("Invalid range");
 922         }
 923     }
 924 
 925     /**
 926      * Returns true if the specified row should be included.
 927      */
 928     private boolean include(int row) {
 929         RowFilter<? super M, ? super I> filter = getRowFilter();
 930         if (filter != null) {
 931             return filter.include(getFilterEntry(row));
 932         }
 933         // null filter, always include the row.
 934         return true;
 935     }
 936 
 937     @SuppressWarnings("unchecked")
 938     private int compare(int model1, int model2) {
 939         int column;
 940         SortOrder sortOrder;
 941         Object v1, v2;
 942         int result;
 943 
 944         for (int counter = 0; counter < cachedSortKeys.length; counter++) {
 945             column = cachedSortKeys[counter].getColumn();
 946             sortOrder = cachedSortKeys[counter].getSortOrder();
 947             if (sortOrder == SortOrder.UNSORTED) {
 948                 result = model1 - model2;
 949             } else {
 950                 // v1 != null && v2 != null
 951                 if (useToString[column]) {
 952                     v1 = getModelWrapper().getStringValueAt(model1, column);
 953                     v2 = getModelWrapper().getStringValueAt(model2, column);
 954                 } else {
 955                     v1 = getModelWrapper().getValueAt(model1, column);
 956                     v2 = getModelWrapper().getValueAt(model2, column);
 957                 }
 958                 // Treat nulls as < then non-null
 959                 if (v1 == null) {
 960                     if (v2 == null) {
 961                         result = 0;
 962                     } else {
 963                         result = -1;
 964                     }
 965                 } else if (v2 == null) {
 966                     result = 1;
 967                 } else {
 968                     Comparator<Object> c =
 969                         (Comparator<Object>)sortComparators[counter];
 970                     result = c.compare(v1, v2);
 971                 }
 972                 if (sortOrder == SortOrder.DESCENDING) {
 973                     result *= -1;
 974                 }
 975             }
 976             if (result != 0) {
 977                 return result;
 978             }
 979         }
 980         // If we get here, they're equal. Fallback to model order.
 981         return model1 - model2;
 982     }
 983 
 984     /**
 985      * Whether not we are filtering/sorting.
 986      */
 987     private boolean isTransformed() {
 988         return (viewToModel != null);
 989     }
 990 
 991     /**
 992      * Insets new set of entries.
 993      *
 994      * @param toAdd the Rows to add, sorted
 995      * @param current the array to insert the items into
 996      */
 997     private void insertInOrder(List<Row> toAdd, Row[] current) {
 998         int last = 0;
 999         int index;
1000         int max = toAdd.size();
1001         for (int i = 0; i < max; i++) {
1002             index = Arrays.binarySearch(current, toAdd.get(i));
1003             if (index < 0) {
1004                 index = -1 - index;
1005             }
1006             System.arraycopy(current, last,
1007                              viewToModel, last + i, index - last);
1008             viewToModel[index + i] = toAdd.get(i);
1009             last = index;
1010         }
1011         System.arraycopy(current, last, viewToModel, last + max,
1012                          current.length - last);
1013     }
1014 
1015     /**
1016      * Returns true if we should try and optimize the processing of the
1017      * <code>TableModelEvent</code>.  If this returns false, assume the
1018      * event was dealt with and no further processing needs to happen.
1019      */
1020     private boolean shouldOptimizeChange(int firstRow, int lastRow) {
1021         if (!isTransformed()) {
1022             // Not transformed, nothing to do.
1023             return false;
1024         }
1025         if (!sorted || (lastRow - firstRow) > viewToModel.length / 10) {
1026             // We either weren't sorted, or to much changed, sort it all
1027             sort();
1028             return false;
1029         }
1030         return true;
1031     }
1032 
1033     private void rowsInserted0(int firstRow, int lastRow) {
1034         int[] oldViewToModel = getViewToModelAsInts(viewToModel);
1035         int i;
1036         int delta = (lastRow - firstRow) + 1;
1037         List<Row> added = new ArrayList<Row>(delta);
1038 
1039         // Build the list of Rows to add into added
1040         for (i = firstRow; i <= lastRow; i++) {
1041             if (include(i)) {
1042                 added.add(new Row(this, i));
1043             }
1044         }
1045 
1046         // Adjust the model index of rows after the effected region
1047         int viewIndex;
1048         for (i = modelToView.length - 1; i >= firstRow; i--) {
1049             viewIndex = modelToView[i];
1050             if (viewIndex != -1) {
1051                 viewToModel[viewIndex].modelIndex += delta;
1052             }
1053         }
1054 
1055         // Insert newly added rows into viewToModel
1056         if (added.size() > 0) {
1057             Collections.sort(added);
1058             Row[] lastViewToModel = viewToModel;
1059             viewToModel = new Row[viewToModel.length + added.size()];
1060             insertInOrder(added, lastViewToModel);
1061         }
1062 
1063         // Update modelToView
1064         createModelToView(getModelWrapper().getRowCount());
1065         setModelToViewFromViewToModel(true);
1066 
1067         // Notify of change
1068         fireRowSorterChanged(oldViewToModel);
1069     }
1070 
1071     private void rowsDeleted0(int firstRow, int lastRow) {
1072         int[] oldViewToModel = getViewToModelAsInts(viewToModel);
1073         int removedFromView = 0;
1074         int i;
1075         int viewIndex;
1076 
1077         // Figure out how many visible rows are going to be effected.
1078         for (i = firstRow; i <= lastRow; i++) {
1079             viewIndex = modelToView[i];
1080             if (viewIndex != -1) {
1081                 removedFromView++;
1082                 viewToModel[viewIndex] = null;
1083             }
1084         }
1085 
1086         // Update the model index of rows after the effected region
1087         int delta = lastRow - firstRow + 1;
1088         for (i = modelToView.length - 1; i > lastRow; i--) {
1089             viewIndex = modelToView[i];
1090             if (viewIndex != -1) {
1091                 viewToModel[viewIndex].modelIndex -= delta;
1092             }
1093         }
1094 
1095         // Then patch up the viewToModel array
1096         if (removedFromView > 0) {
1097             Row[] newViewToModel = new Row[viewToModel.length -
1098                                            removedFromView];
1099             int newIndex = 0;
1100             int last = 0;
1101             for (i = 0; i < viewToModel.length; i++) {
1102                 if (viewToModel[i] == null) {
1103                     System.arraycopy(viewToModel, last,
1104                                      newViewToModel, newIndex, i - last);
1105                     newIndex += (i - last);
1106                     last = i + 1;
1107                 }
1108             }
1109             System.arraycopy(viewToModel, last,
1110                     newViewToModel, newIndex, viewToModel.length - last);
1111             viewToModel = newViewToModel;
1112         }
1113 
1114         // Update the modelToView mapping
1115         createModelToView(getModelWrapper().getRowCount());
1116         setModelToViewFromViewToModel(true);
1117 
1118         // And notify of change
1119         fireRowSorterChanged(oldViewToModel);
1120     }
1121 
1122     private void rowsUpdated0(int firstRow, int lastRow) {
1123         int[] oldViewToModel = getViewToModelAsInts(viewToModel);
1124         int i, j;
1125         int delta = lastRow - firstRow + 1;
1126         int modelIndex;
1127         int last;
1128         int index;
1129 
1130         if (getRowFilter() == null) {
1131             // Sorting only:
1132 
1133             // Remove the effected rows
1134             Row[] updated = new Row[delta];
1135             for (j = 0, i = firstRow; i <= lastRow; i++, j++) {
1136                 updated[j] = viewToModel[modelToView[i]];
1137             }
1138 
1139             // Sort the update rows
1140             Arrays.sort(updated);
1141 
1142             // Build the intermediary array: the array of
1143             // viewToModel without the effected rows.
1144             Row[] intermediary = new Row[viewToModel.length - delta];
1145             for (i = 0, j = 0; i < viewToModel.length; i++) {
1146                 modelIndex = viewToModel[i].modelIndex;
1147                 if (modelIndex < firstRow || modelIndex > lastRow) {
1148                     intermediary[j++] = viewToModel[i];
1149                 }
1150             }
1151 
1152             // Build the new viewToModel
1153             insertInOrder(Arrays.asList(updated), intermediary);
1154 
1155             // Update modelToView
1156             setModelToViewFromViewToModel(false);
1157         }
1158         else {
1159             // Sorting & filtering.
1160 
1161             // Remove the effected rows, adding them to updated and setting
1162             // modelToView to -2 for any rows that were not filtered out
1163             List<Row> updated = new ArrayList<Row>(delta);
1164             int newlyVisible = 0;
1165             int newlyHidden = 0;
1166             int effected = 0;
1167             for (i = firstRow; i <= lastRow; i++) {
1168                 if (modelToView[i] == -1) {
1169                     // This row was filtered out
1170                     if (include(i)) {
1171                         // No longer filtered
1172                         updated.add(new Row(this, i));
1173                         newlyVisible++;
1174                     }
1175                 }
1176                 else {
1177                     // This row was visible, make sure it should still be
1178                     // visible.
1179                     if (!include(i)) {
1180                         newlyHidden++;
1181                     }
1182                     else {
1183                         updated.add(viewToModel[modelToView[i]]);
1184                     }
1185                     modelToView[i] = -2;
1186                     effected++;
1187                 }
1188             }
1189 
1190             // Sort the updated rows
1191             Collections.sort(updated);
1192 
1193             // Build the intermediary array: the array of
1194             // viewToModel without the updated rows.
1195             Row[] intermediary = new Row[viewToModel.length - effected];
1196             for (i = 0, j = 0; i < viewToModel.length; i++) {
1197                 modelIndex = viewToModel[i].modelIndex;
1198                 if (modelToView[modelIndex] != -2) {
1199                     intermediary[j++] = viewToModel[i];
1200                 }
1201             }
1202 
1203             // Recreate viewToModel, if necessary
1204             if (newlyVisible != newlyHidden) {
1205                 viewToModel = new Row[viewToModel.length + newlyVisible -
1206                                       newlyHidden];
1207             }
1208 
1209             // Rebuild the new viewToModel array
1210             insertInOrder(updated, intermediary);
1211 
1212             // Update modelToView
1213             setModelToViewFromViewToModel(true);
1214         }
1215         // And finally fire a sort event.
1216         fireRowSorterChanged(oldViewToModel);
1217     }
1218 
1219     private void checkColumn(int column) {
1220         if (column < 0 || column >= getModelWrapper().getColumnCount()) {
1221             throw new IndexOutOfBoundsException(
1222                     "column beyond range of TableModel");
1223         }
1224     }
1225 
1226 
1227     /**
1228      * <code>DefaultRowSorter.ModelWrapper</code> is responsible for providing
1229      * the data that gets sorted by <code>DefaultRowSorter</code>.  You
1230      * normally do not interact directly with <code>ModelWrapper</code>.
1231      * Subclasses of <code>DefaultRowSorter</code> provide an
1232      * implementation of <code>ModelWrapper</code> wrapping another model.
1233      * For example,
1234      * <code>TableRowSorter</code> provides a <code>ModelWrapper</code> that
1235      * wraps a <code>TableModel</code>.
1236      * <p>
1237      * <code>ModelWrapper</code> makes a distinction between values as
1238      * <code>Object</code>s and <code>String</code>s.  This allows
1239      * implementations to provide a custom string
1240      * converter to be used instead of invoking <code>toString</code> on the
1241      * object.
1242      *
1243      * @param <M> the type of the underlying model
1244      * @param <I> the identifier supplied to the filter
1245      * @since 1.6
1246      * @see RowFilter
1247      * @see RowFilter.Entry
1248      */
1249     protected abstract static class ModelWrapper<M,I> {
1250         /**
1251          * Creates a new <code>ModelWrapper</code>.
1252          */
1253         protected ModelWrapper() {
1254         }
1255 
1256         /**
1257          * Returns the underlying model that this <code>Model</code> is
1258          * wrapping.
1259          *
1260          * @return the underlying model
1261          */
1262         public abstract M getModel();
1263 
1264         /**
1265          * Returns the number of columns in the model.
1266          *
1267          * @return the number of columns in the model
1268          */
1269         public abstract int getColumnCount();
1270 
1271         /**
1272          * Returns the number of rows in the model.
1273          *
1274          * @return the number of rows in the model
1275          */
1276         public abstract int getRowCount();
1277 
1278         /**
1279          * Returns the value at the specified index.
1280          *
1281          * @param row the row index
1282          * @param column the column index
1283          * @return the value at the specified index
1284          * @throws IndexOutOfBoundsException if the indices are outside
1285          *         the range of the model
1286          */
1287         public abstract Object getValueAt(int row, int column);
1288 
1289         /**
1290          * Returns the value as a <code>String</code> at the specified
1291          * index.  This implementation uses <code>toString</code> on
1292          * the result from <code>getValueAt</code> (making sure
1293          * to return an empty string for null values).  Subclasses that
1294          * override this method should never return null.
1295          *
1296          * @param row the row index
1297          * @param column the column index
1298          * @return the value at the specified index as a <code>String</code>
1299          * @throws IndexOutOfBoundsException if the indices are outside
1300          *         the range of the model
1301          */
1302         public String getStringValueAt(int row, int column) {
1303             Object o = getValueAt(row, column);
1304             if (o == null) {
1305                 return "";
1306             }
1307             String string = o.toString();
1308             if (string == null) {
1309                 return "";
1310             }
1311             return string;
1312         }
1313 
1314         /**
1315          * Returns the identifier for the specified row.  The return value
1316          * of this is used as the identifier for the
1317          * <code>RowFilter.Entry</code> that is passed to the
1318          * <code>RowFilter</code>.
1319          *
1320          * @param row the row to return the identifier for, in terms of
1321          *            the underlying model
1322          * @return the identifier
1323          * @see RowFilter.Entry#getIdentifier
1324          */
1325         public abstract I getIdentifier(int row);
1326     }
1327 
1328 
1329     /**
1330      * RowFilter.Entry implementation that delegates to the ModelWrapper.
1331      * getFilterEntry(int) creates the single instance of this that is
1332      * passed to the Filter.  Only call getFilterEntry(int) to get
1333      * the instance.
1334      */
1335     private class FilterEntry extends RowFilter.Entry<M,I> {
1336         /**
1337          * The index into the model, set in getFilterEntry
1338          */
1339         int modelIndex;
1340 
1341         public M getModel() {
1342             return getModelWrapper().getModel();
1343         }
1344 
1345         public int getValueCount() {
1346             return getModelWrapper().getColumnCount();
1347         }
1348 
1349         public Object getValue(int index) {
1350             return getModelWrapper().getValueAt(modelIndex, index);
1351         }
1352 
1353         public String getStringValue(int index) {
1354             return getModelWrapper().getStringValueAt(modelIndex, index);
1355         }
1356 
1357         public I getIdentifier() {
1358             return getModelWrapper().getIdentifier(modelIndex);
1359         }
1360     }
1361 
1362 
1363     /**
1364      * Row is used to handle the actual sorting by way of Comparable.  It
1365      * will use the sortKeys to do the actual comparison.
1366      */
1367     // NOTE: this class is static so that it can be placed in an array
1368     private static class Row implements Comparable<Row> {
1369         private DefaultRowSorter<?, ?> sorter;
1370         int modelIndex;
1371 
1372         public Row(DefaultRowSorter<?, ?> sorter, int index) {
1373             this.sorter = sorter;
1374             modelIndex = index;
1375         }
1376 
1377         public int compareTo(Row o) {
1378             return sorter.compare(modelIndex, o.modelIndex);
1379         }
1380     }
1381 }