JClass Elements

PreviousNextIndex

9

Sortable Table

Features of JCMappingSort  Features of JCSortableTable  Classes and Interfaces

Constructors and Methods  Examples


9.1 Features of JCMappingSort

Sorting can be accomplished by indexing the list of objects that are going to be ordered according to some comparison policy. It can be much more efficient to sort these indices instead of sorting the objects themselves. The idea is to form an array of indices. Initially, a[1] = 1, a[2] = 2, and so on, up to n, the length of the list. After sorting, the result might be a[1] = 9, a[2] = 3, ... a[n-1] = 1, ... a[n] = 7, where now the index in a[1] corresponds to the object that is the smallest element in the list according to the supplied comparison rule. The index in a[2] corresponds to the next smallest object, and so on. The list hasn't changed, but the array supplies a mechanism for traversing the list according to some ordering principle.

The foregoing paragraph shows you that if you want to use this type of mapping sort in your application, you'll need to supply an array of indices and a comparator to use with your list. In some cases, a comparator is already available. A number of common objects implement the Comparable interface in Java 2. You can compare any of these types without needing to supply an explicit comparator.

JCMappingSort provides a sort() method, which takes an implementation of JCIntComparator and an array of indices as parameters, and modifies the passed-in array based on the compare() method defined by your implementation of JCIntComparator.


9.2 Features of JCSortableTable

JCSortableTable uses a comparator and a configureable list of column indices, making this class useful for establishing a sort policy that specifies what should be done when two elements in the primary column have the same value. Elements in the primary column that compare the same are arranged among themselves by sorting the secondary column. The process can be continued as necessary by including more columns in the list.


9.3 Classes and Interfaces

JCMappingSort

CollectionIntComparator

Implements JCIntComparator to compare two lists.

JCIntComparator

An interface that declares a compare method taking two indices as parameters. The compare method must be able to compare the Objects corresponding to the indices.

JCMappingSort

Contains a static sort() method that is passed a JCIntComparator and an array of indices. The array containing the indices is sorted rather than sorting the list objects to which they refer.

You'll find these classes and interfaces in com.klg.jclass.util.

JCSortableTable

JCSortableTable

JCSortableTable is a subclass of JTable that internally wraps any TableModel it is given with a JCRowSortTableModel and provides a Comparator that has a configureable list of the column indexes that it uses for sorting. Clicking on a column header invokes the sorting behavior tied to that column, clicking again reverses the direction of the sort.

JCRowComparator

This interface is to be used with JCRowSortModel. It sorts rows using a specified ordered list of columns as the sort keys. By default, it sorts on the first column.

JCRowSortModel

An interface that defines methods for sorting rows by specifying which columns are to be used as keys.

Using your own comparator with JCSortableTable

If you wish to provide your own comparator for a JCSortableTable, follow these steps:

  1. Create a javax.swing.table.TableModel.
  2. Create a com.klg.jclass.util.swing.DefaultRowSortTableModel, giving it the TableModel.
  3. Set your comparator to this instance of a DefaultRowSortTableModel.
  4. Set the DefaultRowSortTableModel on your JCSortableTable.

Note that the data model you set in step 2 should be a JCRowSortTableModel. If it is not, JCSortableTable will wrap the data model you provide with a JCRowSortTableModel.


9.4 Constructors and Methods

Constructors for JCSortableTable

JCSortableTable()

JCSortableTable is a subclass of JTable that internally wraps any TableModel it is given with a JCRowSortTableModel and provides a Comparator that has a configureable list of the column indexes that it uses for sorting.

JCSortableTable(
  int numRows,
  int numColumns)

Constructor that specifies the number of rows and columns in the table.

JCSortableTable(
  Object[][] rowData,
  Object[] columnNames)

The constructor for a data source composed of an array of Objects.

JCSortableTable(
  TableModel dm)

Constructor that accepts a TableModel.

JCSortableTable(
  TableModel dm,
  ColumnModel cm)

Constructor that accepts both a ColumnModel and a TableModel.

JCSortableTable(
  TableModel dm,

  ColumnModel cm,
  ListSelectionModel sm)

Constructor that accepts a ColumnModel, a TableModel, and a ListSelectionModel.

JCSortableTable(
  Vector rowData,
  Vector columnNames)

The constructor for a Vector data source.

The core of the sorting mechanism is based on providing the sort() method with a list of indices specifying an ordered list of columns on which the sort is to be based:

public static void sort(JCIntComparator comparator, int indices[])

public static void sort(JCIntComparator comparator, int indices[], int start, int end)

Both methods require a JCIntComparator and an array of indices. The second method includes two additional parameters that are useful in many sorting algorithms.

Methods

In addition to the host of methods it inherits from JTable, JCSortableTable adds many of its own:

createDefault
ColumnsFrom
Model()

Overridden from the superclass to allow auto-creation of our own column model.

getAutoSort()

Returns whether the data is automatically sorted when it changes according the current comparator.

getCellEditor()

Takes parameter int row, int column to get the cell editor for that row and column.

getCellRenderer()

Takes parameter int row, int column to get the cell editor for that row and column.

getKeyColumns()

Takes parameter int column to return the key columns used to sort the table model when clicking on the specified column.

getUnsortedRow()

Takes parameter int sortedRow to return the unsorted row index of specified sorted row.

setAutoSort()

Takes parameter boolean autoSort to specify whether the data should be automatically sorted when it changes.

setKeyColumns()

Takes parameters int column, int[] keyColumns to set the key columns used to sort the table model when clicking on specified column.

setModel()

Takes parameter javax.swing.table.TableModel newModel to set the data model for this table to newModel and registers with for listener notifications from the new data model.

setTableHeader()

Takes parameter javax.swing.table.JTableHeader newHeader to overwrite the default implementation and add a MouseListener to the new table header.

sort()

Takes parameter int column to sort rows using the quicksort algorithm.

tableChanged(e)

Uses parameter avax.swing.event.TableModelEvent e to pass information about the event. Overrides super class method to check for a change in sorting.

unsort()

Restores the unsorted order.


9.5 Cell Renderers for JCSortableTable

Normally, you do not need to be concerned with the details of how table cells are rendered because renderers for most common cases have already been supplied. On the other hand, you may wish to use a custom renderer of your own design. While it is possible to use setDefaultRenderer() to set a cell renderer for a JTable, the method is not available for use with JCSortableTable. Instead, JClass uses its own powerful cell editor/renderer mechanism. This allows all JClass products to manage collections of JCCellRenderer types uniformly instead of having to manage the renderer types separately. To set your own cell renderer, use JClass Cell's EditorRendererRegistry, and implement one of the renderer interfaces. Please see the com.klg.jclass.cell API for details.


9.6 Examples

JCMappingSort example

JCMappingSort cannot be instantiated by calling its constructor. Instead, it has two static methods of the form:

The purpose of these two methods is to sort a mapping of indices instead of an array of objects. This is particularly useful when dealing with a Collection, or some form of data model where you reference a data element with an index. Your implementation of the JCIntComparator interface provides the implementation details for the objects you are sorting.

JCIntComparator should look like this:

public interface JCIntComparator {
public int compare(int index1, int index2);
}

The CollectionIntComparator is a specific implementation of JCIntComparator that can compare Collections. Sample code looks like this:

  public class CollectionComparator implements JCIntComparator {

  protected Collection collection;
  protected Comparator comparator;

  public CollectionComparator(Collection collection, Comparator comparator) {
this.collection = collection;
this.comparator = comparator;
}

  public CollectionComparator(Collection collection) {
this(collection, null);
}

  public int compare(int i1, int i2) {
Object a1 = collection.get(i1);
Object a2 = collection.get(i2);

if (comparator != null) {
// use comparator if provided
return comparator.compare(a1, a2);
}
else if (a1 instanceof Comparable) {
// items are comparable so get them to compare themselves
return ((Comparable) a1).compare(a2);
}
else {
// We have no comparator and the objects are not comparable
throw new IllegalArgumentException("Objects are not
  Comparable; please provide a Comparator with the constructor:
  CollectionComparator(Collection collection, Comparator comparator)");
}
}

}

JCSortableTable examples

One use of JCSortableTable is given in examples/elements/SortTable.

A full example based on SortTable.java follows. It demonstrates sorting on columnar data containing Strings, and two types of primitives: Boolean values and integers. The example provides its own implementation of JCRowComparator to perform a comparison between two rows in the table.

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Comparator;
import java.text.*;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.table.AbstractTableModel;
import javax.swing.BoxLayout;
import javax.swing.table.DefaultTableCellRenderer;

import com.klg.jclass.util.swing.JCExitFrame;
import com.klg.jclass.util.swing.JCSortableTable;
import com.klg.jclass.util.swing.DefaultRowSortTableModel;
import com.klg.jclass.util.swing.DefaultRowComparator;
import com.klg.jclass.util.swing.JCRowSortModel;
import com.klg.jclass.util.swing.JCComparableRow;

/**
* Sorting is allowed on these columns:
* "First Name", "Last Name", "Position", "Favorite Number", and
 * "Vegetarian"
*/
public class SortDateJCSortableTable extends JPanel implements
  ActionListener {

protected final static String[] names =
  {"First Name", "Last Name", "Position",
   "Favorite Number", "Vegetarian",
   "Calendar","GregorianCalendar"};

protected final static Object[][] data = {

    {"Diana", "Zukerman", "Research Officer",
    new Integer(1), new Boolean(false),"",""},
     {"Adam", "Petersen", "Consultant",
    new Integer(2), new Boolean(false),"",""},
    {"Mary", "Binfield", "Research Associate",
    new Integer(5), new Boolean(false),"",""},
    {"Michael", "Rizzo", "Research Fellow",
    new Integer(2), new Boolean(true),"",""},
    {"Ahmad", "Baldi", "Consultant",
    new Integer(3), new Boolean(false),"",""},
    {"Ian", "Clemente", "Research Fellow",
    new Integer(7), new Boolean(false),"",""},
     {"David", "Rubinstein", "Consultant",
    new Integer(4), new Boolean(false),"",""},
  };

protected JButton buttonUnsort = null;
protected JCSortableTable sortableTable = null;

/** Indicates that the first object is less than the second object.
   */
public static final int LESS_THAN = -1;
/** Indicates that the first object is equal to the second object. */
public static final int EQUAL = 0;
/** Indicates that the first object is greater than the second object. */
public static final int GREATER_THAN = 1;

public SortDateJCSortableTable() {
// Set a simple BoxLayout manager
setLayout(new BoxLayout(this,BoxLayout.X_AXIS));

//set up the calender values to be tested

for (int r =0 ; r < data[0].length ; r++){
Calendar c = Calendar.getInstance();
c.set(1998+r, r, 1);

GregorianCalendar gc = (GregorianCalendar)c;

data[r][5] = c;
data[r][6] = gc;
}
// Create and add the table
sortableTable = createTable();
add(new JScrollPane(sortableTable));

// Create and add an Unsort button for the table
buttonUnsort = new JButton("Unsort");
buttonUnsort.addActionListener(this);
add(buttonUnsort);
}

public static JCSortableTable createTable() {
  EditableTableModel model = new EditableTableModel();
   JCSortableTable table = new JCSortableTable();

// JCSortTable will do this anyway,
// but this way we have a member handle to it.
DefaultRowSortTableModel mRSmodel = new DefaultRowSortTableModel(model);
mRSmodel.setComparator(new MyComparator());

//set model and cast it down to the "DefaultRowSortTableModel"
table.setModel(mRSmodel);

  // We use the last name if the first name is the same.
int sort0[] = {0, 1};
table.setKeyColumns(0, sort0);

  // We use the first name if the last name is the same.
int sort1[] = {1, 0};
table.setKeyColumns(1, sort1);

  // We use person's name if the department is the same.
int sort2[] = {2, 0, 1};
table.setKeyColumns(2, sort2);

//set the non primitive renderers, no editor defined for this example
table.getColumn("GregorianCalendar").setCellRenderer(new CGFCalendarCellRenderer());
table.getColumn("Calendar").setCellRenderer(new CGFCalendarCellRenderer());

return table;
}

public static void main(String args[]) {
  JCExitFrame frame = new JCExitFrame("SortDateJCSortableTable Example");
  SortDateJCSortableTable app = new SortDateJCSortableTable();

  if (args.length > 0) {
    if (args[0].equals("windows")) {
      try {
  UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
  } catch (Exception e) {}
    }
  }

  frame.getContentPane().add(app);
  frame.setBounds(50, 50, 650, 350);
  frame.show();
}


//===== ActionListener interface method ========

public void actionPerformed(ActionEvent e) {
  if (e.getSource() instanceof JButton) {
  sortableTable.unsort();
}
}

public static class CGFCalendarCellRenderer
extends DefaultTableCellRenderer
{
protected java.text.DateFormat date_formatter =
java.text.DateFormat.getDateInstance();

public void setValue(Object o)
{
  String str = null;
  if (o instanceof String) {
    str = (String)o;
  }
  else if (o instanceof Calendar) {
    str = date_formatter.format(((Calendar)o).getTime());
  }
  else {
    str = o.toString();
  }
super.setValue(o == null ? "" : str);
}
}
private static class EditableTableModel extends AbstractTableModel {

EditableTableModel() {
  super();
}

public int getColumnCount() {
return names.length;
}

public int getRowCount() {
return data.length;
}

public Object getValueAt(int row, int col) {
return data[row][col];
}

public String getColumnName(int column) {
return names[column];
}

public Class getColumnClass(int col) {
return getValueAt(0,col).getClass();
}

  // Disallow edits on dates, and on favorite number
public boolean isCellEditable(int row, int col) {
return col != 3 && col != 5 && col != 6;
}

public void setValueAt(Object aValue, int row, int column) {
data[row][column] = aValue;
}
} // EditableTableModel

static class MyComparator extends DefaultRowComparator {

public MyComparator(){
super(JCRowSortModel.FORWARD);
}
public int compare(JCComparableRow row1, JCComparableRow row2) {
int[] kc = super.getKeyColumns();

  for (int i = 0; i < kc.length; i++) {
    Object o1 = row1.getValueAt(kc[i]);
    Object o2 = row2.getValueAt(kc[i]);

if(o1 instanceof Calendar && o2 instanceof Calendar) {
      Calendar c1 = (Calendar)o1;
      Calendar c2 = (Calendar)o2;

     if(c1.equals(c2)) {
        return EQUAL;
      }
      else if(c1.before(c2)) {
        return LESS_THAN;
      }
      else {
        return GREATER_THAN;
      }
    }
}
return super.compare(row1, row2);

}

}//MyComparator

} // SortDateJCSortableTable


PreviousNextIndex