source: src/main/java/weka/gui/SortedTableModel.java @ 10

Last change on this file since 10 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 10.8 KB
Line 
1/*
2 *    This program is free software; you can redistribute it and/or modify
3 *    it under the terms of the GNU General Public License as published by
4 *    the Free Software Foundation; either version 2 of the License, or
5 *    (at your option) any later version.
6 *
7 *    This program is distributed in the hope that it will be useful,
8 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *    GNU General Public License for more details.
11 *
12 *    You should have received a copy of the GNU General Public License
13 *    along with this program; if not, write to the Free Software
14 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17/*
18 * SortedTableModel.java
19 * Copyright (C) 2005 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.gui;
24
25import weka.core.ClassDiscovery;
26
27import java.awt.event.InputEvent;
28import java.awt.event.MouseAdapter;
29import java.awt.event.MouseEvent;
30import java.util.Date;
31
32import javax.swing.JTable;
33import javax.swing.event.TableModelEvent;
34import javax.swing.event.TableModelListener;
35import javax.swing.table.JTableHeader;
36import javax.swing.table.TableColumnModel;
37import javax.swing.table.TableModel;
38import javax.swing.table.AbstractTableModel;
39
40/**
41 * Represents a TableModel with sorting functionality.
42 *
43 * @author FracPete (fracpete at waikato dot ac dot nz)
44 * @version $Revision: 1.3 $
45 */
46
47public class SortedTableModel
48  extends AbstractTableModel
49  implements TableModelListener {
50
51  /** for serialization */
52  static final long serialVersionUID = 4030907921461127548L;
53 
54  /** the actual table model */
55  protected TableModel mModel;
56
57  /** the mapping between displayed and actual index */
58  protected int[] mIndices;
59
60  /** the sort column */
61  protected int mSortColumn;
62
63  /** whether sorting is ascending or descending */
64  protected boolean mAscending;
65 
66  /**
67   * initializes with no model
68   */
69  public SortedTableModel() {
70    this(null);
71  }
72
73  /**
74   * initializes with the given model
75   *
76   * @param model       the model to initialize the sorted model with
77   */
78  public SortedTableModel(TableModel model) {
79    setModel(model);
80  }
81
82  /**
83   * sets the model to use
84   *
85   * @param value       the model to use
86   */
87  public void setModel(TableModel value) {
88    mModel = value;
89
90    // initialize indices
91    if (mModel == null) {
92      mIndices = null;
93    }
94    else {
95      initializeIndices();
96      mSortColumn = -1;
97      mAscending  = true;
98      mModel.addTableModelListener(this);
99    }
100  }
101
102  /**
103   * (re-)initializes the indices
104   */
105  protected void initializeIndices() {
106    int       i;
107
108    mIndices = new int[mModel.getRowCount()];
109    for (i = 0; i < mIndices.length; i++)
110      mIndices[i] = i;
111  }
112
113  /**
114   * returns the current model, can be null
115   *
116   * @return            the current model
117   */
118  public TableModel getModel() {
119    return mModel;
120  }
121
122  /**
123   * returns whether the table was sorted
124   *
125   * @return        true if the table was sorted
126   */
127  public boolean isSorted() {
128    return (mSortColumn > -1);
129  }
130
131  /**
132   * whether the model is initialized
133   *
134   * @return            true if the model is not null and the sort indices
135   *                    match the number of rows
136   */
137  protected boolean isInitialized() {
138    return (getModel() != null);
139  }
140
141  /**
142   * Returns the actual underlying row the given visible one represents. Useful
143   * for retrieving "non-visual" data that is also stored in a TableModel.
144   *
145   * @param visibleRow  the displayed row to retrieve the original row for
146   * @return            the original row
147   */
148  public int getActualRow(int visibleRow) {
149    if (!isInitialized())
150      return -1;
151    else
152      return mIndices[visibleRow];
153  }
154 
155  /**
156   * Returns the most specific superclass for all the cell values in the
157   * column.
158   *
159   * @param columnIndex     the index of the column
160   * @return                the class of the specified column
161   */
162  public Class getColumnClass(int columnIndex) {
163    if (!isInitialized())
164      return null;
165    else
166      return getModel().getColumnClass(columnIndex);
167  }
168
169  /**
170   * Returns the number of columns in the model
171   *
172   * @return          the number of columns in the model
173   */
174  public int getColumnCount() {
175    if (!isInitialized())
176      return 0;
177    else
178      return getModel().getColumnCount();
179  }
180
181  /**
182   * Returns the name of the column at columnIndex
183   *
184   * @param columnIndex   the column to retrieve the name for
185   * @return              the name of the specified column
186   */
187  public String getColumnName(int columnIndex) {
188    if (!isInitialized())
189      return null;
190    else
191      return getModel().getColumnName(columnIndex);
192  }
193
194  /**
195   * Returns the number of rows in the model.
196   *
197   * @return              the number of rows in the model
198   */
199  public int getRowCount() {
200    if (!isInitialized())
201      return 0;
202    else
203      return getModel().getRowCount();
204  }
205
206  /**
207   * Returns the value for the cell at columnIndex and rowIndex.
208   *
209   * @param rowIndex      the row
210   * @param columnIndex   the column
211   * @return              the value of the sepcified cell
212   */
213  public Object getValueAt(int rowIndex, int columnIndex) {
214    if (!isInitialized())
215      return null;
216    else
217      return getModel().getValueAt(mIndices[rowIndex], columnIndex);
218  }
219
220  /**
221   * Returns true if the cell at rowIndex and columnIndex is editable.
222   *
223   * @param rowIndex      the row
224   * @param columnIndex   the column
225   * @return              true if the cell is editable
226   */
227  public boolean isCellEditable(int rowIndex, int columnIndex) {
228    if (!isInitialized())
229      return false;
230    else
231      return getModel().isCellEditable(mIndices[rowIndex], columnIndex);
232  }
233
234  /**
235   * Sets the value in the cell at columnIndex and rowIndex to aValue.
236   *
237   * @param aValue        the new value of the cell
238   * @param rowIndex      the row
239   * @param columnIndex   the column
240   */
241  public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
242    if (isInitialized())
243      getModel().setValueAt(aValue, mIndices[rowIndex], columnIndex);
244  }
245
246  /**
247   * sorts the table over the given column (ascending)
248   *
249   * @param columnIndex     the column to sort over
250   */
251  public void sort(int columnIndex) {
252    sort(columnIndex, true);
253  }
254
255  /**
256   * sorts the table over the given column, either ascending or descending
257   *
258   * @param columnIndex     the column to sort over
259   * @param ascending       ascending if true, otherwise descending
260   */
261  public void sort(int columnIndex, boolean ascending) {
262    int       columnType;
263    int       i;
264    int       n;
265    int       index;
266    int       backup;
267
268    // can we sort?
269    if (    (!isInitialized())
270         || (getModel().getRowCount() != mIndices.length) ) {
271
272      System.out.println(
273          this.getClass().getName() + ": Table model not initialized!");
274
275      return;
276    }
277
278    // init
279    mSortColumn = columnIndex;
280    mAscending  = ascending;
281    initializeIndices();
282   
283    // determine the column type: 0=string/other, 1=number, 2=date
284    if (ClassDiscovery.isSubclass(Number.class, getColumnClass(mSortColumn)))
285      columnType = 1;
286    else if (ClassDiscovery.isSubclass(Date.class, getColumnClass(mSortColumn)))
287      columnType = 2;
288    else
289      columnType = 0;
290
291    // sort ascending (descending is done below)
292    for (i = 0; i < getRowCount() - 1; i++) {
293      index = i;
294      for (n = i + 1; n < getRowCount(); n++) {
295        if (compare(mIndices[index], mIndices[n], mSortColumn, columnType) > 0)
296          index = n;
297      }
298
299      // found smaller one?
300      if (index != i) {
301        backup          = mIndices[i];
302        mIndices[i]     = mIndices[index];
303        mIndices[index] = backup;
304      }
305    }
306
307    // reverse sorting?
308    if (!mAscending) {
309      for (i = 0; i < getRowCount() / 2; i++) {
310        backup                          = mIndices[i];
311        mIndices[i]                     = mIndices[getRowCount() - i - 1];
312        mIndices[getRowCount() - i - 1] = backup;
313      }
314    }
315  }
316
317  /**
318   * compares two cells, returns -1 if cell1 is less than cell2, 0 if equal
319   * and +1 if greater.
320   *
321   * @param row1        row index of cell1
322   * @param row2        row index of cell2
323   * @param col         colunm index
324   * @param type        the class type: 0=string/other, 1=number, 2=date
325   * @return            -1 if cell1&lt;cell2, 0 if cell1=cell2, +1 if
326   *                    cell1&gt;cell2
327   */
328  protected int compare(int row1, int row2, int col, int type) {
329    int           result;
330    Object        o1;
331    Object        o2;
332    Double        d1;
333    Double        d2;
334
335    o1 = getModel().getValueAt(row1, col);
336    o2 = getModel().getValueAt(row2, col);
337
338    // null is always smaller than non-null values
339    if ( (o1 == null) &&  (o2 == null) ) {
340      result = 0;
341    }
342    else if (o1 == null) {
343      result = -1;
344    }
345    else if (o2 == null) {
346      result = 1;
347    }
348    else {
349      switch (type) {
350        // number
351        case 1:
352          d1 = new Double(((Number) o1).doubleValue());
353          d2 = new Double(((Number) o2).doubleValue());
354          result = d1.compareTo(d2);
355          break;
356         
357        // date
358        case 2:
359          result = ((Date) o1).compareTo((Date) o2);
360          break;
361         
362        // string
363        default:
364          result = o1.toString().compareTo(o2.toString());
365          break;
366      }
367    }
368
369    return result;
370  }
371
372  /**
373   * This fine grain notification tells listeners the exact range of cells,
374   * rows, or columns that changed.
375   *
376   * @param e       the event
377   */
378  public void tableChanged(TableModelEvent e) {
379    initializeIndices();
380    if (isSorted())
381      sort(mSortColumn, mAscending);
382   
383    fireTableChanged(e);
384  }
385 
386  /**
387   * adds a mouselistener to the header
388   *
389   * @param table       the table to add the listener to
390   */
391  public void addMouseListenerToHeader(JTable table) {
392    final SortedTableModel modelFinal = this;
393    final JTable tableFinal = table;
394    tableFinal.setColumnSelectionAllowed(false);
395    JTableHeader header = tableFinal.getTableHeader();
396
397    if (header != null) {
398      MouseAdapter listMouseListener = new MouseAdapter() {
399        public void mouseClicked(MouseEvent e) {
400          TableColumnModel columnModel = tableFinal.getColumnModel();
401          int viewColumn = columnModel.getColumnIndexAtX(e.getX());
402          int column = tableFinal.convertColumnIndexToModel(viewColumn);
403          if (    e.getButton() == MouseEvent.BUTTON1 
404               && e.getClickCount() == 1 
405               && !e.isAltDown()
406               && column != -1 ) {
407            int shiftPressed = e.getModifiers() & InputEvent.SHIFT_MASK;
408            boolean ascending = (shiftPressed == 0);
409            modelFinal.sort(column, ascending);
410          }
411        }
412      };
413     
414      header.addMouseListener(listMouseListener);
415    }
416  }
417}
Note: See TracBrowser for help on using the repository browser.