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

Quick Search    Search Deep

Source code: com/pjsofts/eurobudget/model/TxnTableSorter.java


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