1 /* 2 * Copyright (c) 2004, 2012, 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 = 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 @SuppressWarnings("unchecked") 221 private Vector<Object> getRow(int row) { 222 return dataVector.elementAt(row); 223 } 224 225 @SuppressWarnings("unchecked") 226 private void setRow(Vector<Object> data, int row) { 227 dataVector.setElementAt(data,row); 228 } 229 230 private void swap(int i, int j, int column) { 231 Vector<Object> data = getRow(i); 232 setRow(getRow(j),i); 233 setRow(data,j); 234 235 int a = invertedIndex[i]; 236 invertedIndex[i] = invertedIndex[j]; 237 invertedIndex[j] = a; 238 } 239 240 public void sortByColumn(int column) { 241 sortByColumn(column, !ascending); 242 } 243 244 public void sortByColumn(int column, boolean ascending) { 245 this.ascending = ascending; 246 this.sortColumn = column; 247 sort(column,ascending); 248 } 249 250 public int getIndexOfRow(int row) { 251 return invertedIndex[row]; 252 } 253 254 // Add a mouse listener to the Table to trigger a table sort 255 // when a column heading is clicked in the JTable. 256 public void addMouseListenerToHeaderInTable(JTable table) { 257 tableView = table; 258 columnModel = tableView.getColumnModel(); 259 JTableHeader th = tableView.getTableHeader(); 260 th.addMouseListener(this); 261 } 262 263 public void mouseClicked(MouseEvent e) { 264 int viewColumn = columnModel.getColumnIndexAtX(e.getX()); 265 int column = tableView.convertColumnIndexToModel(viewColumn); 266 if (e.getClickCount() == 1 && column != -1) { 267 if (tableView instanceof XTable) { 268 XTable attrs = (XTable) tableView; 269 // inform the table view that the rows are going to be sorted 270 // against the values in a given column. This gives the 271 // chance to the table view to close its editor - if needed. 272 // 273 attrs.sortRequested(column); 274 } 275 tableView.invalidate(); 276 sortByColumn(column); 277 tableView.validate(); 278 tableView.repaint(); 279 } 280 } 281 282 public void mousePressed(MouseEvent e) { 283 } 284 285 public void mouseEntered(MouseEvent e) { 286 } 287 288 public void mouseExited(MouseEvent e) { 289 } 290 291 public void mouseReleased(MouseEvent e) { 292 } 293 }