001/* DefaultTableModel.java --
002   Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.swing.table;
040
041import java.io.Serializable;
042import java.util.Vector;
043
044import javax.swing.event.TableModelEvent;
045
046/**
047 * A two dimensional data structure used to store <code>Object</code> 
048 * instances, usually for display in a <code>JTable</code> component.
049 * 
050 * @author      Andrew Selkirk
051 */
052public class DefaultTableModel extends AbstractTableModel
053  implements Serializable
054{
055  static final long serialVersionUID = 6680042567037222321L;
056
057  /**
058   * Storage for the rows in the table (each row is itself 
059   * a <code>Vector</code>).
060   */
061  protected Vector dataVector;
062
063  /**
064   * Storage for the column identifiers.
065   */
066  protected Vector columnIdentifiers;
067
068  /**
069   * Creates an empty table with zero rows and zero columns.
070   */
071  public DefaultTableModel() 
072  {
073    this(0, 0);
074  }
075  
076  /**
077   * Creates a new table with the specified number of rows and columns.
078   * All cells in the table are initially empty (set to <code>null</code>).
079   * 
080   * @param numRows  the number of rows.
081   * @param numColumns  the number of columns.
082   */
083  public DefaultTableModel(int numRows, int numColumns) 
084  {
085    Vector defaultNames = new Vector(numColumns);
086    Vector data = new Vector(numRows);
087    for (int i = 0; i < numColumns; i++) 
088      {
089        defaultNames.add(super.getColumnName(i));
090      }          
091    for (int r = 0; r < numRows; r++) 
092      {
093        Vector tmp = new Vector(numColumns);
094        tmp.setSize(numColumns);
095        data.add(tmp);
096      }
097    setDataVector(data, defaultNames);
098  }
099  
100  /**
101   * Creates a new table with the specified column names and number of
102   * rows.  The number of columns is determined by the number of column
103   * names supplied.
104   *   
105   * @param columnNames the column names.
106   * @param numRows the number of rows.
107   */
108  public DefaultTableModel(Vector columnNames, int numRows) 
109  {
110    if (numRows < 0)
111      throw new IllegalArgumentException("numRows < 0");
112    Vector data = new Vector();
113    int numColumns = 0;
114
115    if (columnNames != null)
116      numColumns = columnNames.size();
117    
118    while (0 < numRows--) 
119      {
120        Vector rowData = new Vector();
121        rowData.setSize(numColumns);
122        data.add(rowData);
123      }
124    setDataVector(data, columnNames);
125  }
126
127  /**
128   * Creates a new table with the specified column names and row count.
129   * 
130   * @param columnNames the column names.
131   * @param numRows the number of rows.
132   */
133  public DefaultTableModel(Object[] columnNames, int numRows) 
134  {
135    this(convertToVector(columnNames), numRows);
136  }
137  
138  /**
139   * Creates a new table with the specified data values and column names.
140   * 
141   * @param data the data values.
142   * @param columnNames the column names.
143   */
144  public DefaultTableModel(Vector data, Vector columnNames) 
145  {
146    setDataVector(data, columnNames);
147  }
148
149  /**
150   * Creates a new table with the specified data values and column names.
151   * 
152   * @param data the data values.
153   * @param columnNames the column names.
154   */
155  public DefaultTableModel(Object[][] data, Object[] columnNames) 
156  {
157    this(convertToVector(data), convertToVector(columnNames));
158  }
159
160  /**
161   * Returns the vector containing the row data for the table.
162   * 
163   * @return The data vector.
164   */
165  public Vector getDataVector() 
166  {
167    return dataVector;
168  }
169
170  /**
171   * Sets the data and column identifiers for the table.  The data vector
172   * contains a <code>Vector</code> for each row in the table - if the
173   * number of objects in each row does not match the number of column
174   * names specified, the row data is truncated or expanded (by adding
175   * <code>null</code> values) as required.
176   * 
177   * @param data the data for the table (a vector of row vectors).
178   * @param columnNames the column names.
179   * 
180   * @throws NullPointerException if either argument is <code>null</code>.
181   */
182  public void setDataVector(Vector data, Vector columnNames) 
183  {
184    if (data == null)
185      dataVector = new Vector();
186    else
187      dataVector = data;
188    setColumnIdentifiers(columnNames);
189  }
190
191  /**
192   * Sets the data and column identifiers for the table.
193   * 
194   * @param data the data for the table.
195   * @param columnNames the column names.
196   * 
197   * @throws NullPointerException if either argument is <code>null</code>.
198   */
199  public void setDataVector(Object[][] data, Object[] columnNames) 
200  {
201    setDataVector(convertToVector(data), 
202                  convertToVector(columnNames));
203  }
204  
205  /**
206   * Sends the specified <code>event</code> to all registered listeners.
207   * This method is equivalent to 
208   * {@link AbstractTableModel#fireTableChanged(TableModelEvent)}.
209   * 
210   * @param event the event.
211   */
212  public void newDataAvailable(TableModelEvent event) 
213  {
214    fireTableChanged(event);
215  }
216
217  /**
218   * Sends the specified <code>event</code> to all registered listeners.
219   * This method is equivalent to 
220   * {@link AbstractTableModel#fireTableChanged(TableModelEvent)}.
221   * 
222   * @param event the event.
223   */
224  public void newRowsAdded(TableModelEvent event) 
225  {
226    fireTableChanged(event);
227  }
228
229  /**
230   * Sends the specified <code>event</code> to all registered listeners.
231   * This method is equivalent to 
232   * {@link AbstractTableModel#fireTableChanged(TableModelEvent)}.
233   * 
234   * @param event the event.
235   */
236  public void rowsRemoved(TableModelEvent event) 
237  {
238    fireTableChanged(event);
239  }
240
241  /**
242   * Sets the column identifiers, updates the data rows (truncating
243   * or padding each row with <code>null</code> values) to match the 
244   * number of columns, and sends a {@link TableModelEvent} to all
245   * registered listeners.
246   * 
247   * @param columnIdentifiers the column identifiers.
248   */
249  public void setColumnIdentifiers(Vector columnIdentifiers) 
250  {
251    this.columnIdentifiers = columnIdentifiers;
252    setColumnCount(columnIdentifiers == null ? 0 : columnIdentifiers.size());
253  }
254  
255  /**
256   * Sets the column identifiers, updates the data rows (truncating
257   * or padding each row with <code>null</code> values) to match the 
258   * number of columns, and sends a {@link TableModelEvent} to all
259   * registered listeners.
260   * 
261   * @param columnIdentifiers the column identifiers.
262   */
263  public void setColumnIdentifiers(Object[] columnIdentifiers) 
264  {
265    setColumnIdentifiers(convertToVector(columnIdentifiers));
266  }
267
268  /**
269   * This method is obsolete, use {@link #setRowCount(int)} instead.
270   * 
271   * @param numRows the number of rows.
272   */
273  public void setNumRows(int numRows) 
274  {
275    setRowCount(numRows);
276  }
277
278  /**
279   * Sets the number of rows in the table.  If <code>rowCount</code> is less
280   * than the current number of rows in the table, rows are discarded.
281   * If <code>rowCount</code> is greater than the current number of rows in
282   * the table, new (empty) rows are added.
283   * 
284   * @param rowCount the row count.
285   */
286  public void setRowCount(int rowCount) 
287  {
288    int existingRowCount = dataVector.size();
289    if (rowCount < existingRowCount) 
290    {
291      dataVector.setSize(rowCount);
292      fireTableRowsDeleted(rowCount, existingRowCount - 1);      
293    }
294    else 
295    {
296      int rowsToAdd = rowCount - existingRowCount;
297      addExtraRows(rowsToAdd, columnIdentifiers.size());
298      fireTableRowsInserted(existingRowCount, rowCount - 1);
299    }
300  }
301
302  /**
303   * Sets the number of columns in the table.  Existing rows are truncated
304   * or padded with <code>null</code> values to match the new column count.
305   * A {@link TableModelEvent} is sent to all registered listeners.
306   * 
307   * @param columnCount the column count.
308   */
309  public void setColumnCount(int columnCount) 
310  {
311    for (int i = 0; i < dataVector.size(); ++i)
312      {
313        ((Vector) dataVector.get(i)).setSize(columnCount);
314      }
315    if (columnIdentifiers != null)  
316      columnIdentifiers.setSize(columnCount);
317    fireTableStructureChanged();
318  }
319
320  /**
321   * Adds a column with the specified name to the table.  All cell values
322   * for the column are initially set to <code>null</code>.
323   * 
324   * @param columnName the column name (<code>null</code> permitted).
325   */
326  public void addColumn(Object columnName) 
327  {
328    addColumn(columnName, (Object[]) null);
329  }
330
331  /**
332   * Adds a column with the specified name and data values to the table.  
333   * 
334   * @param columnName the column name (<code>null</code> permitted).
335   * @param columnData the column data.
336   */
337  public void addColumn(Object columnName, Vector columnData) 
338  {
339    Object[] dataArray = null;
340    if (columnData != null) 
341    {
342      int rowCount = dataVector.size();
343      if (columnData.size() < rowCount)
344        columnData.setSize(rowCount);
345      dataArray = columnData.toArray();
346    }
347    addColumn(columnName, dataArray);
348  }
349
350  /**
351   * Adds a column with the specified name and data values to the table.
352   * 
353   * @param columnName the column name (<code>null</code> permitted).
354   * @param columnData the column data.
355   */
356  public void addColumn(Object columnName, Object[] columnData) 
357  {
358    if (columnData != null)
359    {
360      // check columnData array for cases where the number of items
361      // doesn't match the number of rows in the existing table
362      if (columnData.length > dataVector.size()) 
363      {
364        int rowsToAdd = columnData.length - dataVector.size();
365        addExtraRows(rowsToAdd, columnIdentifiers.size());
366      }
367      else if (columnData.length < dataVector.size())
368      {
369        Object[] tmp = new Object[dataVector.size()];
370        System.arraycopy(columnData, 0, tmp, 0, columnData.length);
371        columnData = tmp;
372      }
373    }
374    for (int i = 0; i < dataVector.size(); ++i)
375      {
376        ((Vector) dataVector.get(i)).add(columnData == null ? null : columnData[i]);
377      }
378    columnIdentifiers.add(columnName);
379    fireTableStructureChanged();
380  }
381
382  /**
383   * Adds a new row containing the specified data to the table and sends a
384   * {@link TableModelEvent} to all registered listeners.
385   * 
386   * @param rowData the row data (<code>null</code> permitted).
387   */
388  public void addRow(Vector rowData) 
389  {
390    int rowIndex = dataVector.size();
391    dataVector.add(rowData);
392    newRowsAdded(new TableModelEvent(
393      this, rowIndex, rowIndex, -1, TableModelEvent.INSERT)
394    );
395  }
396
397  /**
398   * Adds a new row containing the specified data to the table and sends a
399   * {@link TableModelEvent} to all registered listeners.
400   * 
401   * @param rowData the row data (<code>null</code> permitted).
402   */
403  public void addRow(Object[] rowData) 
404  {
405    addRow(convertToVector(rowData));
406  }
407
408  /**
409   * Inserts a new row into the table.
410   * 
411   * @param row the row index.
412   * @param rowData the row data.
413   */
414  public void insertRow(int row, Vector rowData) 
415  {
416    dataVector.add(row, rowData);
417    fireTableRowsInserted(row, row);
418  }
419
420  /**
421   * Inserts a new row into the table.
422   * 
423   * @param row the row index.
424   * @param rowData the row data.
425   */
426  public void insertRow(int row, Object[] rowData) 
427  {
428    insertRow(row, convertToVector(rowData));
429  }
430
431  /**
432   * Moves the rows from <code>startIndex</code> to <code>endIndex</code>
433   * (inclusive) to the specified row.
434   * 
435   * @param startIndex the start row.
436   * @param endIndex the end row.
437   * @param toIndex the row to move to.
438   */
439  public void moveRow(int startIndex, int endIndex, int toIndex) 
440  {
441    Vector removed = new Vector();
442    for (int i = endIndex; i >= startIndex; i--)
443    {
444      removed.add(this.dataVector.remove(i));
445    }
446    for (int i = 0; i <= endIndex - startIndex; i++) 
447    {
448      dataVector.insertElementAt(removed.get(i), toIndex);  
449    }
450    int firstRow = Math.min(startIndex, toIndex);
451    int lastRow = Math.max(endIndex, toIndex + (endIndex - startIndex));
452    fireTableRowsUpdated(firstRow, lastRow);
453  }
454
455  /**
456   * Removes a row from the table and sends a {@link TableModelEvent} to
457   * all registered listeners.
458   * 
459   * @param row the row index.
460   */
461  public void removeRow(int row) 
462  {
463    dataVector.remove(row);
464    fireTableRowsDeleted(row, row);
465  }
466
467  /**
468   * Returns the number of rows in the model.
469   * 
470   * @return The row count.
471   */
472  public int getRowCount() 
473  {
474    return dataVector.size();
475  }
476
477  /**
478   * Returns the number of columns in the model.
479   * 
480   * @return The column count.
481   */
482  public int getColumnCount() 
483  {
484    return columnIdentifiers == null ? 0 : columnIdentifiers.size();
485  }
486
487  /**
488   * Get the name of the column. If the column has the column identifier set,
489   * the return value is the result of the .toString() method call on that
490   * identifier. If the identifier is not explicitly set, the returned value
491   * is calculated by {@link AbstractTableModel#getColumnName(int)}.
492   * 
493   * @param column the column index.
494   * 
495   * @return The column name.
496   */
497  public String getColumnName(int column)
498  {
499    String result = "";
500    if (columnIdentifiers == null) 
501      result = super.getColumnName(column);
502    else 
503    {
504      if (column < getColumnCount())
505      {
506        checkSize();
507        Object id = columnIdentifiers.get(column);
508        if (id != null) 
509          result = id.toString();
510        else
511          result = super.getColumnName(column);
512      }
513      else
514        result = super.getColumnName(column);
515    }
516    return result;
517  }
518
519  /**
520   * Returns <code>true</code> if the specified cell can be modified, and
521   * <code>false</code> otherwise.  For this implementation, the method
522   * always returns <code>true</code>.
523   * 
524   * @param row the row index.
525   * @param column the column index.
526   * 
527   * @return <code>true</code> in all cases.
528   */
529  public boolean isCellEditable(int row, int column) 
530  {
531    return true;
532  }
533
534  /**
535   * Returns the value at the specified cell in the table.
536   * 
537   * @param row the row index.
538   * @param column the column index.
539   * 
540   * @return The value (<code>Object</code>, possibly <code>null</code>) at 
541   *         the specified cell in the table.
542   */
543  public Object getValueAt(int row, int column) 
544  {
545    return ((Vector) dataVector.get(row)).get(column);
546  }
547
548  /**
549   * Sets the value for the specified cell in the table and sends a 
550   * {@link TableModelEvent} to all registered listeners.
551   * 
552   * @param value the value (<code>Object</code>, <code>null</code> permitted).
553   * @param row the row index.
554   * @param column the column index.
555   */
556  public void setValueAt(Object value, int row, int column) 
557  {
558    ((Vector) dataVector.get(row)).set(column, value);
559    fireTableCellUpdated(row, column);
560  }
561
562  /**
563   * Converts the data array to a <code>Vector</code>.
564   * 
565   * @param data the data array (<code>null</code> permitted).
566   * 
567   * @return A vector (or <code>null</code> if the data array 
568   *         is <code>null</code>).
569   */
570  protected static Vector convertToVector(Object[] data) 
571  {
572    if (data == null)
573      return null;
574    Vector vector = new Vector(data.length);
575    for (int i = 0; i < data.length; i++) 
576      vector.add(data[i]);
577    return vector;          
578  }
579  
580  /**
581   * Converts the data array to a <code>Vector</code> of rows.
582   * 
583   * @param data the data array (<code>null</code> permitted).
584   * 
585   * @return A vector (or <code>null</code> if the data array 
586   *         is <code>null</code>.
587   */
588  protected static Vector convertToVector(Object[][] data) 
589  {
590    if (data == null)
591      return null;
592    Vector vector = new Vector(data.length);
593    for (int i = 0; i < data.length; i++)
594      vector.add(convertToVector(data[i]));
595    return vector;
596  }
597
598  /**
599   * This method adds some rows to <code>dataVector</code>.
600   *
601   * @param rowsToAdd number of rows to add
602   * @param nbColumns size of the added rows
603   */
604  private void addExtraRows(int rowsToAdd, int nbColumns)
605  {
606    for (int i = 0; i < rowsToAdd; i++) 
607      {
608        Vector tmp = new Vector();
609        tmp.setSize(columnIdentifiers.size());
610        dataVector.add(tmp);
611      } 
612  }
613
614  /**
615   * Checks the real columns/rows sizes against the ones returned by
616   * <code>getColumnCount()</code> and <code>getRowCount()</code>.
617   * If the supposed one are bigger, then we grow <code>columIdentifiers</code>
618   * and <code>dataVector</code> to their expected size.
619   */
620  private void checkSize()
621  {
622    int columnCount = getColumnCount();
623    int rowCount = getRowCount();
624    
625    if (columnCount > columnIdentifiers.size())
626      columnIdentifiers.setSize(columnCount);
627           
628    if (dataVector != null && rowCount > dataVector.size())
629      {
630        int rowsToAdd = rowCount - dataVector.size();
631        addExtraRows(rowsToAdd, columnCount);
632      }
633  }
634}