Clover coverage report - baseCode - 0.2.5
Coverage timestamp: Tue Apr 12 2005 11:31:58 EDT
file stats: LOC: 586   Methods: 42
NCLOC: 429   Classes: 7
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
TableSorter.java 0% 0% 0% 0%
coverage
 1   
 package baseCode.gui.table;
 2   
 
 3   
 import java.awt.Color;
 4   
 import java.awt.Component;
 5   
 import java.awt.Graphics;
 6   
 import java.awt.Point;
 7   
 import java.awt.event.MouseAdapter;
 8   
 import java.awt.event.MouseEvent;
 9   
 import java.awt.event.MouseListener;
 10   
 import java.util.ArrayList;
 11   
 import java.util.Arrays;
 12   
 import java.util.Comparator;
 13   
 import java.util.HashMap;
 14   
 import java.util.Iterator;
 15   
 import java.util.List;
 16   
 import java.util.Map;
 17   
 import java.util.Vector;
 18   
 
 19   
 import javax.swing.Icon;
 20   
 import javax.swing.JLabel;
 21   
 import javax.swing.JTable;
 22   
 import javax.swing.SwingConstants;
 23   
 import javax.swing.event.TableModelEvent;
 24   
 import javax.swing.event.TableModelListener;
 25   
 import javax.swing.table.AbstractTableModel;
 26   
 import javax.swing.table.JTableHeader;
 27   
 import javax.swing.table.TableCellRenderer;
 28   
 import javax.swing.table.TableColumnModel;
 29   
 import javax.swing.table.TableModel;
 30   
 
 31   
 import baseCode.gui.JMatrixDisplay;
 32   
 
 33   
 /**
 34   
  * TableSorter is a decorator for TableModels; adding sorting functionality to a supplied TableModel. TableSorter does
 35   
  * not store or copy the data in its TableModel; instead it maintains a map from the row indexes of the view to the row
 36   
  * indexes of the model. As requests are made of the sorter (like getValueAt(row, col)) they are passed to the
 37   
  * underlying model after the row numbers have been translated via the internal mapping array. This way, the TableSorter
 38   
  * appears to hold another copy of the table with the rows in a different order. <p/>TableSorter registers itself as a
 39   
  * listener to the underlying model, just as the JTable itself would. Events recieved from the model are examined,
 40   
  * sometimes manipulated (typically widened), and then passed on to the TableSorter's listeners (typically the JTable).
 41   
  * If a change to the model has invalidated the order of TableSorter's rows, a note of this is made and the sorter will
 42   
  * resort the rows the next time a value is requested. <p/>When the tableHeader property is set, either by using the
 43   
  * setTableHeader() method or the two argument constructor, the table header may be used as a complete UI for
 44   
  * TableSorter. The default renderer of the tableHeader is decorated with a renderer that indicates the sorting status
 45   
  * of each column. In addition, a mouse listener is installed with the following behavior:
 46   
  * <ul>
 47   
  * <li>Mouse-click: Clears the sorting status of all other columns and advances the sorting status of that column
 48   
  * through three values: {NOT_SORTED, ASCENDING, DESCENDING} (then back to NOT_SORTED again).
 49   
  * <li>SHIFT-mouse-click: Clears the sorting status of all other columns and cycles the sorting status of the column
 50   
  * through the same three values, in the opposite order: {NOT_SORTED, DESCENDING, ASCENDING}.
 51   
  * <li>CONTROL-mouse-click and CONTROL-SHIFT-mouse-click: as above except that the changes to the column do not cancel
 52   
  * the statuses of columns that are already sorting - giving a way to initiate a compound sort.
 53   
  * </ul>
 54   
  * <p/>This is a long overdue rewrite of a class of the same name that first appeared in the swing table demos in 1997.
 55   
  * 
 56   
  * @author Philip Milne
 57   
  * @author Brendon McLean
 58   
  * @author Dan van Enckevort
 59   
  * @author Parwinder Sekhon
 60   
  * @author Paul Pavlidis (minor)
 61   
  * @version 2.0 02/27/04
 62   
  * @version $Id: TableSorter.java,v 1.10 2004/09/20 22:19:03 pavlidis Exp $
 63   
  */
 64   
 
 65   
 public class TableSorter extends AbstractTableModel {
 66   
    protected TableModel tableModel;
 67   
    JMatrixDisplay m_matrixDisplay; // get rid of this!
 68   
 
 69   
    public static final int DESCENDING = -1;
 70   
    public static final int NOT_SORTED = 0;
 71   
    public static final int ASCENDING = 1;
 72   
 
 73   
    private static Directive EMPTY_DIRECTIVE = new Directive( -1, NOT_SORTED );
 74   
 
 75   
    public static final Comparator COMPARABLE_COMAPRATOR = new Comparator() {
 76  0
       public int compare( Object o1, Object o2 ) {
 77  0
          return ( ( Comparable ) o1 ).compareTo( o2 );
 78   
       }
 79   
    };
 80   
    public static final Comparator LEXICAL_COMPARATOR = new Comparator() {
 81  0
       public int compare( Object o1, Object o2 ) {
 82  0
          return o1.toString().compareTo( o2.toString() );
 83   
       }
 84   
    };
 85   
 
 86   
    private Row[] viewToModel;
 87   
    int[] modelToView;
 88   
 
 89   
    private JTableHeader tableHeader;
 90   
    private MouseListener mouseListener;
 91   
    private TableModelListener tableModelListener;
 92   
    private Map columnComparators = new HashMap();
 93   
    List sortingColumns = new ArrayList();
 94   
 
 95  0
    public TableSorter() {
 96  0
       this.mouseListener = new MouseHandler();
 97  0
       this.tableModelListener = new TableModelHandler();
 98   
    }
 99   
 
 100  0
    public TableSorter( TableModel tableModel ) {
 101  0
       this();
 102  0
       setTableModel( tableModel );
 103   
    }
 104   
 
 105  0
    public TableSorter( TableModel tableModel, JTableHeader tableHeader ) {
 106  0
       this();
 107  0
       setTableHeader( tableHeader );
 108  0
       setTableModel( tableModel );
 109   
    }
 110   
 
 111  0
    public TableSorter( TableModel tableModel, JMatrixDisplay matrixDisplay ) {
 112  0
       this( tableModel );
 113  0
       m_matrixDisplay = matrixDisplay;
 114   
    }
 115   
 
 116  0
    void clearSortingState() {
 117  0
       viewToModel = null;
 118  0
       modelToView = null;
 119   
    }
 120   
 
 121  0
    public TableModel getTableModel() {
 122  0
       return tableModel;
 123   
    }
 124   
 
 125  0
    public void setTableModel( TableModel tableModel ) {
 126  0
       if ( this.tableModel != null ) {
 127  0
          this.tableModel.removeTableModelListener( tableModelListener );
 128   
       }
 129   
 
 130  0
       this.tableModel = tableModel;
 131  0
       if ( this.tableModel != null ) {
 132  0
          this.tableModel.addTableModelListener( tableModelListener );
 133   
       }
 134   
 
 135  0
       clearSortingState();
 136  0
       fireTableStructureChanged();
 137   
    }
 138   
 
 139  0
    public JTableHeader getTableHeader() {
 140  0
       return tableHeader;
 141   
    }
 142   
 
 143  0
    public void setTableHeader( JTableHeader tableHeader ) {
 144  0
       if ( this.tableHeader != null ) {
 145  0
          this.tableHeader.removeMouseListener( mouseListener );
 146  0
          TableCellRenderer defaultRenderer = this.tableHeader
 147   
                .getDefaultRenderer();
 148  0
          if ( defaultRenderer instanceof SortableHeaderRenderer ) {
 149  0
             this.tableHeader
 150   
                   .setDefaultRenderer( ( ( SortableHeaderRenderer ) defaultRenderer ).tableCellRenderer );
 151   
          }
 152   
       }
 153  0
       this.tableHeader = tableHeader;
 154  0
       if ( this.tableHeader != null ) {
 155  0
          this.tableHeader.addMouseListener( mouseListener );
 156  0
          this.tableHeader.setDefaultRenderer( new SortableHeaderRenderer(
 157   
                this.tableHeader.getDefaultRenderer() ) );
 158   
       }
 159   
    }
 160   
 
 161  0
    public boolean isSorting() {
 162  0
       return sortingColumns.size() != 0;
 163   
    }
 164   
 
 165  0
    private Directive getDirective( int column ) {
 166  0
       for ( int i = 0; i < sortingColumns.size(); i++ ) {
 167  0
          Directive directive = ( Directive ) sortingColumns.get( i );
 168  0
          if ( directive.column == column ) {
 169  0
             return directive;
 170   
          }
 171   
       }
 172  0
       return EMPTY_DIRECTIVE;
 173   
    }
 174   
 
 175  0
    public int getSortingStatus( int column ) {
 176  0
       return getDirective( column ).direction;
 177   
    }
 178   
 
 179  0
    private void sortingStatusChanged() {
 180  0
       clearSortingState();
 181  0
       fireTableDataChanged();
 182  0
       if ( tableHeader != null ) {
 183  0
          tableHeader.repaint();
 184   
       }
 185   
    }
 186   
 
 187  0
    public void setSortingStatus( int column, int status ) {
 188  0
       Directive directive = getDirective( column );
 189  0
       if ( directive != EMPTY_DIRECTIVE ) {
 190  0
          sortingColumns.remove( directive );
 191   
       }
 192  0
       if ( status != NOT_SORTED ) {
 193  0
          sortingColumns.add( new Directive( column, status ) );
 194   
       }
 195  0
       sortingStatusChanged();
 196   
    }
 197   
 
 198  0
    protected Icon getHeaderRendererIcon( int column, int size ) {
 199  0
       Directive directive = getDirective( column );
 200  0
       if ( directive == EMPTY_DIRECTIVE ) {
 201  0
          return null;
 202   
       }
 203  0
       return new Arrow( directive.direction == DESCENDING, size, sortingColumns
 204   
             .indexOf( directive ) );
 205   
    }
 206   
 
 207  0
    public void cancelSorting() {
 208  0
       sortingColumns.clear();
 209  0
       sortingStatusChanged();
 210   
    }
 211   
 
 212  0
    public void setColumnComparator( Class type, Comparator comparator ) {
 213  0
       if ( comparator == null ) {
 214  0
          columnComparators.remove( type );
 215   
       } else {
 216  0
          columnComparators.put( type, comparator );
 217   
       }
 218   
    }
 219   
 
 220  0
    protected Comparator getComparator( int column ) {
 221  0
       Class columnType = tableModel.getColumnClass( column );
 222  0
       Comparator comparator = ( Comparator ) columnComparators.get( columnType );
 223  0
       if ( comparator != null ) {
 224  0
          return comparator;
 225   
       }
 226  0
       if ( Comparable.class.isAssignableFrom( columnType ) ) {
 227  0
          return COMPARABLE_COMAPRATOR;
 228   
       }
 229  0
       return LEXICAL_COMPARATOR;
 230   
    }
 231   
 
 232  0
    protected Comparator getComparator( Class columnClass ) {
 233  0
       Class columnType = columnClass;
 234  0
       Comparator comparator = ( Comparator ) columnComparators.get( columnType );
 235  0
       if ( comparator != null ) {
 236  0
          return comparator;
 237   
       }
 238  0
       if ( Comparable.class.isAssignableFrom( columnType ) ) {
 239  0
          return COMPARABLE_COMAPRATOR;
 240   
       }
 241  0
       return LEXICAL_COMPARATOR;
 242   
    }
 243   
 
 244  0
    private Row[] getViewToModel() {
 245  0
       if ( viewToModel == null ) {
 246  0
          int tableModelRowCount = tableModel.getRowCount();
 247  0
          viewToModel = new Row[tableModelRowCount];
 248  0
          for ( int row = 0; row < tableModelRowCount; row++ ) {
 249  0
             viewToModel[row] = new Row( row );
 250   
          }
 251   
 
 252  0
          if ( isSorting() ) {
 253  0
             Arrays.sort( viewToModel );
 254   
          }
 255   
       }
 256  0
       return viewToModel;
 257   
    }
 258   
 
 259  0
    public int modelIndex( int viewIndex ) {
 260  0
       return getViewToModel()[viewIndex].modelIndex;
 261   
    }
 262   
 
 263  0
    int[] getModelToView() {
 264  0
       if ( modelToView == null ) {
 265  0
          int n = getViewToModel().length;
 266  0
          modelToView = new int[n];
 267  0
          for ( int i = 0; i < n; i++ ) {
 268  0
             modelToView[modelIndex( i )] = i;
 269   
          }
 270   
       }
 271  0
       return modelToView;
 272   
    }
 273   
 
 274   
    // TableModel interface methods
 275   
 
 276  0
    public int getRowCount() {
 277  0
       return ( tableModel == null ) ? 0 : tableModel.getRowCount();
 278   
    }
 279   
 
 280  0
    public int getColumnCount() {
 281  0
       return ( tableModel == null ) ? 0 : tableModel.getColumnCount();
 282   
    }
 283   
 
 284  0
    public String getColumnName( int column ) {
 285  0
       return tableModel.getColumnName( column );
 286   
    }
 287   
 
 288  0
    public Class getColumnClass( int column ) {
 289  0
       return tableModel.getColumnClass( column );
 290   
    }
 291   
 
 292  0
    public boolean isCellEditable( int row, int column ) {
 293  0
       return tableModel.isCellEditable( modelIndex( row ), column );
 294   
    }
 295   
 
 296  0
    public Object getValueAt( int row, int column ) {
 297  0
       return tableModel.getValueAt( modelIndex( row ), column );
 298   
    }
 299   
 
 300  0
    public void setValueAt( Object aValue, int row, int column ) {
 301  0
       tableModel.setValueAt( aValue, modelIndex( row ), column );
 302   
    }
 303   
 
 304   
    // Helper classes
 305   
 
 306   
    private class Row implements Comparable {
 307   
       int modelIndex;
 308   
 
 309  0
       public Row( int index ) {
 310  0
          this.modelIndex = index;
 311   
       }
 312   
 
 313  0
       public int compareTo( Object o ) {
 314  0
          int row1 = modelIndex;
 315  0
          int row2 = ( ( Row ) o ).modelIndex;
 316   
 
 317  0
          for ( Iterator it = sortingColumns.iterator(); it.hasNext(); ) {
 318  0
             Directive directive = ( Directive ) it.next();
 319  0
             int column = directive.column;
 320  0
             Object o1 = tableModel.getValueAt( row1, column );
 321  0
             Object o2 = tableModel.getValueAt( row2, column );
 322  0
             Comparator comparator = null;
 323  0
             int comparison = 0;
 324  0
             boolean favor = false;
 325   
 
 326  0
             if ( o1 == null && o2 == null ) {
 327  0
                comparison = 0;
 328  0
             } else if ( o1 == null ) {
 329  0
                if ( directive.direction == DESCENDING ) // Define null less than everything, except null.
 330  0
                   comparison = -1;
 331   
                else
 332   
                   // Define null greater than everything, except null.
 333  0
                   comparison = 1;
 334  0
             } else if ( o2 == null ) {
 335  0
                if ( directive.direction == DESCENDING )
 336  0
                   comparison = 1;
 337   
                else
 338  0
                   comparison = -1;
 339  0
             } else if ( o1 != null && o2 != null ) {
 340  0
                if ( o1.getClass().equals( Double.class ) ) {
 341  0
                   comparator = getComparator( Double.class );
 342  0
                } else if ( o1.getClass().equals( Integer.class ) ) {
 343  0
                   comparator = getComparator( Integer.class );
 344  0
                } else if ( o1.getClass().equals( Point.class ) ) {
 345  0
                   comparator = getComparator( Double.class );
 346   
 
 347   
                   // If sortColumn is in the matrix display, then model.getValueAt()
 348   
                   // returns a Point object that represents a coordinate into the
 349   
                   // display matrix. This is done so that the display matrix object
 350   
                   // can be asked for both the color and the value. We are here only
 351   
                   // interested in the value.
 352   
 
 353   
                   /**
 354   
                    * @todo we shouldn't include a dependency on JMatrixDisplay here. Find a workaround.
 355   
                    */
 356  0
                   if ( m_matrixDisplay != null ) {
 357   
 
 358  0
                      Point p1 = ( Point ) o1;
 359  0
                      Point p2 = ( Point ) o2;
 360   
 
 361  0
                      o1 = new Double( m_matrixDisplay.getValue( p1.x, p1.y ) );
 362  0
                      o2 = new Double( m_matrixDisplay.getValue( p2.x, p2.y ) );
 363   
                   }
 364  0
                } else if ( o1.getClass().equals( ArrayList.class ) ) {
 365  0
                   if ( ( ( ArrayList ) o1 ).get( 0 ).getClass().equals(
 366   
                         Double.class ) ) {
 367  0
                      comparator = getComparator( Double.class );
 368   
                      //only comparing the first member of the list
 369  0
                      Double a = ( Double ) ( ( ArrayList ) o1 ).get( 0 );
 370  0
                      Double b = ( Double ) ( ( ArrayList ) o2 ).get( 0 );
 371   
 
 372   
                      // yes, we did get an array list, but we want to
 373   
                      // compare the Double values the array lists contain,
 374   
                      // not the array lists themselves.
 375  0
                      o1 = a;
 376  0
                      o2 = b;
 377   
                   }
 378  0
                } else if ( o1.getClass().equals( Vector.class ) ) {
 379  0
                   if ( ( ( Vector ) o1 ).get( 0 ).getClass().equals(
 380   
                         String.class ) ) {
 381  0
                      if ( ( ( Vector ) o1 ).size() == 2 ) {
 382  0
                         comparison = -1;
 383  0
                         favor = true;
 384  0
                      } else if ( ( ( Vector ) o2 ).size() == 2 ) {
 385  0
                         comparison = 1;
 386  0
                         favor = true;
 387   
                      } else //compare normally
 388   
                      {
 389  0
                         comparator = getComparator( String.class );
 390  0
                         String a = ( String ) ( ( Vector ) o1 ).get( 0 );
 391  0
                         String b = ( String ) ( ( Vector ) o2 ).get( 0 );
 392  0
                         o1 = a;
 393  0
                         o2 = b;
 394   
                      }
 395  0
                   } else if ( ( ( Vector ) o1 ).get( 0 ).getClass().equals(
 396   
                         Double.class ) ) {
 397   
 
 398   
                      //only comparing the first member of the list
 399  0
                      Double a = ( Double ) ( ( ArrayList ) o1 ).get( 0 );
 400  0
                      Double b = ( Double ) ( ( ArrayList ) o2 ).get( 0 );
 401  0
                      comparator = getComparator( Double.class );
 402   
 
 403   
                      // yes, we did get a Vector, but we want to
 404   
                      // compare the Double values the Vectors contain,
 405   
                      // not the Vectors themselves
 406  0
                      o1 = a;
 407  0
                      o2 = b;
 408   
                   }
 409   
                } else {
 410  0
                   comparator = getComparator( column );
 411   
                }
 412  0
                if ( favor != true ) { //we're not favoring anyone
 413  0
                   comparison = comparator.compare( o1, o2 );
 414   
                }
 415   
             }
 416   
 
 417  0
             if ( comparison != 0 ) {
 418  0
                return directive.direction == DESCENDING ? -comparison
 419   
                      : comparison;
 420   
             }
 421   
          }
 422  0
          return 0;
 423   
       }
 424   
    }
 425   
 
 426   
    private class TableModelHandler implements TableModelListener {
 427  0
       public void tableChanged( TableModelEvent e ) {
 428   
          // If we're not sorting by anything, just pass the event along.
 429  0
          if ( !isSorting() ) {
 430  0
             clearSortingState();
 431  0
             fireTableChanged( e );
 432  0
             return;
 433   
          }
 434   
 
 435   
          // If the table structure has changed, cancel the sorting; the
 436   
          // sorting columns may have been either moved or deleted from
 437   
          // the model.
 438  0
          if ( e.getFirstRow() == TableModelEvent.HEADER_ROW ) {
 439  0
             cancelSorting();
 440  0
             fireTableChanged( e );
 441  0
             return;
 442   
          }
 443   
 
 444   
          // We can map a cell event through to the view without widening
 445   
          // when the following conditions apply:
 446   
          //
 447   
          // a) all the changes are on one row (e.getFirstRow() == e.getLastRow()) and,
 448   
          // b) all the changes are in one column (column != TableModelEvent.ALL_COLUMNS) and,
 449   
          // c) we are not sorting on that column (getSortingStatus(column) == NOT_SORTED) and,
 450   
          // d) a reverse lookup will not trigger a sort (modelToView != null)
 451   
          //
 452   
          // Note: INSERT and DELETE events fail this test as they have column == ALL_COLUMNS.
 453   
          //
 454   
          // The last check, for (modelToView != null) is to see if modelToView
 455   
          // is already allocated. If we don't do this check; sorting can become
 456   
          // a performance bottleneck for applications where cells
 457   
          // change rapidly in different parts of the table. If cells
 458   
          // change alternately in the sorting column and then outside of
 459   
          // it this class can end up re-sorting on alternate cell updates -
 460   
          // which can be a performance problem for large tables. The last
 461   
          // clause avoids this problem.
 462  0
          int column = e.getColumn();
 463  0
          if ( e.getFirstRow() == e.getLastRow()
 464   
                && column != TableModelEvent.ALL_COLUMNS
 465   
                && getSortingStatus( column ) == NOT_SORTED
 466   
                && modelToView != null ) {
 467  0
             int viewIndex = getModelToView()[e.getFirstRow()];
 468  0
             fireTableChanged( new TableModelEvent( TableSorter.this, viewIndex,
 469   
                   viewIndex, column, e.getType() ) );
 470  0
             return;
 471   
          }
 472   
 
 473   
          // Something has happened to the data that may have invalidated the row order.
 474  0
          clearSortingState();
 475  0
          fireTableDataChanged();
 476  0
          return;
 477   
       }
 478   
    }
 479   
 
 480   
    private class MouseHandler extends MouseAdapter {
 481  0
       public void mouseClicked( MouseEvent e ) {
 482  0
          if ( e.getButton() == MouseEvent.BUTTON1 ) {
 483  0
             JTableHeader h = ( JTableHeader ) e.getSource();
 484  0
             TableColumnModel columnModel = h.getColumnModel();
 485  0
             int viewColumn = columnModel.getColumnIndexAtX( e.getX() );
 486  0
             int column = columnModel.getColumn( viewColumn ).getModelIndex();
 487  0
             if ( column != -1 ) {
 488  0
                int status = getSortingStatus( column );
 489  0
                if ( !e.isControlDown() ) {
 490  0
                   cancelSorting();
 491   
                }
 492   
                // Cycle the sorting states through {ASCENDING, DESCENDING} ignoring NOT_SORTED
 493  0
                status = ( status == ASCENDING ) ? DESCENDING : ASCENDING;
 494  0
                setSortingStatus( column, status );
 495   
             }
 496   
          }
 497   
       }
 498   
    }
 499   
 
 500   
    private static class Arrow implements Icon {
 501   
       private boolean descending;
 502   
       private int size;
 503   
       private int priority;
 504   
 
 505  0
       public Arrow( boolean descending, int size, int priority ) {
 506  0
          this.descending = descending;
 507  0
          this.size = size;
 508  0
          this.priority = priority;
 509   
       }
 510   
 
 511  0
       public void paintIcon( Component c, Graphics g, int x, int y ) {
 512  0
          Color color = c == null ? Color.GRAY : c.getBackground();
 513   
          // In a compound sort, make each succesive triangle 20%
 514   
          // smaller than the previous one.
 515  0
          int dx = ( int ) ( size / 2 * Math.pow( 0.8, priority ) );
 516  0
          int dy = descending ? dx : -dx;
 517   
          // Align icon (roughly) with font baseline.
 518  0
          y = y + 5 * size / 6 + ( descending ? -dy : 0 );
 519  0
          int shift = descending ? 1 : -1;
 520  0
          g.translate( x, y );
 521   
 
 522   
          // Right diagonal.
 523  0
          g.setColor( color.darker() );
 524  0
          g.drawLine( dx / 2, dy, 0, 0 );
 525  0
          g.drawLine( dx / 2, dy + shift, 0, shift );
 526   
 
 527   
          // Left diagonal.
 528  0
          g.setColor( color.brighter() );
 529  0
          g.drawLine( dx / 2, dy, dx, 0 );
 530  0
          g.drawLine( dx / 2, dy + shift, dx, shift );
 531   
 
 532   
          // Horizontal line.
 533  0
          if ( descending ) {
 534  0
             g.setColor( color.darker().darker() );
 535   
          } else {
 536  0
             g.setColor( color.brighter().brighter() );
 537   
          }
 538  0
          g.drawLine( dx, 0, 0, 0 );
 539   
 
 540  0
          g.setColor( color );
 541  0
          g.translate( -x, -y );
 542   
       }
 543   
 
 544  0
       public int getIconWidth() {
 545  0
          return size;
 546   
       }
 547   
 
 548  0
       public int getIconHeight() {
 549  0
          return size;
 550   
       }
 551   
    }
 552   
 
 553   
    private class SortableHeaderRenderer implements TableCellRenderer {
 554   
       TableCellRenderer tableCellRenderer;
 555   
 
 556  0
       public SortableHeaderRenderer( TableCellRenderer tableCellRenderer ) {
 557  0
          this.tableCellRenderer = tableCellRenderer;
 558   
       }
 559   
 
 560  0
       public Component getTableCellRendererComponent( JTable table,
 561   
             Object value, boolean isSelected, boolean hasFocus, int row,
 562   
             int column ) {
 563  0
          Component c = tableCellRenderer.getTableCellRendererComponent( table,
 564   
                value, isSelected, hasFocus, row, column );
 565  0
          if ( c instanceof JLabel ) {
 566  0
             JLabel l = ( JLabel ) c;
 567  0
             l.setHorizontalTextPosition( SwingConstants.LEFT );
 568  0
             l.setVerticalAlignment( SwingConstants.BOTTOM );
 569  0
             int modelColumn = table.convertColumnIndexToModel( column );
 570  0
             l.setIcon( getHeaderRendererIcon( modelColumn, l.getFont()
 571   
                   .getSize() ) );
 572   
          }
 573  0
          return c;
 574   
       }
 575   
    }
 576   
 
 577   
    private static class Directive {
 578   
       int column;
 579   
       int direction;
 580   
 
 581  0
       public Directive( int column, int direction ) {
 582  0
          this.column = column;
 583  0
          this.direction = direction;
 584   
       }
 585   
    }
 586   
 }