KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > lucane > applications > jmail > base > SortableTable


1 package org.lucane.applications.jmail.base;
2
3 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
4  * This file is part of JMail *
5  * Copyright (C) 2002-2003 Yvan Norsa <norsay@wanadoo.fr> *
6  * *
7  * JMail is free software; you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation; either version 2 of the License, or *
10  * any later version. *
11  * *
12  * JMail is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License along *
18  * with JMail; if not, write to the Free Software Foundation, Inc., *
19  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
20  * *
21  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

22
23 import java.awt.event.*;
24 import java.text.*;
25 import java.util.*;
26 import javax.swing.*;
27 import javax.swing.event.*;
28 import javax.swing.table.*;
29
30 /** This class allows to create a sortable table easily, with the minimum
31     amount of work. It may be not very efficient, but it allows the *client*
32     class to be lighter than if it had to handle itself all the sorting thing..
33     It also handles multiple data types simultaneously.
34     This class is mainly inspired from Sun's tutorial about JTable
35 */

36 final class SortableTable extends JTable
37 {
38     /** Content of the table */
39     private Object JavaDoc rowData[][];
40
41     /** Names of the columns */
42     private Object JavaDoc columnsNames[];
43
44     /** Indexes of the rows */
45     private Object JavaDoc indexes[];
46
47     /** Indexes of the columns */
48     private int rowIndexes[];
49
50     /** Columns which we are currently sorting */
51     private int[] sortingColumns;
52
53     /** Tells what is the sort order : ascending or descending */
54     private boolean ascending;
55
56     /** List of the listeners */
57     private ArrayList listeners;
58
59     /** Default constructor */
60     protected SortableTable()
61     {
62     super();
63
64     ascending = true;
65     listeners = new ArrayList();
66
67     init();
68     }
69
70     /** Constructor
71      * @param rowData content of the table
72      * @param columnsNames names of the columns
73      */

74     protected SortableTable(Object JavaDoc rowData[][], Object JavaDoc columnsNames[])
75     {
76     super(rowData, columnsNames);
77
78     this.rowData = rowData;
79     this.columnsNames = columnsNames;
80
81     ascending = true;
82     listeners = new ArrayList();
83
84     init();
85     }
86
87     /** Allows to make cells editable
88      * NOTE : it returns currently false, independantly from row and col, because this class has been made for JMail. I suppose one should correct it, making a call to the <code>JTable</code>'s original method
89      * @param row number of the row
90      * @param col number of the col
91      * @return boolean telling wether this cell is editable or not
92      */

93     public final boolean isCellEditable(int row, int col)
94     {
95     return(false);
96     }
97
98     /** Performs the init stuff */
99     private void init()
100     {
101     sortingColumns = null;
102
103     ascending = true;
104
105     listeners = new ArrayList();
106
107     //setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
108
setShowGrid(false);
109
110     indexes = new Object JavaDoc[0];
111     rowIndexes = new int[0];
112     }
113
114     /** Allows to set the indexes array
115      * @param indexes new indexes array
116      */

117     protected final void setIndexes(Object JavaDoc indexes[])
118     {
119     this.indexes = indexes;
120     }
121
122     /** Allows to set the table data
123      * @param rowData content of the table
124      * @param columnsNames names of the columns
125      */

126     protected final void setData(Object JavaDoc rowData[][], Object JavaDoc columnsNames[])
127     {
128     this.rowData = rowData;
129     this.columnsNames = columnsNames;
130
131     ((DefaultTableModel)dataModel).setDataVector(rowData, columnsNames);
132     reallocateIndexes();
133     }
134
135     /** Returns the index of selected row
136      * @return the index of row currently selected
137      */

138     protected final Object JavaDoc getSelectedRowIndex()
139     {
140     return(indexes[getSelectedRow()]);
141     }
142
143     protected final Object JavaDoc[] getSelectedRowsIndexes()
144     {
145     int[] rows = getSelectedRows();
146     Object JavaDoc indices[] = new Object JavaDoc[rows.length];
147
148     for(int i = 0; i < rows.length; i++)
149         indices[i] = indexes[rows[i]];
150
151     return(indices);
152     }
153
154     /** Allows to add a <code>ListSelectionListener</code> listener to the table
155      * @param listener <code>ListSelectionListener</code> listener to add
156      */

157     protected final void addListSelectionListener(ListSelectionListener listener)
158     {
159     getSelectionModel().addListSelectionListener(listener);
160     }
161
162     /** Allows to add a <code>TableModelListener</code> listener to the table
163      * @param listener <code>TableModelListener</code> listener to add
164      */

165     protected final void addTableModelListener(TableModelListener listener)
166     {
167     dataModel.addTableModelListener(listener);
168     }
169
170     /** Compare the content of two rows on one column
171      * @param row1 first row to be looked to
172      * @param row2 second tow to be looked to
173      * @param column column to look at
174      * @return result of the comparison (-1, 1, 0)
175      */

176     private int compareRowsByColumn(int row1, int row2, int column)
177     {
178     Class JavaDoc type = dataModel.getColumnClass(column);
179
180     DefaultTableModel data = null;
181
182     data = (DefaultTableModel)dataModel;
183
184     Object JavaDoc o1 = data.getValueAt(row1, column);
185     Object JavaDoc o2 = data.getValueAt(row2, column);
186
187     if(o1 == null && o2 == null)
188         return(0);
189
190     else if(o1 == null)
191         return(-1);
192
193     else if(o2 == null)
194         return(1);
195
196     if(type == java.util.Date JavaDoc.class)
197     {
198         Date d1 = (Date)data.getValueAt(row1, column);
199         long n1 = d1.getTime();
200         Date d2 = (Date)data.getValueAt(row2, column);
201         long n2 = d2.getTime();
202
203         if(n1 < n2)
204         return(-1);
205
206         else if(n1 > n2)
207         return(1);
208
209         else
210         return(0);
211     }
212
213     else if(type == String JavaDoc.class)
214     {
215         String JavaDoc s1 = (String JavaDoc)data.getValueAt(row1, column);
216         String JavaDoc s2 = (String JavaDoc)data.getValueAt(row2, column);
217         int result = s1.compareTo(s2);
218
219         if(result < 0)
220         return(-1);
221
222         else if(result > 0)
223         return(1);
224
225         else
226         return(0);
227     }
228
229     else
230     {
231         Object JavaDoc v1 = data.getValueAt(row1, column);
232         String JavaDoc s1 = v1.toString();
233         Object JavaDoc v2 = data.getValueAt(row2, column);
234         String JavaDoc s2 = v2.toString();
235         int result = s1.compareTo(s2);
236
237         if(result < 0)
238         return(-1);
239
240         else if(result > 0)
241         return(1);
242
243         else
244         return(0);
245     }
246     }
247
248     /** Compares the content of two rows, for each column
249      * @param row1 first row to be looked to
250      * @param row2 second row to be looked to
251      * @return result of the comparison
252      */

253     private int compare(int row1, int row2)
254     {
255     for(int level = 0; level < sortingColumns.length; level++)
256     {
257         int result = compareRowsByColumn(row1, row2, sortingColumns[level]);
258
259         if(result != 0)
260         {
261         if(ascending)
262             return(result);
263
264         else
265             return(-result);
266         }
267     }
268
269     return(0);
270     }
271
272     /** Reperforms the calculation of the indexes */
273     private void reallocateIndexes()
274     {
275     int rowCount = dataModel.getRowCount();
276
277     rowIndexes = new int[rowCount];
278
279     for(int row = 0; row < rowCount; row++)
280         rowIndexes[row] = row;
281     }
282
283     /** Sorts the content of the table */
284     private void sort()
285     {
286     shuttlesort((int[])rowIndexes.clone(), rowIndexes, 0, indexes.length);
287     }
288
289     /** Really performs the shuttlesort
290      *
291      * @param from *low* indexes
292      * @param to *high* indexes
293      * @param low start index
294      * @param high end index
295      */

296     private void shuttlesort(int from[], int to[], int low, int high)
297     {
298     if((high - low) < 2)
299         return;
300
301     int middle = (low + high) / 2;
302     shuttlesort(to, from, low, middle);
303     shuttlesort(to, from, middle, high);
304
305     int p = low;
306     int q = middle;
307
308     if((high - low) >= 4 && compare(from[middle - 1], from[middle]) <= 0)
309     {
310         for(int i = low; i < high; i++)
311         to[i] = from[i];
312
313         return;
314     }
315
316     for(int i = low; i < high; i++)
317     {
318         if(q >= high || (p < middle && compare(from[p], from[q]) <= 0))
319         to[i] = from[p++];
320
321         else
322         to[i] = from[q++];
323     }
324     }
325
326     /** Requests a sort by column
327      * @param column column number
328      */

329     private void sortByColumn(int column)
330     {
331     sortByColumn(column, true);
332     }
333
334     /** Performs a sort by column
335      * @param column column number
336      * @param ascending sort order
337      */

338     private void sortByColumn(int column, boolean ascending)
339     {
340     this.ascending = ascending;
341
342     sortingColumns = new int[1];
343     sortingColumns[0] = column;
344
345     sort();
346
347     ((DefaultTableModel)dataModel).fireTableChanged(new TableModelEvent(dataModel));
348     }
349
350     /** Allows to catch mouse events */
351     protected final void addMouseListenerToHeaderInTable()
352     {
353     setColumnSelectionAllowed(false);
354     
355     MouseAdapter listMouseListener = new SortableTableListener(this);
356     listeners.add(listMouseListener);
357
358     JTableHeader th = getTableHeader();
359     th.addMouseListener(listMouseListener);
360     }
361
362     /** Remove every MouseListener attached to the table (needed after a change) */
363     protected final void removeListeners()
364     {
365     JTableHeader th = getTableHeader();
366     
367     int size = listeners.size();
368
369     for(int i = 0; i < size; i++)
370     {
371         SortableTableListener l = (SortableTableListener)listeners.get(i);
372         th.removeMouseListener((MouseListener)l);
373     }
374     }
375
376     /** Sorts the data contained in the table
377      * TODO : rewrite the code and/or implement a better sort
378      * @param column column number
379      * @param ascending sort order
380      */

381     private void dataSort(int column, boolean ascending)
382     {
383     try
384     {
385         if(ascending)
386         {
387         int j;
388         int limit = rowData.length;
389         int st = -1;
390         
391         while(st < limit)
392         {
393             st++;
394             limit--;
395             boolean swapped = false;
396
397             for(j = st; j < limit; j++)
398             {
399             if(rowData[j][column] instanceof String JavaDoc)
400             {
401                 String JavaDoc s1 = (String JavaDoc)rowData[j][column];
402                 String JavaDoc s2 = (String JavaDoc)rowData[j + 1][column];
403
404                 if(s1.compareTo(s2) > 0)
405                 {
406                 swap(j, (j+1));
407                 swapped = true;
408                 
409                 Object JavaDoc tmpId = new Object JavaDoc();
410                 tmpId = indexes[j];
411                 indexes[j] = indexes[j + 1];
412                 indexes[j + 1] = tmpId;
413                 }
414             }
415
416             else if(rowData[j][column] instanceof Date)
417             {
418                 DateFormat df = DateFormat.getInstance();
419                 df.setLenient(true);
420                 
421                 Date d1 = (Date)rowData[j][column];
422                 Date d2 = (Date)rowData[j + 1][column];
423                 
424                 if(d1.after(d2))
425                 {
426                 swap(j, (j+1));
427                 swapped = true;
428                 
429                 Object JavaDoc tmpId = new Object JavaDoc();
430                 tmpId = indexes[j];
431                 indexes[j] = indexes[j + 1];
432                 indexes[j + 1] = tmpId;
433                 }
434             }
435             }
436
437             if(!swapped)
438             return;
439
440             else
441             swapped = false;
442         
443             for(j = limit; --j >= st;)
444             {
445             if(rowData[j][column] instanceof String JavaDoc)
446             {
447                 String JavaDoc s1 = (String JavaDoc)rowData[j][column];
448                 String JavaDoc s2 = (String JavaDoc)rowData[j + 1][column];
449
450                 if(s1.compareTo(s2) > 0)
451                 {
452                 swap(j, (j+1));
453                 swapped = true;
454                 
455                 Object JavaDoc tmpId = new Object JavaDoc();
456                 tmpId = indexes[j];
457                 indexes[j] = indexes[j + 1];
458                 indexes[j + 1] = tmpId;
459                 }
460             }
461
462             else if(rowData[j][column] instanceof Date)
463             {
464                 DateFormat df = DateFormat.getInstance();
465                 df.setLenient(true);
466
467                 Date d1 = (Date)rowData[j][column];
468                 Date d2 = (Date)rowData[j + 1][column];
469
470                 if(d1.after(d2))
471                 {
472                     swap(j, (j+1));
473                 swapped = true;
474                 
475                 Object JavaDoc tmpId = new Object JavaDoc();
476                 tmpId = indexes[j];
477                 indexes[j] = indexes[j + 1];
478                 indexes[j + 1] = tmpId;
479                 }
480             }
481             }
482
483             if(!swapped)
484             return;
485         }
486         }
487        
488         else
489         {
490         int j;
491         int limit = rowData.length;
492         int st = -1;
493             
494         while(st < limit)
495         {
496             st++;
497             limit--;
498             boolean swapped = false;
499
500             for(j = st; j < limit; j++)
501             {
502             if(rowData[j][column] instanceof String JavaDoc)
503             {
504                 String JavaDoc s1 = (String JavaDoc)rowData[j][column];
505                 String JavaDoc s2 = (String JavaDoc)rowData[j + 1][column];
506
507                 if(s1.compareTo(s2) < 0)
508                 {
509                 swap(j, (j+1));
510                 swapped = true;
511
512                 Object JavaDoc tmpId = new Object JavaDoc();
513                 tmpId = indexes[j];
514                 indexes[j] = indexes[j + 1];
515                 indexes[j + 1] = tmpId;
516                 }
517             }
518
519             else if(rowData[j][column] instanceof Date)
520             {
521                 DateFormat df = DateFormat.getInstance();
522                 df.setLenient(true);
523
524                 Date d1 = (Date)rowData[j][column];
525                 Date d2 = (Date)rowData[j + 1][column];
526
527                 if(d1.before(d2))
528                 {
529                 swap(j, (j+1));
530                 swapped = true;
531
532                 Object JavaDoc tmpId = new Object JavaDoc();
533                 tmpId = indexes[j];
534                 indexes[j] = indexes[j + 1];
535                 indexes[j + 1] = tmpId;
536                 }
537             }
538             }
539
540             if(!swapped)
541             return;
542
543             else
544             swapped = false;
545
546             for(j = limit; --j >= st;)
547             {
548             if(rowData[j][column] instanceof String JavaDoc)
549             {
550                 String JavaDoc s1 = (String JavaDoc)rowData[j][column];
551                 String JavaDoc s2 = (String JavaDoc)rowData[j + 1][column];
552
553                 if(s1.compareTo(s2) < 0)
554                 {
555                 swap(j, (j+1));
556                 swapped = true;
557
558                 Object JavaDoc tmpId = new Object JavaDoc();
559                 tmpId = indexes[j];
560                 indexes[j] = indexes[j + 1];
561                 indexes[j + 1] = tmpId;
562                 }
563             }
564
565             else if(rowData[j][column] instanceof Date)
566             {
567                 DateFormat df = DateFormat.getInstance();
568                 df.setLenient(true);
569
570                 Date d1 = (Date)rowData[j][column];
571                 Date d2 = (Date)rowData[j + 1][column];
572
573                 if(d1.before(d2))
574                 {
575                 swap(j, (j+1));
576                 swapped = true;
577
578                 Object JavaDoc tmpId = new Object JavaDoc();
579                 tmpId = indexes[j];
580                 indexes[j] = indexes[j + 1];
581                 indexes[j + 1] = tmpId;
582                 }
583             }
584             }
585
586             if(!swapped)
587             return;
588         }
589         }
590     }
591
592     catch(Exception JavaDoc e)
593     {
594         e.printStackTrace();
595     }
596     }
597
598     /** Swap two rows of the table
599      * @param j first row
600      * @param k second row
601      */

602     private void swap(int j, int k)
603     {
604     int size = columnsNames.length;
605
606     Object JavaDoc T[] = new Object JavaDoc[size];
607
608     for(int i = 0; i < size; i++)
609         T[i] = rowData[j][i];
610         
611     for(int i = 0; i < size; i++)
612         rowData[j][i] = rowData[k][i];
613
614     for(int i = 0; i < size; i++)
615         rowData[k][i] = T[i];
616     }
617
618     /** Listener for this class */
619     private final class SortableTableListener extends MouseAdapter
620     {
621     /** Internal table */
622     private JTable tableView;
623     
624     /** Constructor
625      * @param table internal table
626      */

627     protected SortableTableListener(JTable table)
628     {
629         tableView = table;
630     }
631
632     /** Method invoked when a mouse event is triggered
633      * @param e mouse event
634      */

635     public final void mouseClicked(MouseEvent e)
636     {
637         TableColumnModel columnModel = tableView.getColumnModel();
638         int viewColumn = columnModel.getColumnIndexAtX(e.getX());
639         int column = tableView.convertColumnIndexToModel(viewColumn);
640             
641         if(e.getClickCount() == 1 && column != -1)
642         {
643         int shiftPressed = e.getModifiers() & InputEvent.SHIFT_MASK;
644                 
645         if(shiftPressed == 0)
646             ascending = true;
647                 
648         else
649             ascending = false;
650
651         sortByColumn(column, ascending);
652         dataSort(column, ascending);
653         tableView.setModel(dataModel);
654         setData(rowData, columnsNames);
655         }
656     }
657     }
658 }
659
Popular Tags