Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: cor/gui/JspmTableSorter.java


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