/* * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.swing.table; import java.text.Collator; import java.util.*; import javax.swing.DefaultRowSorter; import javax.swing.RowFilter; import javax.swing.SortOrder; /** * An implementation of RowSorter that provides sorting * and filtering using a TableModel. * The following example shows adding sorting to a JTable: *
 *   TableModel myModel = createMyTableModel();
 *   JTable table = new JTable(myModel);
 *   table.setRowSorter(new TableRowSorter(myModel));
 * 
* This will do all the wiring such that when the user does the appropriate * gesture, such as clicking on the column header, the table will * visually sort. *

* JTable's row-based methods and JTable's * selection model refer to the view and not the underlying * model. Therefore, it is necessary to convert between the two. For * example, to get the selection in terms of myModel * you need to convert the indices: *

 *   int[] selection = table.getSelectedRows();
 *   for (int i = 0; i < selection.length; i++) {
 *     selection[i] = table.convertRowIndexToModel(selection[i]);
 *   }
 * 
* Similarly to select a row in JTable based on * a coordinate from the underlying model do the inverse: *
 *   table.setRowSelectionInterval(table.convertRowIndexToView(row),
 *                                 table.convertRowIndexToView(row));
 * 
*

* The previous example assumes you have not enabled filtering. If you * have enabled filtering convertRowIndexToView will return * -1 for locations that are not visible in the view. *

* TableRowSorter uses Comparators for doing * comparisons. The following defines how a Comparator is * chosen for a column: *

    *
  1. If a Comparator has been specified for the column by the * setComparator method, use it. *
  2. If the column class as returned by getColumnClass is * String, use the Comparator returned by * Collator.getInstance(). *
  3. If the column class implements Comparable, use a * Comparator that invokes the compareTo * method. *
  4. If a TableStringConverter has been specified, use it * to convert the values to Strings and then use the * Comparator returned by Collator.getInstance(). *
  5. Otherwise use the Comparator returned by * Collator.getInstance() on the results from * calling toString on the objects. *
*

* In addition to sorting TableRowSorter provides the ability * to filter. A filter is specified using the setFilter * method. The following example will only show rows containing the string * "foo": *

 *   TableModel myModel = createMyTableModel();
 *   TableRowSorter sorter = new TableRowSorter(myModel);
 *   sorter.setRowFilter(RowFilter.regexFilter(".*foo.*"));
 *   JTable table = new JTable(myModel);
 *   table.setRowSorter(sorter);
 * 
*

* If the underlying model structure changes (the * modelStructureChanged method is invoked) the following * are reset to their default values: Comparators by * column, current sort order, and whether each column is sortable. The default * sort order is natural (the same as the model), and columns are * sortable by default. *

* TableRowSorter has one formal type parameter: the type * of the model. Passing in a type that corresponds exactly to your * model allows you to filter based on your model without casting. * Refer to the documentation of RowFilter for an example * of this. *

* WARNING: DefaultTableModel returns a column * class of Object. As such all comparisons will * be done using toString. This may be unnecessarily * expensive. If the column only contains one type of value, such as * an Integer, you should override getColumnClass and * return the appropriate Class. This will dramatically * increase the performance of this class. * * @param the type of the model, which must be an implementation of * TableModel * @see javax.swing.JTable * @see javax.swing.RowFilter * @see javax.swing.table.DefaultTableModel * @see java.text.Collator * @see java.util.Comparator * @since 1.6 */ public class TableRowSorter extends DefaultRowSorter { /** * Comparator that uses compareTo on the contents. */ private static final Comparator COMPARABLE_COMPARATOR = new ComparableComparator(); /** * Underlying model. */ private M tableModel; /** * For toString conversions. */ private TableStringConverter stringConverter; /** * Creates a TableRowSorter with an empty model. */ public TableRowSorter() { this(null); } /** * Creates a TableRowSorter using model * as the underlying TableModel. * * @param model the underlying TableModel to use, * null is treated as an empty model */ public TableRowSorter(M model) { setModel(model); } /** * Sets the TableModel to use as the underlying model * for this TableRowSorter. A value of null * can be used to set an empty model. * * @param model the underlying model to use, or null */ public void setModel(M model) { tableModel = model; setModelWrapper(new TableRowSorterModelWrapper()); } /** * Sets the object responsible for converting values from the * model to strings. If non-null this * is used to convert any object values, that do not have a * registered Comparator, to strings. * * @param stringConverter the object responsible for converting values * from the model to strings */ public void setStringConverter(TableStringConverter stringConverter) { this.stringConverter = stringConverter; } /** * Returns the object responsible for converting values from the * model to strings. * * @return object responsible for converting values to strings. */ public TableStringConverter getStringConverter() { return stringConverter; } /** * Returns the Comparator for the specified * column. If a Comparator has not been specified using * the setComparator method a Comparator * will be returned based on the column class * (TableModel.getColumnClass) of the specified column. * If the column class is String, * Collator.getInstance is returned. If the * column class implements Comparable a private * Comparator is returned that invokes the * compareTo method. Otherwise * Collator.getInstance is returned. * * @throws IndexOutOfBoundsException {@inheritDoc} */ public Comparator getComparator(int column) { Comparator comparator = super.getComparator(column); if (comparator != null) { return comparator; } Class columnClass = getModel().getColumnClass(column); if (columnClass == String.class) { return Collator.getInstance(); } if (Comparable.class.isAssignableFrom(columnClass)) { return COMPARABLE_COMPARATOR; } return Collator.getInstance(); } /** * {@inheritDoc} * * @throws IndexOutOfBoundsException {@inheritDoc} */ protected boolean useToString(int column) { Comparator comparator = super.getComparator(column); if (comparator != null) { return false; } Class columnClass = getModel().getColumnClass(column); if (columnClass == String.class) { return false; } if (Comparable.class.isAssignableFrom(columnClass)) { return false; } return true; } /** * Implementation of DefaultRowSorter.ModelWrapper that delegates to a * TableModel. */ private class TableRowSorterModelWrapper extends ModelWrapper { public M getModel() { return tableModel; } public int getColumnCount() { return (tableModel == null) ? 0 : tableModel.getColumnCount(); } public int getRowCount() { return (tableModel == null) ? 0 : tableModel.getRowCount(); } public Object getValueAt(int row, int column) { return tableModel.getValueAt(row, column); } public String getStringValueAt(int row, int column) { TableStringConverter converter = getStringConverter(); if (converter != null) { // Use the converter String value = converter.toString( tableModel, row, column); if (value != null) { return value; } return ""; } // No converter, use getValueAt followed by toString Object o = getValueAt(row, column); if (o == null) { return ""; } String string = o.toString(); if (string == null) { return ""; } return string; } public Integer getIdentifier(int index) { return index; } } private static class ComparableComparator implements Comparator { @SuppressWarnings("unchecked") public int compare(Object o1, Object o2) { return ((Comparable)o1).compareTo(o2); } } }