Clover coverage report - Common - 1.0.0
Coverage timestamp: sam. déc. 27 2003 15:13:46 CET
file stats: LOC: 488   Methods: 17
NCLOC: 292   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
TableModelSorter.java 0% 0% 0% 0%
coverage
 1   
 /*
 2   
  * EJTools, the Enterprise Java Tools
 3   
  *
 4   
  * Distributable under LGPL license.
 5   
  * See terms of license at www.gnu.org.
 6   
  */
 7   
 package org.ejtools.swing.table;
 8   
 
 9   
 import java.awt.event.InputEvent;
 10   
 import java.awt.event.MouseAdapter;
 11   
 import java.awt.event.MouseEvent;
 12   
 import java.util.Date;
 13   
 import java.util.Vector;
 14   
 
 15   
 import javax.swing.JTable;
 16   
 import javax.swing.event.TableModelEvent;
 17   
 import javax.swing.table.JTableHeader;
 18   
 import javax.swing.table.TableColumnModel;
 19   
 import javax.swing.table.TableModel;
 20   
 
 21   
 /**
 22   
  * @author    Laurent Etiemble
 23   
  * @version   $Revision: 1.8 $
 24   
  */
 25   
 public class TableModelSorter extends TableModelIndexed
 26   
 {
 27   
    /** Description of the Field */
 28   
    protected boolean ascending = true;
 29   
    /** Description of the Field */
 30   
    protected int column = -1;
 31   
    /** Description of the Field */
 32   
    protected int compares;
 33   
    /** Description of the Field */
 34   
    protected Vector sortingColumns = new Vector();
 35   
 
 36   
 
 37   
    /**
 38   
     * Constructor for TableModelSorter.
 39   
     *
 40   
     * @param model
 41   
     */
 42  0
    public TableModelSorter(TableModel model)
 43   
    {
 44  0
       super(model);
 45  0
       this.reallocateIndexes();
 46   
    }
 47   
 
 48   
 
 49   
    /**
 50   
     * Add a mouse listener to the Table to trigger a table sort when a column
 51   
     * heading is clicked in the JTable.
 52   
     *
 53   
     * @param table  The feature to be added to the MouseListenerToHeaderInTable
 54   
     *      attribute
 55   
     */
 56  0
    public void addMouseListenerToHeaderInTable(JTable table)
 57   
    {
 58  0
       final TableModelSorter tableSorter = this;
 59  0
       final JTable tableView = table;
 60  0
       tableView.setColumnSelectionAllowed(false);
 61  0
       MouseAdapter listMouseListener =
 62   
          new MouseAdapter()
 63   
          {
 64  0
             public void mouseClicked(MouseEvent e)
 65   
             {
 66  0
                TableColumnModel columnModel = tableView.getColumnModel();
 67  0
                int viewColumn = columnModel.getColumnIndexAtX(e.getX());
 68  0
                int column = tableView.convertColumnIndexToModel(viewColumn);
 69  0
                if (e.getClickCount() == 1 && column != -1)
 70   
                {
 71  0
                   int shiftPressed = e.getModifiers() & InputEvent.SHIFT_MASK;
 72  0
                   boolean ascending = (shiftPressed == 0);
 73  0
                   tableSorter.sortByColumn(column, ascending);
 74   
                }
 75   
             }
 76   
          };
 77  0
       JTableHeader th = tableView.getTableHeader();
 78  0
       th.addMouseListener(listMouseListener);
 79   
    }
 80   
 
 81   
 
 82   
    /** Description of the Method */
 83  0
    public void checkModel()
 84   
    {
 85  0
       if (indexes.length != model.getRowCount())
 86   
       {
 87  0
          System.err.println("Sorter not informed of a change in model.");
 88   
       }
 89   
    }
 90   
 
 91   
 
 92   
    /**
 93   
     * Description of the Method
 94   
     *
 95   
     * @param row1  Description of the Parameter
 96   
     * @param row2  Description of the Parameter
 97   
     * @return      Description of the Return Value
 98   
     */
 99  0
    public int compare(int row1, int row2)
 100   
    {
 101  0
       this.compares++;
 102  0
       for (int level = 0; level < this.sortingColumns.size(); level++)
 103   
       {
 104  0
          Integer column = (Integer) this.sortingColumns.elementAt(level);
 105  0
          int result = this.compareRowsByColumn(row1, row2, column.intValue());
 106  0
          if (result != 0)
 107   
          {
 108  0
             return this.ascending ? result : -result;
 109   
          }
 110   
       }
 111  0
       return 0;
 112   
    }
 113   
 
 114   
 
 115   
 
 116   
    /**
 117   
     * Description of the Method
 118   
     *
 119   
     * @param row1    Description of the Parameter
 120   
     * @param row2    Description of the Parameter
 121   
     * @param column  Description of the Parameter
 122   
     * @return        Description of the Return Value
 123   
     */
 124  0
    public int compareRowsByColumn(int row1, int row2, int column)
 125   
    {
 126  0
       Class type = this.model.getColumnClass(column);
 127  0
       TableModel data = this.model;
 128   
 
 129   
       // Check for nulls.
 130  0
       Object o1 = data.getValueAt(row1, column);
 131  0
       Object o2 = data.getValueAt(row2, column);
 132   
 
 133   
       // If both values are null, return 0.
 134  0
       if (o1 == null && o2 == null)
 135   
       {
 136  0
          return 0;
 137   
       }
 138  0
       else if (o1 == null)
 139   
       {
 140   
          // Define null less than everything.
 141  0
          return -1;
 142   
       }
 143  0
       else if (o2 == null)
 144   
       {
 145  0
          return 1;
 146   
       }
 147   
 
 148   
       /*
 149   
        * We copy all returned values from the getValue call in case
 150   
        * an optimised model is reusing one object to return many
 151   
        * values.  The Number subclasses in the JDK are immutable and
 152   
        * so will not be used in this way but other subclasses of
 153   
        * Number might want to do this to save space and avoid
 154   
        * unnecessary heap allocation.
 155   
        */
 156  0
       if (Comparable.class.isAssignableFrom(type))
 157   
       {
 158  0
          Comparable n1 = (Comparable) data.getValueAt(row1, column);
 159  0
          Comparable n2 = (Comparable) data.getValueAt(row2, column);
 160   
 
 161  0
          int result = n1.compareTo(n2);
 162   
 
 163  0
          if (result < 0)
 164   
          {
 165  0
             return -1;
 166   
          }
 167  0
          else if (result > 0)
 168   
          {
 169  0
             return 1;
 170   
          }
 171   
          else
 172   
          {
 173  0
             return 0;
 174   
          }
 175   
       }
 176  0
       else if (Number.class.isAssignableFrom(type))
 177   
       {
 178  0
          Number n1 = (Number) data.getValueAt(row1, column);
 179  0
          double d1 = n1.doubleValue();
 180  0
          Number n2 = (Number) data.getValueAt(row2, column);
 181  0
          double d2 = n2.doubleValue();
 182   
 
 183  0
          if (d1 < d2)
 184   
          {
 185  0
             return -1;
 186   
          }
 187  0
          else if (d1 > d2)
 188   
          {
 189  0
             return 1;
 190   
          }
 191   
          else
 192   
          {
 193  0
             return 0;
 194   
          }
 195   
       }
 196  0
       else if (type == Date.class)
 197   
       {
 198  0
          Date d1 = (Date) data.getValueAt(row1, column);
 199  0
          long n1 = d1.getTime();
 200  0
          Date d2 = (Date) data.getValueAt(row2, column);
 201  0
          long n2 = d2.getTime();
 202   
 
 203  0
          if (n1 < n2)
 204   
          {
 205  0
             return -1;
 206   
          }
 207  0
          else if (n1 > n2)
 208   
          {
 209  0
             return 1;
 210   
          }
 211   
          else
 212   
          {
 213  0
             return 0;
 214   
          }
 215   
       }
 216  0
       else if (type == String.class)
 217   
       {
 218  0
          String s1 = (String) data.getValueAt(row1, column);
 219  0
          String s2 = (String) data.getValueAt(row2, column);
 220  0
          int result = s1.compareTo(s2);
 221   
 
 222  0
          if (result < 0)
 223   
          {
 224  0
             return -1;
 225   
          }
 226  0
          else if (result > 0)
 227   
          {
 228  0
             return 1;
 229   
          }
 230   
          else
 231   
          {
 232  0
             return 0;
 233   
          }
 234   
       }
 235  0
       else if (type == Boolean.class)
 236   
       {
 237  0
          Boolean bool1 = (Boolean) data.getValueAt(row1, column);
 238  0
          boolean b1 = bool1.booleanValue();
 239  0
          Boolean bool2 = (Boolean) data.getValueAt(row2, column);
 240  0
          boolean b2 = bool2.booleanValue();
 241   
 
 242  0
          if (b1 == b2)
 243   
          {
 244  0
             return 0;
 245   
          }
 246  0
          else if (b1)
 247   
          {
 248   
             // Define false < true
 249  0
             return 1;
 250   
          }
 251   
          else
 252   
          {
 253  0
             return -1;
 254   
          }
 255   
       }
 256   
       else
 257   
       {
 258  0
          String comp1 = o1.toString();
 259  0
          String comp2 = o2.toString();
 260   
 
 261  0
          int result = comp1.compareTo(comp2);
 262   
 
 263  0
          if (result < 0)
 264   
          {
 265  0
             return -1;
 266   
          }
 267  0
          else if (result > 0)
 268   
          {
 269  0
             return 1;
 270   
          }
 271   
          else
 272   
          {
 273  0
             return 0;
 274   
          }
 275   
       }
 276   
    }
 277   
 
 278   
 
 279   
    /**
 280   
     * Gets the column attribute of the TableModelSorter object
 281   
     *
 282   
     * @return   The column value
 283   
     */
 284  0
    public int getColumn()
 285   
    {
 286  0
       return this.column;
 287   
    }
 288   
 
 289   
 
 290   
    /**
 291   
     * Gets the valueAt attribute of the TableModelSorter object
 292   
     *
 293   
     * @param aRow     Description of the Parameter
 294   
     * @param aColumn  Description of the Parameter
 295   
     * @return         The valueAt value
 296   
     */
 297  0
    public Object getValueAt(int aRow, int aColumn)
 298   
    {
 299  0
       this.checkModel();
 300  0
       return super.getValueAt(aRow, aColumn);
 301   
    }
 302   
 
 303   
 
 304   
    /**
 305   
     * Gets the ascending attribute of the TableModelSorter object
 306   
     *
 307   
     * @return   The ascending value
 308   
     */
 309  0
    public boolean isAscending()
 310   
    {
 311  0
       return this.ascending;
 312   
    }
 313   
 
 314   
 
 315   
    /** Description of the Method */
 316  0
    public void n2sort()
 317   
    {
 318  0
       for (int i = 0; i < getRowCount(); i++)
 319   
       {
 320  0
          for (int j = i + 1; j < getRowCount(); j++)
 321   
          {
 322  0
             if (compare(indexes[i], indexes[j]) == -1)
 323   
             {
 324  0
                this.swap(i, j);
 325   
             }
 326   
          }
 327   
       }
 328   
    }
 329   
 
 330   
 
 331   
 
 332   
    /** Description of the Method */
 333  0
    public void reallocateIndexes()
 334   
    {
 335  0
       int rowCount = model.getRowCount();
 336  0
       this.indexes = new int[rowCount];
 337   
 
 338   
       // Initialise with the identity mapping.
 339  0
       for (int row = 0; row < rowCount; row++)
 340   
       {
 341  0
          indexes[row] = row;
 342   
       }
 343   
    }
 344   
 
 345   
 
 346   
    /**
 347   
     * Sets the valueAt attribute of the TableModelSorter object
 348   
     *
 349   
     * @param aValue   The new valueAt value
 350   
     * @param aRow     The new valueAt value
 351   
     * @param aColumn  The new valueAt value
 352   
     */
 353  0
    public void setValueAt(Object aValue, int aRow, int aColumn)
 354   
    {
 355  0
       this.checkModel();
 356  0
       super.setValueAt(aValue, aRow, aColumn);
 357   
    }
 358   
 
 359   
 
 360   
 
 361   
    /**
 362   
     * This is a home-grown implementation which we have not had time to research
 363   
     * - it may perform poorly in some circumstances. It requires twice the space
 364   
     * of an in-place algorithm and makes NlogN assigments shuttling the values
 365   
     * between the two arrays. The number of compares appears to vary between N-1
 366   
     * and NlogN depending on the initial order but the main reason for using it
 367   
     * here is that, unlike qsort, it is stable.
 368   
     *
 369   
     * @param from  Description of the Parameter
 370   
     * @param to    Description of the Parameter
 371   
     * @param low   Description of the Parameter
 372   
     * @param high  Description of the Parameter
 373   
     */
 374  0
    public void shuttlesort(int from[], int to[], int low, int high)
 375   
    {
 376  0
       if (high - low < 2)
 377   
       {
 378  0
          return;
 379   
       }
 380  0
       int middle = (low + high) / 2;
 381   
 
 382  0
       this.shuttlesort(to, from, low, middle);
 383  0
       this.shuttlesort(to, from, middle, high);
 384   
 
 385  0
       int p = low;
 386  0
       int q = middle;
 387   
 
 388   
       /*
 389   
        * This is an optional short-cut; at each recursive call,
 390   
        * check to see if the elements in this subset are already
 391   
        * ordered.  If so, no further comparisons are needed; the
 392   
        * sub-array can just be copied.  The array must be copied rather
 393   
        * than assigned otherwise sister calls in the recursion might
 394   
        * get out of sinc.  When the number of elements is three they
 395   
        * are partitioned so that the first set, [low, mid), has one
 396   
        * element and and the second, [mid, high), has two. We skip the
 397   
        * optimisation when the number of elements is three or less as
 398   
        * the first compare in the normal merge will produce the same
 399   
        * sequence of steps. This optimisation seems to be worthwhile
 400   
        * for partially ordered lists but some analysis is needed to
 401   
        * find out how the performance drops to Nlog(N) as the initial
 402   
        * order diminishes - it may drop very quickly.
 403   
        */
 404  0
       if (high - low >= 4 && this.compare(from[middle - 1], from[middle]) <= 0)
 405   
       {
 406  0
          for (int i = low; i < high; i++)
 407   
          {
 408  0
             to[i] = from[i];
 409   
          }
 410  0
          return;
 411   
       }
 412   
 
 413   
       // A normal merge.
 414  0
       for (int i = low; i < high; i++)
 415   
       {
 416  0
          if (q >= high || (p < middle && this.compare(from[p], from[q]) <= 0))
 417   
          {
 418  0
             to[i] = from[p++];
 419   
          }
 420   
          else
 421   
          {
 422  0
             to[i] = from[q++];
 423   
          }
 424   
       }
 425   
    }
 426   
 
 427   
 
 428   
    /**
 429   
     * Description of the Method
 430   
     *
 431   
     * @param sender  Description of the Parameter
 432   
     */
 433  0
    public void sort(Object sender)
 434   
    {
 435  0
       this.checkModel();
 436   
 
 437  0
       this.compares = 0;
 438   
       // n2sort();
 439   
       // qsort(0, indexes.length-1);
 440  0
       this.shuttlesort((int[]) indexes.clone(), indexes, 0, indexes.length);
 441   
       //System.out.println("Compares: "+compares);
 442   
    }
 443   
 
 444   
 
 445   
 
 446   
    /**
 447   
     * Description of the Method
 448   
     *
 449   
     * @param column  Description of the Parameter
 450   
     */
 451  0
    public void sortByColumn(int column)
 452   
    {
 453  0
       this.sortByColumn(column, true);
 454   
    }
 455   
 
 456   
 
 457   
    /**
 458   
     * Description of the Method
 459   
     *
 460   
     * @param column     Description of the Parameter
 461   
     * @param ascending  Description of the Parameter
 462   
     */
 463  0
    public void sortByColumn(int column, boolean ascending)
 464   
    {
 465  0
       this.ascending = ascending;
 466  0
       this.sortingColumns.removeAllElements();
 467  0
       this.sortingColumns.addElement(new Integer(column));
 468  0
       this.sort(this);
 469  0
       super.tableChanged(new TableModelEvent(this));
 470   
    }
 471   
 
 472   
 
 473   
    /**
 474   
     * Description of the Method
 475   
     *
 476   
     * @param event  Description of the Parameter
 477   
     */
 478  0
    public void tableChanged(TableModelEvent event)
 479   
    {
 480  0
       this.reallocateIndexes();
 481   
 //      if (column >= 0)
 482   
 //      {
 483   
 //         this.sortByColumn(column, ascending);
 484   
 //      }
 485  0
       super.tableChanged(event);
 486   
    }
 487   
 }
 488