001package ca.bc.webarts.widgets; 002 003/******************************************************************************** 004 * 005 * SortableTableModel Class 006 * Author : Amir Kost 007 * Purpose : A TableModel that enables column sorting by clicking the column 008 * 009 * This class extends the DefaultTableModel class and enables sorting of columns 010 * It sorts the TableModel's dataVector whenever a column is clicked. 011 * Clicking the same column again causes it to be sorted in reverse order. 012 * The sort is based on Java's Collection class sort method. 013 * 014 * Usage : 015 * 016 * SortableTableModel model = new SortableTableModel(); 017 * JTable table = new JTable(model); 018 * model.addMouseListenerToHeaderInTable(table); 019 * 020 *******************************************************************************/ 021 022import java.util.Vector; 023import java.util.Comparator; 024import java.util.Date; 025import java.util.Collections; 026 027import javax.swing.table.TableModel; 028import javax.swing.table.DefaultTableModel; 029import javax.swing.table.TableColumnModel; 030import javax.swing.table.JTableHeader; 031import javax.swing.event.TableModelEvent; 032import java.awt.event.MouseAdapter; 033import java.awt.event.MouseEvent; 034import java.awt.event.InputEvent; 035import javax.swing.JTable; 036 037public class SortableTableModel extends DefaultTableModel implements Comparator{ 038 039 protected int currCol; 040 protected Vector ascendCol; // this vector stores the state (ascending or descending) of each column 041 protected Integer one = new Integer(1); 042 protected Integer minusOne = new Integer(-1); 043 044 public SortableTableModel() { 045 super(); 046 ascendCol = new Vector(); 047 } 048 049 public SortableTableModel(DefaultTableModel copyModel) { 050 super(); 051 ascendCol = new Vector(); 052 if (copyModel!=null) 053 { 054 Vector columnData = new Vector(); 055 Vector data = new Vector(); // vector of vectors 056 for (int i=0;i< copyModel.getColumnCount() ; i++) 057 { 058 columnData.removeAllElements(); 059 for (int j = 0; j< copyModel.getRowCount(); j++) 060 columnData.add(copyModel.getValueAt(j,i)); 061 super.addColumn(copyModel.getColumnName(i), columnData); 062 ascendCol.add(one); 063 } 064 065 } 066 } 067 068 /******************************************************************* 069 * addColumn methods are inherited from the DefaultTableModel class. 070 *******************************************************************/ 071 072 public void addColumn(Object columnName) { 073 super.addColumn(columnName); 074 ascendCol.add(one); 075 } 076 077 public void addColumn(Object columnName, Object[] columnData) { 078 super.addColumn(columnName, columnData); 079 ascendCol.add(one); 080 } 081 082 public void addColumn(Object columnName, Vector columnData) { 083 super.addColumn(columnName, columnData); 084 ascendCol.add(one); 085 } 086 087 /***************************************************************** 088 * This method is the implementation of the Comparator interface. 089 * It is used for sorting the rows 090 *****************************************************************/ 091 public int compare(Object v1, Object v2) { 092 093 // the comparison is between 2 vectors, each representing a row 094 // the comparison is done between 2 objects from the different rows that are in the column that is being sorted 095 096 int ascending = ((Integer) ascendCol.get(currCol)).intValue(); 097 if (v1 == null && v2 == null) { 098 return 0; 099 } else if (v2 == null) { // Define null less than everything. 100 return 1 * ascending; 101 } else if (v1 == null) { 102 return -1 * ascending; 103 } 104 105 Object o1 = ((Vector) v1).get(currCol); 106 Object o2 = ((Vector) v2).get(currCol); 107 108 // If both values are null, return 0. 109 if (o1 == null && o2 == null) { 110 return 0; 111 } else if (o2 == null) { // Define null less than everything. 112 return 1 * ascending; 113 } else if (o1 == null) { 114 return -1 * ascending; 115 } 116 117 if (o1 instanceof Number && o2 instanceof Number) { 118 Number n1 = (Number) o1; 119 double d1 = n1.doubleValue(); 120 Number n2 = (Number) o2; 121 double d2 = n2.doubleValue(); 122 123 if (d1 == d2) { 124 return 0; 125 } else if (d1 > d2) { 126 return 1 * ascending; 127 } else { 128 return -1 * ascending; 129 } 130 131 } else if (o1 instanceof Boolean && o2 instanceof Boolean) { 132 Boolean bool1 = (Boolean) o1; 133 boolean b1 = bool1.booleanValue(); 134 Boolean bool2 = (Boolean) o2; 135 boolean b2 = bool2.booleanValue(); 136 137 if (b1 == b2) { 138 return 0; 139 } else if (b1) { 140 return 1 * ascending; 141 } else { 142 return -1 * ascending; 143 } 144 145 } else { 146 // default case 147 if (o1 instanceof Comparable && o2 instanceof Comparable) { 148 Comparable c1 = (Comparable) o1; 149 Comparable c2 = (Comparable) o2; // superflous cast, no need for it! 150 151 try { 152 return c1.compareTo(c2) * ascending; 153 } catch (ClassCastException cce) { 154 // forget it... we'll deal with them like 2 normal objects below. 155 } 156 } 157 158 String s1 = o1.toString(); 159 String s2 = o2.toString(); 160 return s1.compareTo(s2) * ascending; 161 } 162 } 163 164 /*************************************************************************** 165 * This method sorts the rows using Java's Collections class. 166 * After sorting, it changes the state of the column - 167 * if the column was ascending, its new state is descending, and vice versa. 168 ***************************************************************************/ 169 public void sort() { 170 Collections.sort(dataVector, this); 171 Integer val = (Integer) ascendCol.get(currCol); 172 ascendCol.remove(currCol); 173 if(val.equals(one)) // change the state of the column 174 ascendCol.add(currCol, minusOne); 175 else 176 ascendCol.add(currCol, one); 177 } 178 179 public void sortByColumn(int column) { 180 this.currCol = column; 181 sort(); 182 fireTableChanged(new TableModelEvent(this)); 183 } 184 185 // Add a mouse listener to the Table to trigger a table sort 186 // when a column heading is clicked in the JTable. 187 public void addMouseListenerToHeaderInTable(JTable table) { 188 final SortableTableModel sorter = this; 189 final JTable tableView = table; 190 tableView.setColumnSelectionAllowed(false); 191 MouseAdapter listMouseListener = new MouseAdapter() { 192 public void mouseClicked(MouseEvent e) { 193 TableColumnModel columnModel = tableView.getColumnModel(); 194 int viewColumn = columnModel.getColumnIndexAtX(e.getX()); 195 int column = tableView.convertColumnIndexToModel(viewColumn); 196 if (e.getClickCount() == 1 && column != -1) { 197 int shiftPressed = e.getModifiers()&InputEvent.SHIFT_MASK; 198 boolean ascending = (shiftPressed == 0); 199 sorter.sortByColumn(column); 200 } 201 } 202 }; 203 JTableHeader th = tableView.getTableHeader(); 204 th.addMouseListener(listMouseListener); 205 } 206 207} 208