1 package org.wcb.util;
2 /***
3 * Copyright (C) 1999 Walter Bogaardt
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18 *
19 * I basically modified the mouse adapter so that my table header renderer works.
20 * A sorter for TableModels. The sorter has a model (conforming to TableModel)
21 * and itself implements TableModel. TableSorter does not store or copy
22 * the data in the TableModel, instead it maintains an array of
23 * integers which it keeps the same size as the number of rows in its
24 * model. When the model changes it notifies the sorter that something
25 * has changed eg. "rowsAdded" so that its internal array of integers
26 * can be reallocated. As requests are made of the sorter (like
27 * getValueAt(row, col) it redirects them to its model via the mapping
28 * array. That way the TableSorter appears to hold another copy of the table
29 * with the rows in a different order. The sorting algorthm used is stable
30 * which means that it does not move around rows when its comparison
31 * function returns 0 to denote that they are equivalent.
32 */
33
34 import java.util.Vector;
35 import java.util.Date;
36 import org.wcb.util.SortButtonRenderer;
37
38 import javax.swing.table.TableModel;
39 import javax.swing.event.TableModelEvent;
40
41
42 import java.awt.event.MouseAdapter;
43 import java.awt.event.MouseEvent;
44 import java.awt.event.InputEvent;
45 import javax.swing.JTable;
46 import javax.swing.table.JTableHeader;
47 import javax.swing.table.TableColumnModel;
48
49 public class TableSorter extends TableMap
50 {
51 private int indexes[];
52 private Vector sortingColumns = new Vector();
53 private boolean ascending = true;
54 private int compares;
55
56
57 public TableSorter()
58 {
59 indexes = new int[0];
60 }
61
62 public TableSorter(TableModel model)
63 {
64 setModel(model);
65 }
66
67 public void setModel(TableModel model) {
68 super.setModel(model);
69 reallocateIndexes();
70 }
71
72 public int getMappingToRow(int row) throws ArrayIndexOutOfBoundsException
73 {
74 return indexes[row];
75 }
76
77 public int compareRowsByColumn(int row1, int row2, int column)
78 {
79
80 Class type = model.getColumnClass(column);
81 TableModel data = model;
82
83
84
85 Object o1 = data.getValueAt(row1, column);
86 Object o2 = data.getValueAt(row2, column);
87
88
89 if (o1 == null && o2 == null)
90 {
91 return 0;
92 }
93 else if (o1 == null)
94 {
95 return -1;
96 }
97 else if (o2 == null)
98 {
99 return 1;
100 }
101
102
103
104
105
106
107
108 if (type.getSuperclass() == java.lang.Number.class)
109 {
110 Number n1 = (Number) data.getValueAt(row1, column);
111 double d1 = n1.doubleValue();
112 Number n2 = (Number) data.getValueAt(row2, column);
113 double d2 = n2.doubleValue();
114
115 if (d1 < d2)
116 return -1;
117 else if (d1 > d2)
118 return 1;
119 else
120 return 0;
121 }
122 else if (type == java.util.Date.class)
123 {
124 Date d1 = (Date)data.getValueAt(row1, column);
125 long n1 = d1.getTime();
126 Date d2 = (Date)data.getValueAt(row2, column);
127 long n2 = d2.getTime();
128
129 if (n1 < n2)
130 {
131 return -1;
132 }
133 else if (n1 > n2)
134 {
135 return 1;
136 }
137 else
138 {
139 return 0;
140 }
141 }
142 else if (type == String.class)
143 {
144 String s1 = (String)data.getValueAt(row1, column);
145 String s2 = (String)data.getValueAt(row2, column);
146 int result = s1.compareTo(s2);
147
148 if (result < 0)
149 {
150 return -1;
151 }
152 else if (result > 0)
153 {
154 return 1;
155 }
156 else
157 {
158 return 0;
159 }
160 }
161 else if (type == Boolean.class)
162 {
163 Boolean bool1 = (Boolean)data.getValueAt(row1, column);
164 boolean b1 = bool1.booleanValue();
165 Boolean bool2 = (Boolean)data.getValueAt(row2, column);
166 boolean b2 = bool2.booleanValue();
167
168 if (b1 == b2)
169 {
170 return 0;
171 }
172 else if (b1) {
173
174 return 1;
175 }
176 else
177 {
178 return -1;
179 }
180 }
181 else
182 {
183 Object v1 = data.getValueAt(row1, column);
184 String s1 = v1.toString();
185 Object v2 = data.getValueAt(row2, column);
186 String s2 = v2.toString();
187 int result = s1.compareTo(s2);
188
189 if (result < 0)
190 {
191 return -1;
192 }
193 else if (result > 0)
194 {
195 return 1;
196 }
197 else
198 {
199 return 0;
200 }
201 }
202 }
203
204 public int compare(int row1, int row2)
205 {
206 compares++;
207
208 for (int level = 0; level < sortingColumns.size(); level++)
209 {
210 Integer column = (Integer) sortingColumns.elementAt(level);
211
212 int result = compareRowsByColumn(row1, row2, column.intValue());
213 if (result != 0)
214 {
215 return ascending ? result : -result;
216 }
217 }
218 return 0;
219 }
220
221 public void reallocateIndexes()
222 {
223 int rowCount = model.getRowCount();
224
225
226
227 indexes = new int[rowCount];
228
229
230 for(int row = 0; row < rowCount; row++)
231 {
232 indexes[row] = row;
233 }
234 }
235
236 public void tableChanged(TableModelEvent e)
237 {
238 reallocateIndexes();
239
240 super.tableChanged(e);
241 }
242
243 public void checkModel()
244 {
245 if (indexes.length != model.getRowCount())
246 {
247 System.err.println("Sorter not informed of a change in model.");
248 }
249 }
250
251 public void sort(Object sender)
252 {
253 checkModel();
254
255 compares = 0;
256
257
258 shuttlesort((int[])indexes.clone(), indexes, 0, indexes.length);
259 }
260
261 public void n2sort() {
262 for (int i = 0; i < getRowCount(); i++)
263 {
264 for (int j = i + 1; j < getRowCount(); j++)
265 {
266 if (compare(indexes[i], indexes[j]) == -1)
267 {
268 swap(i, j);
269 }
270 }
271 }
272 }
273
274
275
276
277
278
279
280
281 public void shuttlesort(int from[], int to[], int low, int high) {
282 if (high - low < 2) {
283 return;
284 }
285 int middle = (low + high)/2;
286 shuttlesort(to, from, low, middle);
287 shuttlesort(to, from, middle, high);
288
289 int p = low;
290 int q = middle;
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307 if (high - low >= 4 && compare(from[middle-1], from[middle]) <= 0)
308 {
309 for (int i = low; i < high; i++)
310 {
311 to[i] = from[i];
312 }
313 return;
314 }
315
316
317
318 for(int i = low; i < high; i++)
319 {
320 if (q >= high || (p < middle && compare(from[p], from[q]) <= 0))
321 {
322 to[i] = from[p++];
323 }
324 else {
325 to[i] = from[q++];
326 }
327 }
328 }
329
330 public void swap(int i, int j) {
331 int tmp = indexes[i];
332 indexes[i] = indexes[j];
333 indexes[j] = tmp;
334 }
335
336
337
338
339 public Object getValueAt(int aRow, int aColumn)
340 {
341 checkModel();
342 return model.getValueAt(indexes[aRow], aColumn);
343 }
344
345 public void setValueAt(Object aValue, int aRow, int aColumn)
346 {
347 checkModel();
348 model.setValueAt(aValue, indexes[aRow], aColumn);
349 }
350
351 public void sortByColumn(int column) {
352 sortByColumn(column, true);
353 }
354
355 public void sortByColumn(int column, boolean ascending) {
356 this.ascending = ascending;
357 sortingColumns.removeAllElements();
358 sortingColumns.addElement(new Integer(column));
359 sort(this);
360 super.tableChanged(new TableModelEvent(this));
361 }
362
363
364
365
366 public void addMouseListenerToHeaderInTable(JTable table) {
367 final TableSorter sorter = this;
368 final JTable tableView = table;
369 tableView.setColumnSelectionAllowed(false);
370 MouseAdapter listMouseListener = new MouseAdapter() {
371 public void mouseClicked(MouseEvent e) {
372 TableColumnModel columnModel = tableView.getColumnModel();
373 int viewColumn = columnModel.getColumnIndexAtX(e.getX());
374 int column = tableView.convertColumnIndexToModel(viewColumn);
375 if(e.getClickCount() == 1 && column != -1)
376 {
377 int shiftPressed = e.getModifiers()&InputEvent.SHIFT_MASK;
378 boolean ascending = (shiftPressed == 0);
379 sorter.sortByColumn(column, ascending);
380 }
381 }
382 };
383 JTableHeader th = tableView.getTableHeader();
384 th.addMouseListener(listMouseListener);
385 }
386
387
388
389
390
391
392
393 public void addMouseListenerToHeaderInTable(JTable table, SortButtonRenderer headRenderer) {
394 final TableSorter sorter = this;
395 final JTable tableView = table;
396 final SortButtonRenderer hRenderer = headRenderer;
397
398 tableView.setColumnSelectionAllowed(false);
399 MouseAdapter listMouseListener = new MouseAdapter() {
400 public void mousePressed(MouseEvent e) {
401 int col = tableView.getTableHeader().columnAtPoint(e.getPoint());
402 tableView.convertColumnIndexToModel(col);
403 hRenderer.setPressedColumn(col);
404 hRenderer.setSelectedColumn(col);
405 tableView.getTableHeader().repaint();
406
407 if (tableView.isEditing())
408 {
409 tableView.getCellEditor().stopCellEditing();
410 }
411
412 boolean ascending;
413 if (SortButtonRenderer.DOWN == hRenderer.getState(col))
414 {
415 ascending = true;
416 } else
417 {
418 ascending = false;
419 }
420 sorter.sortByColumn(col, ascending);
421 }
422
423 public void mouseReleased(MouseEvent e) {
424 tableView.getTableHeader().columnAtPoint(e.getPoint());
425 hRenderer.setPressedColumn(-1);
426 tableView.getTableHeader().repaint();
427 }
428 };
429 JTableHeader th = tableView.getTableHeader();
430 th.addMouseListener(listMouseListener);
431 }
432 }
433
434
435
436
437
438
439