source: trunk/java-client/org/nzdl/gsdl/SimpleGraphicalClient/TableSorter.java@ 2225

Last change on this file since 2225 was 2225, checked in by daven, 23 years ago

added QueryHistory, tooltips on collectionInfoButton. Altered
CSModel to retain CollectionInfo objects and longCollectionNames
for performance. Added files for a BerryBasket - to be turned on
soon. Added sorting of the QueryHistory via the TableMap and TableSorter
files - from the Java Swing tutorial - not sure about GPL status
of these 2.

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