| /* |
| * Copyright (c) 2004, 2008, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.tools.jconsole.inspector; |
| |
| |
| // Imports for picking up mouse events from the JTable. |
| |
| import java.awt.event.MouseEvent; |
| import java.awt.event.MouseListener; |
| import java.util.Vector; |
| import javax.swing.JTable; |
| import javax.swing.event.TableModelEvent; |
| import javax.swing.event.TableModelListener; |
| import javax.swing.table.DefaultTableModel; |
| import javax.swing.table.JTableHeader; |
| import javax.swing.table.TableColumnModel; |
| import sun.tools.jconsole.JConsole; |
| |
| @SuppressWarnings("serial") |
| public class TableSorter extends DefaultTableModel implements MouseListener { |
| private boolean ascending = true; |
| private TableColumnModel columnModel; |
| private JTable tableView; |
| private Vector<TableModelListener> evtListenerList; |
| private int sortColumn = 0; |
| |
| private int[] invertedIndex; |
| |
| public TableSorter() { |
| super(); |
| evtListenerList = new Vector<TableModelListener>(); |
| } |
| |
| public TableSorter(Object[] columnNames, int numRows) { |
| super(columnNames,numRows); |
| evtListenerList = new Vector<TableModelListener>(); |
| } |
| |
| @Override |
| public void newDataAvailable(TableModelEvent e) { |
| super.newDataAvailable(e); |
| invertedIndex = new int[getRowCount()]; |
| for (int i = 0; i < invertedIndex.length; i++) { |
| invertedIndex[i] = i; |
| } |
| sort(this.sortColumn, this.ascending); |
| } |
| |
| @Override |
| public void addTableModelListener(TableModelListener l) { |
| evtListenerList.add(l); |
| super.addTableModelListener(l); |
| } |
| |
| @Override |
| public void removeTableModelListener(TableModelListener l) { |
| evtListenerList.remove(l); |
| super.removeTableModelListener(l); |
| } |
| |
| private void removeListeners() { |
| for(TableModelListener tnl : evtListenerList) |
| super.removeTableModelListener(tnl); |
| } |
| |
| private void restoreListeners() { |
| for(TableModelListener tnl : evtListenerList) |
| super.addTableModelListener(tnl); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private int compare(Object o1, Object o2) { |
| // take care of the case where both o1 & o2 are null. Needed to keep |
| // the method symmetric. Without this quickSort gives surprising results. |
| if (o1 == o2) |
| return 0; |
| if (o1==null) |
| return 1; |
| if (o2==null) |
| return -1; |
| //two object of the same class and that are comparable |
| else if ((o1.getClass().equals(o2.getClass())) && |
| (o1 instanceof Comparable)) { |
| return (((Comparable) o1).compareTo(o2)); |
| } |
| else { |
| return o1.toString().compareTo(o2.toString()); |
| } |
| } |
| |
| private void sort(int column, boolean isAscending) { |
| final XMBeanAttributes attrs = |
| (tableView instanceof XMBeanAttributes) |
| ?(XMBeanAttributes) tableView |
| :null; |
| |
| // We cannot sort rows when a cell is being |
| // edited - so we're going to cancel cell editing here if needed. |
| // This might happen when the user is editing a row, and clicks on |
| // another row without validating. In that case there are two events |
| // that compete: one is the validation of the value that was previously |
| // edited, the other is the mouse click that opens the new editor. |
| // |
| // When we reach here the previous value is already validated, and the |
| // old editor is closed, but the new editor might have opened. |
| // It's this new editor that wil be cancelled here, if needed. |
| // |
| if (attrs != null && attrs.isEditing()) |
| attrs.cancelCellEditing(); |
| |
| // remove registered listeners |
| removeListeners(); |
| // do the sort |
| |
| if (JConsole.isDebug()) { |
| System.err.println("sorting table against column="+column |
| +" ascending="+isAscending); |
| } |
| quickSort(0,getRowCount()-1,column,isAscending); |
| // restore registered listeners |
| restoreListeners(); |
| |
| // update row heights in XMBeanAttributes (required by expandable cells) |
| if (attrs != null) { |
| for (int i = 0; i < getRowCount(); i++) { |
| Vector data = (Vector) dataVector.elementAt(i); |
| attrs.updateRowHeight(data.elementAt(1), i); |
| } |
| } |
| } |
| |
| private boolean compareS(Object s1, Object s2, boolean isAscending) { |
| if (isAscending) |
| return (compare(s1,s2) > 0); |
| else |
| return (compare(s1,s2) < 0); |
| } |
| |
| private boolean compareG(Object s1, Object s2, boolean isAscending) { |
| if (isAscending) |
| return (compare(s1,s2) < 0); |
| else |
| return (compare(s1,s2) > 0); |
| } |
| |
| private void quickSort(int lo0,int hi0, int key, boolean isAscending) { |
| int lo = lo0; |
| int hi = hi0; |
| Object mid; |
| |
| if ( hi0 > lo0) |
| { |
| mid = getValueAt( ( lo0 + hi0 ) / 2 , key); |
| |
| while( lo <= hi ) |
| { |
| /* find the first element that is greater than |
| * or equal to the partition element starting |
| * from the left Index. |
| */ |
| while( ( lo < hi0 ) && |
| ( compareS(mid,getValueAt(lo,key), isAscending) )) |
| ++lo; |
| |
| /* find an element that is smaller than or equal to |
| * the partition element starting from the right Index. |
| */ |
| while( ( hi > lo0 ) && |
| ( compareG(mid,getValueAt(hi,key), isAscending) )) |
| --hi; |
| |
| // if the indexes have not crossed, swap |
| if( lo <= hi ) |
| { |
| swap(lo, hi, key); |
| ++lo; |
| --hi; |
| } |
| } |
| |
| /* If the right index has not reached the |
| * left side of array |
| * must now sort the left partition. |
| */ |
| if( lo0 < hi ) |
| quickSort(lo0, hi , key, isAscending); |
| |
| /* If the left index has not reached the right |
| * side of array |
| * must now sort the right partition. |
| */ |
| if( lo <= hi0 ) |
| quickSort(lo, hi0 , key, isAscending); |
| } |
| } |
| |
| private Vector getRow(int row) { |
| return (Vector) dataVector.elementAt(row); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private void setRow(Vector data, int row) { |
| dataVector.setElementAt(data,row); |
| } |
| |
| private void swap(int i, int j, int column) { |
| Vector data = getRow(i); |
| setRow(getRow(j),i); |
| setRow(data,j); |
| |
| int a = invertedIndex[i]; |
| invertedIndex[i] = invertedIndex[j]; |
| invertedIndex[j] = a; |
| } |
| |
| public void sortByColumn(int column) { |
| sortByColumn(column, !ascending); |
| } |
| |
| public void sortByColumn(int column, boolean ascending) { |
| this.ascending = ascending; |
| this.sortColumn = column; |
| sort(column,ascending); |
| } |
| |
| public int getIndexOfRow(int row) { |
| return invertedIndex[row]; |
| } |
| |
| // Add a mouse listener to the Table to trigger a table sort |
| // when a column heading is clicked in the JTable. |
| public void addMouseListenerToHeaderInTable(JTable table) { |
| tableView = table; |
| columnModel = tableView.getColumnModel(); |
| JTableHeader th = tableView.getTableHeader(); |
| th.addMouseListener(this); |
| } |
| |
| public void mouseClicked(MouseEvent e) { |
| int viewColumn = columnModel.getColumnIndexAtX(e.getX()); |
| int column = tableView.convertColumnIndexToModel(viewColumn); |
| if (e.getClickCount() == 1 && column != -1) { |
| if (tableView instanceof XTable) { |
| XTable attrs = (XTable) tableView; |
| // inform the table view that the rows are going to be sorted |
| // against the values in a given column. This gives the |
| // chance to the table view to close its editor - if needed. |
| // |
| attrs.sortRequested(column); |
| } |
| tableView.invalidate(); |
| sortByColumn(column); |
| tableView.validate(); |
| tableView.repaint(); |
| } |
| } |
| |
| public void mousePressed(MouseEvent e) { |
| } |
| |
| public void mouseEntered(MouseEvent e) { |
| } |
| |
| public void mouseExited(MouseEvent e) { |
| } |
| |
| public void mouseReleased(MouseEvent e) { |
| } |
| } |