View Javadoc
1 /* 2 * ==================================================================== 3 * 4 * This code is subject to the freebxml License, Version 1.1 5 * 6 * Copyright (c) 2003 freebxml.org. All rights reserved. 7 * 8 * $Header: /cvsroot/ebxmlrr/jaxr/src/com/sun/xml/registry/client/browser/TableSorter.java,v 1.2 2003/09/10 17:18:16 farrukh_najmi Exp $ 9 * ==================================================================== 10 */ 11 package com.sun.xml.registry.client.browser; 12 13 import java.util.*; 14 15 import javax.swing.table.TableModel; 16 import javax.swing.event.TableModelEvent; 17 18 // Imports for picking up mouse events from the JTable. 19 20 import java.awt.event.MouseAdapter; 21 import java.awt.event.MouseEvent; 22 import java.awt.event.InputEvent; 23 import javax.swing.JTable; 24 import javax.swing.table.JTableHeader; 25 import javax.swing.table.TableColumnModel; 26 27 import com.sun.xml.registry.client.browser.RegistryObjectsTable; 28 29 /*** 30 * A sorter for TableModels. The sorter has a model (conforming to TableModel) 31 * and itself implements TableModel. TableSorter does not store or copy 32 * the data in the TableModel, instead it maintains an array of 33 * integers which it keeps the same size as the number of rows in its 34 * model. When the model changes it notifies the sorter that something 35 * has changed eg. "rowsAdded" so that its internal array of integers 36 * can be reallocated. As requests are made of the sorter (like 37 * getValueAt(row, col) it redirects them to its model via the mapping 38 * array. That way the TableSorter appears to hold another copy of the table 39 * with the rows in a different order. The sorting algorthm used is stable 40 * which means that it does not move around rows when its comparison 41 * function returns 0 to denote that they are equivalent. 42 * 43 * @version 1.5 12/17/97 44 * @author Philip Milne 45 */ 46 public class TableSorter extends TableMap { 47 int indexes[]; 48 Vector sortingColumns = new Vector(); 49 boolean ascending = true; 50 int compares; 51 52 public TableSorter() { 53 indexes = new int[0]; // for consistency 54 } 55 56 public TableSorter(TableModel model) { 57 setModel(model); 58 } 59 60 public void setModel(TableModel model) { 61 super.setModel(model); 62 reallocateIndexes(); 63 } 64 65 public int compareRowsByColumn(int row1, int row2, int column) { 66 Class type = model.getColumnClass(column); 67 TableModel data = model; 68 69 // Check for nulls. 70 71 Object o1 = RegistryObjectsTable.convertValue(data.getValueAt(row1, column)); 72 Object o2 = RegistryObjectsTable.convertValue(data.getValueAt(row2, column)); 73 74 // If both values are null, return 0. 75 if (o1 == null && o2 == null) { 76 return 0; 77 } else if (o1 == null) { // Define null less than everything. 78 return -1; 79 } else if (o2 == null) { 80 return 1; 81 } 82 83 /* 84 * We copy all returned values from the getValue call in case 85 * an optimised model is reusing one object to return many 86 * values. The Number subclasses in the JDK are immutable and 87 * so will not be used in this way but other subclasses of 88 * Number might want to do this to save space and avoid 89 * unnecessary heap allocation. 90 */ 91 92 if (type.getSuperclass() == java.lang.Number.class) { 93 Number n1 = (Number)o1; 94 double d1 = n1.doubleValue(); 95 Number n2 = (Number)o2; 96 double d2 = n2.doubleValue(); 97 98 if (d1 < d2) { 99 return -1; 100 } else if (d1 > d2) { 101 return 1; 102 } else { 103 return 0; 104 } 105 } else if (type == java.util.Date.class) { 106 Date d1 = (Date)o1; 107 long n1 = d1.getTime(); 108 Date d2 = (Date)o2; 109 long n2 = d2.getTime(); 110 111 if (n1 < n2) { 112 return -1; 113 } else if (n1 > n2) { 114 return 1; 115 } else { 116 return 0; 117 } 118 } else if (type == String.class) { 119 String s1 = (String)o1; 120 String s2 = (String)o2; 121 int result = s1.compareTo(s2); 122 123 if (result < 0) { 124 return -1; 125 } else if (result > 0) { 126 return 1; 127 } else { 128 return 0; 129 } 130 } else if (type == Boolean.class) { 131 Boolean bool1 = (Boolean)o1; 132 boolean b1 = bool1.booleanValue(); 133 Boolean bool2 = (Boolean)o2; 134 boolean b2 = bool2.booleanValue(); 135 136 if (b1 == b2) { 137 return 0; 138 } else if (b1) { // Define false < true 139 return 1; 140 } else { 141 return -1; 142 } 143 } else { 144 String s1 = o1.toString(); 145 String s2 = o2.toString(); 146 int result = s1.compareTo(s2); 147 148 if (result < 0) { 149 return -1; 150 } else if (result > 0) { 151 return 1; 152 } else { 153 return 0; 154 } 155 } 156 } 157 158 public int compare(int row1, int row2) { 159 compares++; 160 for (int level = 0; level < sortingColumns.size(); level++) { 161 Integer column = (Integer)sortingColumns.elementAt(level); 162 int result = compareRowsByColumn(row1, row2, column.intValue()); 163 if (result != 0) { 164 return ascending ? result : -result; 165 } 166 } 167 return 0; 168 } 169 170 public void reallocateIndexes() { 171 int rowCount = model.getRowCount(); 172 173 // Set up a new array of indexes with the right number of elements 174 // for the new data model. 175 indexes = new int[rowCount]; 176 177 // Initialise with the identity mapping. 178 for (int row = 0; row < rowCount; row++) { 179 indexes[row] = row; 180 } 181 } 182 183 public void tableChanged(TableModelEvent e) { 184 //System.out.println("Sorter: tableChanged"); 185 reallocateIndexes(); 186 187 super.tableChanged(e); 188 } 189 190 public void checkModel() { 191 if (indexes.length != model.getRowCount()) { 192 System.err.println("Sorter not informed of a change in model."); 193 } 194 } 195 196 public void sort(Object sender) { 197 checkModel(); 198 199 compares = 0; 200 // n2sort(); 201 // qsort(0, indexes.length-1); 202 shuttlesort((int[])indexes.clone(), indexes, 0, indexes.length); 203 //System.out.println("Compares: "+compares); 204 } 205 206 public void n2sort() { 207 for (int i = 0; i < getRowCount(); i++) { 208 for (int j = i+1; j < getRowCount(); j++) { 209 if (compare(indexes[i], indexes[j]) == -1) { 210 swap(i, j); 211 } 212 } 213 } 214 } 215 216 // This is a home-grown implementation which we have not had time 217 // to research - it may perform poorly in some circumstances. It 218 // requires twice the space of an in-place algorithm and makes 219 // NlogN assigments shuttling the values between the two 220 // arrays. The number of compares appears to vary between N-1 and 221 // NlogN depending on the initial order but the main reason for 222 // using it here is that, unlike qsort, it is stable. 223 public void shuttlesort(int from[], int to[], int low, int high) { 224 if (high - low < 2) { 225 return; 226 } 227 int middle = (low + high)/2; 228 shuttlesort(to, from, low, middle); 229 shuttlesort(to, from, middle, high); 230 231 int p = low; 232 int q = middle; 233 234 /* This is an optional short-cut; at each recursive call, 235 check to see if the elements in this subset are already 236 ordered. If so, no further comparisons are needed; the 237 sub-array can just be copied. The array must be copied rather 238 than assigned otherwise sister calls in the recursion might 239 get out of sinc. When the number of elements is three they 240 are partitioned so that the first set, [low, mid), has one 241 element and and the second, [mid, high), has two. We skip the 242 optimisation when the number of elements is three or less as 243 the first compare in the normal merge will produce the same 244 sequence of steps. This optimisation seems to be worthwhile 245 for partially ordered lists but some analysis is needed to 246 find out how the performance drops to Nlog(N) as the initial 247 order diminishes - it may drop very quickly. */ 248 249 if (high - low >= 4 && compare(from[middle-1], from[middle]) <= 0) { 250 for (int i = low; i < high; i++) { 251 to[i] = from[i]; 252 } 253 return; 254 } 255 256 // A normal merge. 257 258 for (int i = low; i < high; i++) { 259 if (q >= high || (p < middle && compare(from[p], from[q]) <= 0)) { 260 to[i] = from[p++]; 261 } 262 else { 263 to[i] = from[q++]; 264 } 265 } 266 } 267 268 public void swap(int i, int j) { 269 int tmp = indexes[i]; 270 indexes[i] = indexes[j]; 271 indexes[j] = tmp; 272 } 273 274 // The mapping only affects the contents of the data rows. 275 // Pass all requests to these rows through the mapping array: "indexes". 276 277 public Object getValueAt(int aRow, int aColumn) { 278 checkModel(); 279 return model.getValueAt(indexes[aRow], aColumn); 280 } 281 282 public void setValueAt(Object aValue, int aRow, int aColumn) { 283 checkModel(); 284 model.setValueAt(aValue, indexes[aRow], aColumn); 285 } 286 287 public void sortByColumn(int column) { 288 sortByColumn(column, true); 289 } 290 291 public void sortByColumn(int column, boolean ascending) { 292 this.ascending = ascending; 293 sortingColumns.removeAllElements(); 294 sortingColumns.addElement(new Integer(column)); 295 sort(this); 296 super.tableChanged(new TableModelEvent(this)); 297 } 298 299 // There is no-where else to put this. 300 // Add a mouse listener to the Table to trigger a table sort 301 // when a column heading is clicked in the JTable. 302 public void addMouseListenerToHeaderInTable(JTable table) { 303 final TableSorter sorter = this; 304 final JTable tableView = table; 305 tableView.setColumnSelectionAllowed(false); 306 MouseAdapter listMouseListener = new MouseAdapter() { 307 public void mouseClicked(MouseEvent e) { 308 TableColumnModel columnModel = tableView.getColumnModel(); 309 int viewColumn = columnModel.getColumnIndexAtX(e.getX()); 310 int column = tableView.convertColumnIndexToModel(viewColumn); 311 if (e.getClickCount() == 1 && column != -1) { 312 //System.out.println("Sorting ..."); 313 int shiftPressed = e.getModifiers()&InputEvent.SHIFT_MASK; 314 boolean ascending = (shiftPressed == 0); 315 sorter.sortByColumn(column, ascending); 316 } 317 } 318 }; 319 JTableHeader th = tableView.getTableHeader(); 320 th.addMouseListener(listMouseListener); 321 } 322 }

This page was automatically generated by Maven