1 /* 2 * Copyright (c) 2004, 2008, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.tools.jconsole.inspector; 27 28 29 // Imports for picking up mouse events from the JTable. 30 31 import java.awt.event.MouseEvent; 32 import java.awt.event.MouseListener; 33 import java.util.Vector; 34 import javax.swing.JTable; 35 import javax.swing.event.TableModelEvent; 36 import javax.swing.event.TableModelListener; 37 import javax.swing.table.DefaultTableModel; 38 import javax.swing.table.JTableHeader; 39 import javax.swing.table.TableColumnModel; 40 import sun.tools.jconsole.JConsole; 41 42 @SuppressWarnings("serial") 43 public class TableSorter extends DefaultTableModel implements MouseListener { 44 private boolean ascending = true; 45 private TableColumnModel columnModel; 46 private JTable tableView; 47 private Vector<TableModelListener> evtListenerList; 48 private int sortColumn = 0; 49 50 private int[] invertedIndex; 51 52 public TableSorter() { 53 super(); 54 evtListenerList = new Vector<TableModelListener>(); 55 } 56 57 public TableSorter(Object[] columnNames, int numRows) { 58 super(columnNames,numRows); 59 evtListenerList = new Vector<TableModelListener>(); 60 } 61 62 @Override 63 public void newDataAvailable(TableModelEvent e) { 64 super.newDataAvailable(e); 65 invertedIndex = new int[getRowCount()]; 66 for (int i = 0; i < invertedIndex.length; i++) { 67 invertedIndex[i] = i; 68 } 69 sort(this.sortColumn, this.ascending); 70 } 71 72 @Override 73 public void addTableModelListener(TableModelListener l) { 74 evtListenerList.add(l); 75 super.addTableModelListener(l); 76 } 77 78 @Override 79 public void removeTableModelListener(TableModelListener l) { 80 evtListenerList.remove(l); 81 super.removeTableModelListener(l); 82 } 83 84 private void removeListeners() { 85 for(TableModelListener tnl : evtListenerList) 86 super.removeTableModelListener(tnl); 87 } 88 89 private void restoreListeners() { 90 for(TableModelListener tnl : evtListenerList) 91 super.addTableModelListener(tnl); 92 } 93 94 @SuppressWarnings("unchecked") 95 private int compare(Object o1, Object o2) { 96 // take care of the case where both o1 & o2 are null. Needed to keep 97 // the method symmetric. Without this quickSort gives surprising results. 98 if (o1 == o2) 99 return 0; 100 if (o1==null) 101 return 1; 102 if (o2==null) 103 return -1; 104 //two object of the same class and that are comparable 105 else if ((o1.getClass().equals(o2.getClass())) && 106 (o1 instanceof Comparable)) { 107 return (((Comparable) o1).compareTo(o2)); 108 } 109 else { 110 return o1.toString().compareTo(o2.toString()); 111 } 112 } 113 114 private void sort(int column, boolean isAscending) { 115 final XMBeanAttributes attrs = 116 (tableView instanceof XMBeanAttributes) 117 ?(XMBeanAttributes) tableView 118 :null; 119 120 // We cannot sort rows when a cell is being 121 // edited - so we're going to cancel cell editing here if needed. 122 // This might happen when the user is editing a row, and clicks on 123 // another row without validating. In that case there are two events 124 // that compete: one is the validation of the value that was previously 125 // edited, the other is the mouse click that opens the new editor. 126 // 127 // When we reach here the previous value is already validated, and the 128 // old editor is closed, but the new editor might have opened. 129 // It's this new editor that wil be cancelled here, if needed. 130 // 131 if (attrs != null && attrs.isEditing()) 132 attrs.cancelCellEditing(); 133 134 // remove registered listeners 135 removeListeners(); 136 // do the sort 137 138 if (JConsole.isDebug()) { 139 System.err.println("sorting table against column="+column 140 +" ascending="+isAscending); 141 } 142 quickSort(0,getRowCount()-1,column,isAscending); 143 // restore registered listeners 144 restoreListeners(); 145 146 // update row heights in XMBeanAttributes (required by expandable cells) 147 if (attrs != null) { 148 for (int i = 0; i < getRowCount(); i++) { 149 Vector data = (Vector) dataVector.elementAt(i); 150 attrs.updateRowHeight(data.elementAt(1), i); 151 } 152 } 153 } 154 155 private boolean compareS(Object s1, Object s2, boolean isAscending) { 156 if (isAscending) 157 return (compare(s1,s2) > 0); 158 else 159 return (compare(s1,s2) < 0); 160 } 161 162 private boolean compareG(Object s1, Object s2, boolean isAscending) { 163 if (isAscending) 164 return (compare(s1,s2) < 0); 165 else 166 return (compare(s1,s2) > 0); 167 } 168 169 private void quickSort(int lo0,int hi0, int key, boolean isAscending) { 170 int lo = lo0; 171 int hi = hi0; 172 Object mid; 173 174 if ( hi0 > lo0) 175 { 176 mid = getValueAt( ( lo0 + hi0 ) / 2 , key); 177 178 while( lo <= hi ) 179 { 180 /* find the first element that is greater than 181 * or equal to the partition element starting 182 * from the left Index. 183 */ 184 while( ( lo < hi0 ) && 185 ( compareS(mid,getValueAt(lo,key), isAscending) )) 186 ++lo; 187 188 /* find an element that is smaller than or equal to 189 * the partition element starting from the right Index. 190 */ 191 while( ( hi > lo0 ) && 192 ( compareG(mid,getValueAt(hi,key), isAscending) )) 193 --hi; 194 195 // if the indexes have not crossed, swap 196 if( lo <= hi ) 197 { 198 swap(lo, hi, key); 199 ++lo; 200 --hi; 201 } 202 } 203 204 /* If the right index has not reached the 205 * left side of array 206 * must now sort the left partition. 207 */ 208 if( lo0 < hi ) 209 quickSort(lo0, hi , key, isAscending); 210 211 /* If the left index has not reached the right 212 * side of array 213 * must now sort the right partition. 214 */ 215 if( lo <= hi0 ) 216 quickSort(lo, hi0 , key, isAscending); 217 } 218 } 219 220 private Vector getRow(int row) { 221 return (Vector) dataVector.elementAt(row); 222 } 223 224 @SuppressWarnings("unchecked") 225 private void setRow(Vector data, int row) { 226 dataVector.setElementAt(data,row); 227 } 228 229 private void swap(int i, int j, int column) { 230 Vector data = getRow(i); 231 setRow(getRow(j),i); 232 setRow(data,j); 233 234 int a = invertedIndex[i]; 235 invertedIndex[i] = invertedIndex[j]; 236 invertedIndex[j] = a; 237 } 238 239 public void sortByColumn(int column) { 240 sortByColumn(column, !ascending); 241 } 242 243 public void sortByColumn(int column, boolean ascending) { 244 this.ascending = ascending; 245 this.sortColumn = column; 246 sort(column,ascending); 247 } 248 249 public int getIndexOfRow(int row) { 250 return invertedIndex[row]; 251 } 252 253 // Add a mouse listener to the Table to trigger a table sort 254 // when a column heading is clicked in the JTable. 255 public void addMouseListenerToHeaderInTable(JTable table) { 256 tableView = table; 257 columnModel = tableView.getColumnModel(); 258 JTableHeader th = tableView.getTableHeader(); 259 th.addMouseListener(this); 260 } 261 262 public void mouseClicked(MouseEvent e) { 263 int viewColumn = columnModel.getColumnIndexAtX(e.getX()); 264 int column = tableView.convertColumnIndexToModel(viewColumn); 265 if (e.getClickCount() == 1 && column != -1) { 266 if (tableView instanceof XTable) { 267 XTable attrs = (XTable) tableView; 268 // inform the table view that the rows are going to be sorted 269 // against the values in a given column. This gives the 270 // chance to the table view to close its editor - if needed. 271 // 272 attrs.sortRequested(column); 273 } 274 tableView.invalidate(); 275 sortByColumn(column); 276 tableView.validate(); 277 tableView.repaint(); 278 } 279 } 280 281 public void mousePressed(MouseEvent e) { 282 } 283 284 public void mouseEntered(MouseEvent e) { 285 } 286 287 public void mouseExited(MouseEvent e) { 288 } 289 290 public void mouseReleased(MouseEvent e) { 291 } 292 }