1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 /***
32 * A sorter for TableModels. The sorter has a model (conforming to TableModel)
33 * and itself implements TableModel. TableSorter does not store or copy
34 * the data in the TableModel, instead it maintains an array of
35 * integers which it keeps the same size as the number of rows in its
36 * model. When the model changes it notifies the sorter that something
37 * has changed eg. "rowsAdded" so that its internal array of integers
38 * can be reallocated. As requests are made of the sorter (like
39 * getValueAt(row, col) it redirects them to its model via the mapping
40 * array. That way the TableSorter appears to hold another copy of the table
41 * with the rows in a different order. The sorting algorthm used is stable
42 * which means that it does not move around rows when its comparison
43 * function returns 0 to denote that they are equivalent.
44 *
45 * @version 1.8 04/23/99
46 * @author Philip Milne
47 */
48 package org.xnap.gui.table;
49
50 import java.util.Vector;
51
52 import javax.swing.event.TableModelEvent;
53 import javax.swing.table.AbstractTableModel;
54
55 public abstract class AbstractSortableTableModel
56 extends AbstractTableModel implements SortableModel
57 {
58
59
60
61 protected int indexes[] = new int[0];
62 protected int revIndexes[] = new int[0];
63 /***
64 * All columns to be sorted by.
65 */
66 protected Vector sortingColumns = new Vector();
67 protected boolean ascending = true;
68 /***
69 * Counts number of compares.
70 */
71 protected int compares;
72 protected int lastSortedColumn = -1;
73 protected boolean maintainSortOrder;
74
75
76
77 public AbstractSortableTableModel(boolean maintainSortOrder)
78 {
79 this.maintainSortOrder = maintainSortOrder;
80 }
81
82 public AbstractSortableTableModel()
83 {
84 this(false);
85 }
86
87
88
89 /***
90 * We need to mangle these events to fire the right indicies.
91 */
92 public void fireTableChanged(TableModelEvent e)
93 {
94 reallocateIndexes(e);
95
96 if (maintainSortOrder) {
97
98 if (sort()) {
99
100 return;
101 }
102 }
103
104 if (e.getType() == TableModelEvent.DELETE
105 || (e.getType() == TableModelEvent.UPDATE &&
106 e.getLastRow() >= revIndexes.length)) {
107
108 super.fireTableChanged(new TableModelEvent(this));
109 }
110 else {
111 for (int i = e.getFirstRow(); i <= e.getLastRow(); i++) {
112 TableModelEvent t = new TableModelEvent
113 (this, revIndexes[i], revIndexes[i], e.getColumn(),
114 e.getType());
115 super.fireTableChanged(t);
116 }
117 }
118 }
119
120 public abstract Object get(int aRow, int aColumn);
121
122 public int getSortedColumn()
123 {
124 return lastSortedColumn;
125 }
126
127 /***
128 * The mapping only affects the contents of the data rows.
129 * Pass all requests to these rows through the mapping array: "indexes".
130 */
131 public Object getValueAt(int aRow, int aColumn)
132 {
133 synchronized (indexes) {
134 return get(indexes[aRow], aColumn);
135 }
136 }
137
138 public boolean isSortedAscending()
139 {
140 return ascending;
141 }
142
143 public boolean isCellEditable(int row, int column)
144 {
145 return false;
146 }
147
148 /***
149 * Returns the mapped row index.
150 *
151 * @deprecated
152 */
153 public int mapToDtmIndex(int i)
154 {
155 return mapToIndex(i);
156 }
157
158 /***
159 * Returns the mapped row index.
160 */
161 public int mapToIndex(int i)
162 {
163 return indexes[i];
164 }
165
166 public void set(Object aValue, int aRow, int aColumn)
167 {
168 }
169
170 public void setMaintainSortOrder(boolean newValue)
171 {
172 maintainSortOrder = newValue;
173 }
174
175 public void setSortedAscending(boolean newValue)
176 {
177 ascending = newValue;
178 }
179
180 public void setValueAt(Object aValue, int aRow, int aColumn)
181 {
182 synchronized (indexes) {
183 set(aValue, indexes[aRow], aColumn);
184 }
185 }
186
187 /***
188 * Sorts the table.
189 *
190 * @return true, if table is sorted ascending; false, if descending
191 */
192 public boolean sortByColumn(int column, boolean ascending, boolean revert)
193 {
194
195 if (column < 0 || column >= getColumnCount()) {
196 throw new IndexOutOfBoundsException("Column is invalid");
197 }
198
199 sortingColumns.clear();
200 sortingColumns.add(new Integer(column));
201
202 this.ascending = ascending;
203
204 if (!sort() && revert) {
205 this.ascending = !ascending;
206 sort();
207 }
208 lastSortedColumn = column;
209
210 return ascending;
211 }
212
213 public void resort()
214 {
215 if (lastSortedColumn != -1) {
216 sortByColumn(lastSortedColumn, ascending, false);
217 }
218 }
219
220 /***
221 * Compares two rows.
222 */
223 protected int compare(int row1, int row2)
224 {
225 compares++;
226 for(int level = 0; level < sortingColumns.size(); level++) {
227 Integer column = (Integer)sortingColumns.elementAt(level);
228 int result = compareRowsByColumn(row1, row2, column.intValue());
229 if (result != 0)
230 return ascending ? result : -result;
231 }
232 return 0;
233 }
234
235 /***
236 * Compares two rows by a column.
237 */
238 protected int compareRowsByColumn(int row1, int row2, int column)
239 {
240 Class type = getColumnClass(column);
241
242
243 Object o1 = get(row1, column);
244 Object o2 = get(row2, column);
245
246
247
248 if (o1 == null && o2 == null) {
249 return 0;
250 } else if (o1 == null) {
251 return -1;
252 } else if (o2 == null) {
253 return 1;
254 }
255
256
257
258
259
260
261
262 if (type.equals(String.class)) {
263 String s1 = (String)o1;
264 String s2 = (String)o2;
265 int result = s1.compareToIgnoreCase(s2);
266
267
268 if (s1.length() == 0 ^ s2.length() == 0) {
269 result = -result;
270 }
271
272 return result;
273 }
274 else if (o1 instanceof Comparable) {
275 return ((Comparable)o1).compareTo(o2);
276 }
277 else if (type == Boolean.class) {
278 boolean b1 = ((Boolean)o1).booleanValue();
279 boolean b2 = ((Boolean)o2).booleanValue();
280
281
282 if (b1 == b2) {
283 return 0;
284 } else if (b1) {
285 return 1;
286 } else {
287 return -1;
288 }
289 }
290 else {
291
292 return o1.toString().compareTo(o2.toString());
293 }
294 }
295
296 protected void reallocateIndexes(TableModelEvent e)
297 {
298 int rowCount = getRowCount();
299
300 if (rowCount == indexes.length)
301 return;
302
303
304
305 int newIndexes[] = new int[rowCount];
306 int newRevIndexes[] = new int[rowCount];
307
308 synchronized (indexes) {
309 int row = 0;
310
311 if (e != null && e.getType() == TableModelEvent.DELETE) {
312
313
314
315 int skipped = 0;
316 for(; row < indexes.length; row++) {
317 int dec = 0;
318 boolean skip = false;
319 for (int i = e.getFirstRow(); i <= e.getLastRow(); i++) {
320 if (i < indexes[row])
321 dec++;
322 else if (i == indexes[row])
323 skip = true;
324 }
325 if (skip) {
326 skipped++;
327 continue;
328 }
329
330 newIndexes[row - skipped] = indexes[row] - dec;
331 newRevIndexes[indexes[row] - dec] = row - skipped;
332 }
333 }
334 else if (e != null && (e.getType() == TableModelEvent.UPDATE
335 && e.getLastRow() >= indexes.length)) {
336
337 for (int i = 0; i < rowCount; i++) {
338 newIndexes[i] = i;
339 newRevIndexes[i] = i;
340 }
341 }
342 else {
343
344
345 for(; row < indexes.length && row < rowCount; row++) {
346 newIndexes[row] = indexes[row];
347 newRevIndexes[row] = revIndexes[row];
348 }
349 for(; row < rowCount; row++) {
350 newIndexes[row] = row;
351 newRevIndexes[row] = row;
352 }
353
354 }
355
356 indexes = newIndexes;
357 revIndexes = newRevIndexes;
358 }
359 }
360
361 protected void reallocateIndexes()
362 {
363 reallocateIndexes(null);
364 }
365
366 /***
367 * Returns false if nothing has changed.
368 */
369 protected boolean sort()
370 {
371 synchronized (indexes) {
372 compares = 0;
373
374
375
376 int[] oldIndexes = new int[indexes.length];
377 System.arraycopy(indexes, 0, oldIndexes, 0, indexes.length);
378
379 if (shuttlesort(oldIndexes, indexes, 0, indexes.length)) {
380
381
382
383
384
385
386 for(int i = 0; i < indexes.length; i++) {
387 revIndexes[indexes[i]] = i;
388 }
389
390 super.fireTableChanged(new TableModelEvent(this));
391
392 return true;
393 }
394 else {
395 return false;
396 }
397 }
398 }
399
400 /***
401 * Returns false if nothing has changed.
402 */
403
404
405
406
407
408
409
410 public boolean shuttlesort(int from[], int to[], int low, int high)
411 {
412 if (high - low < 2) {
413 return false;
414 }
415
416 boolean changed = false;
417 int middle = (low + high) / 2;
418 changed |= shuttlesort(to, from, low, middle);
419 changed |= shuttlesort(to, from, middle, high);
420
421 int p = low;
422 int q = middle;
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439 if (high - low >= 4) {
440 if (compare(from[middle-1], from[middle]) <= 0) {
441 for (int i = low; i < high; i++) {
442 to[i] = from[i];
443 }
444 return changed;
445 } else {
446
447 if (compare(from[high-1], from[low]) < 0) {
448 int i = low;
449 for( ; q < high; q++) {
450 to[i++] = from[q];
451 }
452 for( ; i < high; i++) {
453 to[i] = from[p++];
454 }
455 return changed;
456 }
457 }
458 }
459
460
461 for (int i = low; i < high; i++) {
462 if (q >= high || (p < middle && compare(from[p], from[q]) <= 0)) {
463 to[i] = from[p++];
464 }
465 else {
466 changed |= (p < middle);
467 to[i] = from[q++];
468 }
469 }
470
471 return changed;
472 }
473
474
475
476
477
478 public void n2sort() {
479 for(int i = 0; i < getRowCount(); i++) {
480 for(int j = i+1; j < getRowCount(); j++) {
481 if (compare(indexes[i], indexes[j]) == -1) {
482 swap(i, j);
483 }
484 }
485 }
486 }
487
488 protected void swap(int i, int j) {
489 int tmp = indexes[i];
490 indexes[i] = indexes[j];
491 indexes[j] = tmp;
492 }
493
494 }