DefaultTableModel.java 17.8 KB
Newer Older
Tom Tromey committed
1
/* DefaultTableModel.java --
2
   Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
Tom Tromey committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU Classpath 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 for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */


package javax.swing.table;

import java.io.Serializable;
import java.util.Vector;

import javax.swing.event.TableModelEvent;

/**
47
 * A two dimensional data structure used to store <code>Object</code>
Tom Tromey committed
48
 * instances, usually for display in a <code>JTable</code> component.
49 50
 *
 * @author      Andrew Selkirk
Tom Tromey committed
51 52 53 54 55 56 57
 */
public class DefaultTableModel extends AbstractTableModel
  implements Serializable
{
  static final long serialVersionUID = 6680042567037222321L;

  /**
58
   * Storage for the rows in the table (each row is itself
Tom Tromey committed
59 60 61 62 63 64 65 66 67 68 69 70
   * a <code>Vector</code>).
   */
  protected Vector dataVector;

  /**
   * Storage for the column identifiers.
   */
  protected Vector columnIdentifiers;

  /**
   * Creates an empty table with zero rows and zero columns.
   */
71
  public DefaultTableModel()
Tom Tromey committed
72 73 74
  {
    this(0, 0);
  }
75

Tom Tromey committed
76 77 78
  /**
   * Creates a new table with the specified number of rows and columns.
   * All cells in the table are initially empty (set to <code>null</code>).
79
   *
Tom Tromey committed
80 81 82
   * @param numRows  the number of rows.
   * @param numColumns  the number of columns.
   */
83
  public DefaultTableModel(int numRows, int numColumns)
Tom Tromey committed
84 85 86
  {
    Vector defaultNames = new Vector(numColumns);
    Vector data = new Vector(numRows);
87
    for (int i = 0; i < numColumns; i++)
Tom Tromey committed
88 89
      {
        defaultNames.add(super.getColumnName(i));
90 91
      }
    for (int r = 0; r < numRows; r++)
Tom Tromey committed
92 93 94 95 96 97 98
      {
        Vector tmp = new Vector(numColumns);
        tmp.setSize(numColumns);
        data.add(tmp);
      }
    setDataVector(data, defaultNames);
  }
99

Tom Tromey committed
100 101 102 103
  /**
   * Creates a new table with the specified column names and number of
   * rows.  The number of columns is determined by the number of column
   * names supplied.
104
   *
Tom Tromey committed
105 106 107
   * @param columnNames the column names.
   * @param numRows the number of rows.
   */
108
  public DefaultTableModel(Vector columnNames, int numRows)
Tom Tromey committed
109 110 111 112 113 114 115 116
  {
    if (numRows < 0)
      throw new IllegalArgumentException("numRows < 0");
    Vector data = new Vector();
    int numColumns = 0;

    if (columnNames != null)
      numColumns = columnNames.size();
117 118

    while (0 < numRows--)
Tom Tromey committed
119 120 121 122 123 124 125 126 127 128
      {
        Vector rowData = new Vector();
        rowData.setSize(numColumns);
        data.add(rowData);
      }
    setDataVector(data, columnNames);
  }

  /**
   * Creates a new table with the specified column names and row count.
129
   *
Tom Tromey committed
130 131 132
   * @param columnNames the column names.
   * @param numRows the number of rows.
   */
133
  public DefaultTableModel(Object[] columnNames, int numRows)
Tom Tromey committed
134 135 136
  {
    this(convertToVector(columnNames), numRows);
  }
137

Tom Tromey committed
138 139
  /**
   * Creates a new table with the specified data values and column names.
140
   *
Tom Tromey committed
141 142 143
   * @param data the data values.
   * @param columnNames the column names.
   */
144
  public DefaultTableModel(Vector data, Vector columnNames)
Tom Tromey committed
145 146 147 148 149 150
  {
    setDataVector(data, columnNames);
  }

  /**
   * Creates a new table with the specified data values and column names.
151
   *
Tom Tromey committed
152 153 154
   * @param data the data values.
   * @param columnNames the column names.
   */
155
  public DefaultTableModel(Object[][] data, Object[] columnNames)
Tom Tromey committed
156 157 158 159 160 161
  {
    this(convertToVector(data), convertToVector(columnNames));
  }

  /**
   * Returns the vector containing the row data for the table.
162
   *
Tom Tromey committed
163 164
   * @return The data vector.
   */
165
  public Vector getDataVector()
Tom Tromey committed
166 167 168 169 170 171 172 173 174 175
  {
    return dataVector;
  }

  /**
   * Sets the data and column identifiers for the table.  The data vector
   * contains a <code>Vector</code> for each row in the table - if the
   * number of objects in each row does not match the number of column
   * names specified, the row data is truncated or expanded (by adding
   * <code>null</code> values) as required.
176
   *
Tom Tromey committed
177 178
   * @param data the data for the table (a vector of row vectors).
   * @param columnNames the column names.
179
   *
Tom Tromey committed
180 181
   * @throws NullPointerException if either argument is <code>null</code>.
   */
182
  public void setDataVector(Vector data, Vector columnNames)
Tom Tromey committed
183 184 185 186 187 188 189 190 191 192
  {
    if (data == null)
      dataVector = new Vector();
    else
      dataVector = data;
    setColumnIdentifiers(columnNames);
  }

  /**
   * Sets the data and column identifiers for the table.
193
   *
Tom Tromey committed
194 195
   * @param data the data for the table.
   * @param columnNames the column names.
196
   *
Tom Tromey committed
197 198
   * @throws NullPointerException if either argument is <code>null</code>.
   */
199
  public void setDataVector(Object[][] data, Object[] columnNames)
Tom Tromey committed
200
  {
201
    setDataVector(convertToVector(data),
Tom Tromey committed
202 203
                  convertToVector(columnNames));
  }
204

Tom Tromey committed
205 206
  /**
   * Sends the specified <code>event</code> to all registered listeners.
207
   * This method is equivalent to
Tom Tromey committed
208
   * {@link AbstractTableModel#fireTableChanged(TableModelEvent)}.
209
   *
Tom Tromey committed
210 211
   * @param event the event.
   */
212
  public void newDataAvailable(TableModelEvent event)
Tom Tromey committed
213 214 215 216 217 218
  {
    fireTableChanged(event);
  }

  /**
   * Sends the specified <code>event</code> to all registered listeners.
219
   * This method is equivalent to
Tom Tromey committed
220
   * {@link AbstractTableModel#fireTableChanged(TableModelEvent)}.
221
   *
Tom Tromey committed
222 223
   * @param event the event.
   */
224
  public void newRowsAdded(TableModelEvent event)
Tom Tromey committed
225 226 227 228 229 230
  {
    fireTableChanged(event);
  }

  /**
   * Sends the specified <code>event</code> to all registered listeners.
231
   * This method is equivalent to
Tom Tromey committed
232
   * {@link AbstractTableModel#fireTableChanged(TableModelEvent)}.
233
   *
Tom Tromey committed
234 235
   * @param event the event.
   */
236
  public void rowsRemoved(TableModelEvent event)
Tom Tromey committed
237 238 239 240 241 242
  {
    fireTableChanged(event);
  }

  /**
   * Sets the column identifiers, updates the data rows (truncating
243
   * or padding each row with <code>null</code> values) to match the
Tom Tromey committed
244 245
   * number of columns, and sends a {@link TableModelEvent} to all
   * registered listeners.
246
   *
Tom Tromey committed
247 248
   * @param columnIdentifiers the column identifiers.
   */
249
  public void setColumnIdentifiers(Vector columnIdentifiers)
Tom Tromey committed
250 251
  {
    this.columnIdentifiers = columnIdentifiers;
252
    setColumnCount(columnIdentifiers == null ? 0 : columnIdentifiers.size());
Tom Tromey committed
253
  }
254

Tom Tromey committed
255 256
  /**
   * Sets the column identifiers, updates the data rows (truncating
257
   * or padding each row with <code>null</code> values) to match the
Tom Tromey committed
258 259
   * number of columns, and sends a {@link TableModelEvent} to all
   * registered listeners.
260
   *
Tom Tromey committed
261 262
   * @param columnIdentifiers the column identifiers.
   */
263
  public void setColumnIdentifiers(Object[] columnIdentifiers)
Tom Tromey committed
264 265 266 267 268 269
  {
    setColumnIdentifiers(convertToVector(columnIdentifiers));
  }

  /**
   * This method is obsolete, use {@link #setRowCount(int)} instead.
270
   *
Tom Tromey committed
271 272
   * @param numRows the number of rows.
   */
273
  public void setNumRows(int numRows)
Tom Tromey committed
274 275 276 277 278 279 280 281 282
  {
    setRowCount(numRows);
  }

  /**
   * Sets the number of rows in the table.  If <code>rowCount</code> is less
   * than the current number of rows in the table, rows are discarded.
   * If <code>rowCount</code> is greater than the current number of rows in
   * the table, new (empty) rows are added.
283
   *
Tom Tromey committed
284 285
   * @param rowCount the row count.
   */
286
  public void setRowCount(int rowCount)
Tom Tromey committed
287 288
  {
    int existingRowCount = dataVector.size();
289
    if (rowCount < existingRowCount)
Tom Tromey committed
290 291
    {
      dataVector.setSize(rowCount);
292
      fireTableRowsDeleted(rowCount, existingRowCount - 1);
Tom Tromey committed
293
    }
294
    else
Tom Tromey committed
295 296
    {
      int rowsToAdd = rowCount - existingRowCount;
297
      addExtraRows(rowsToAdd, columnIdentifiers.size());
298
      fireTableRowsInserted(existingRowCount, rowCount - 1);
Tom Tromey committed
299 300 301 302 303 304 305
    }
  }

  /**
   * Sets the number of columns in the table.  Existing rows are truncated
   * or padded with <code>null</code> values to match the new column count.
   * A {@link TableModelEvent} is sent to all registered listeners.
306
   *
Tom Tromey committed
307 308
   * @param columnCount the column count.
   */
309
  public void setColumnCount(int columnCount)
Tom Tromey committed
310 311 312 313 314
  {
    for (int i = 0; i < dataVector.size(); ++i)
      {
        ((Vector) dataVector.get(i)).setSize(columnCount);
      }
315
    if (columnIdentifiers != null)
Tom Tromey committed
316 317 318 319 320 321 322
      columnIdentifiers.setSize(columnCount);
    fireTableStructureChanged();
  }

  /**
   * Adds a column with the specified name to the table.  All cell values
   * for the column are initially set to <code>null</code>.
323
   *
Tom Tromey committed
324 325
   * @param columnName the column name (<code>null</code> permitted).
   */
326
  public void addColumn(Object columnName)
Tom Tromey committed
327 328 329 330 331
  {
    addColumn(columnName, (Object[]) null);
  }

  /**
332 333
   * Adds a column with the specified name and data values to the table.
   *
Tom Tromey committed
334 335 336
   * @param columnName the column name (<code>null</code> permitted).
   * @param columnData the column data.
   */
337
  public void addColumn(Object columnName, Vector columnData)
Tom Tromey committed
338 339
  {
    Object[] dataArray = null;
340
    if (columnData != null)
Tom Tromey committed
341 342 343 344 345 346 347 348 349 350 351
    {
      int rowCount = dataVector.size();
      if (columnData.size() < rowCount)
        columnData.setSize(rowCount);
      dataArray = columnData.toArray();
    }
    addColumn(columnName, dataArray);
  }

  /**
   * Adds a column with the specified name and data values to the table.
352
   *
Tom Tromey committed
353 354 355
   * @param columnName the column name (<code>null</code> permitted).
   * @param columnData the column data.
   */
356
  public void addColumn(Object columnName, Object[] columnData)
357
  {
Tom Tromey committed
358 359 360 361
    if (columnData != null)
    {
      // check columnData array for cases where the number of items
      // doesn't match the number of rows in the existing table
362
      if (columnData.length > dataVector.size())
Tom Tromey committed
363 364
      {
        int rowsToAdd = columnData.length - dataVector.size();
365
        addExtraRows(rowsToAdd, columnIdentifiers.size());
Tom Tromey committed
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
      }
      else if (columnData.length < dataVector.size())
      {
        Object[] tmp = new Object[dataVector.size()];
        System.arraycopy(columnData, 0, tmp, 0, columnData.length);
        columnData = tmp;
      }
    }
    for (int i = 0; i < dataVector.size(); ++i)
      {
        ((Vector) dataVector.get(i)).add(columnData == null ? null : columnData[i]);
      }
    columnIdentifiers.add(columnName);
    fireTableStructureChanged();
  }

  /**
   * Adds a new row containing the specified data to the table and sends a
   * {@link TableModelEvent} to all registered listeners.
385
   *
Tom Tromey committed
386 387
   * @param rowData the row data (<code>null</code> permitted).
   */
388
  public void addRow(Vector rowData)
389
  {
Tom Tromey committed
390 391 392 393 394 395 396 397 398 399
    int rowIndex = dataVector.size();
    dataVector.add(rowData);
    newRowsAdded(new TableModelEvent(
      this, rowIndex, rowIndex, -1, TableModelEvent.INSERT)
    );
  }

  /**
   * Adds a new row containing the specified data to the table and sends a
   * {@link TableModelEvent} to all registered listeners.
400
   *
Tom Tromey committed
401 402
   * @param rowData the row data (<code>null</code> permitted).
   */
403
  public void addRow(Object[] rowData)
404
  {
Tom Tromey committed
405 406 407 408 409
    addRow(convertToVector(rowData));
  }

  /**
   * Inserts a new row into the table.
410
   *
Tom Tromey committed
411 412 413
   * @param row the row index.
   * @param rowData the row data.
   */
414
  public void insertRow(int row, Vector rowData)
415
  {
Tom Tromey committed
416
    dataVector.add(row, rowData);
417
    fireTableRowsInserted(row, row);
Tom Tromey committed
418 419 420 421
  }

  /**
   * Inserts a new row into the table.
422
   *
Tom Tromey committed
423 424 425
   * @param row the row index.
   * @param rowData the row data.
   */
426
  public void insertRow(int row, Object[] rowData)
427
  {
Tom Tromey committed
428 429 430 431 432 433
    insertRow(row, convertToVector(rowData));
  }

  /**
   * Moves the rows from <code>startIndex</code> to <code>endIndex</code>
   * (inclusive) to the specified row.
434
   *
Tom Tromey committed
435 436 437 438
   * @param startIndex the start row.
   * @param endIndex the end row.
   * @param toIndex the row to move to.
   */
439
  public void moveRow(int startIndex, int endIndex, int toIndex)
440
  {
Tom Tromey committed
441 442 443 444 445
    Vector removed = new Vector();
    for (int i = endIndex; i >= startIndex; i--)
    {
      removed.add(this.dataVector.remove(i));
    }
446
    for (int i = 0; i <= endIndex - startIndex; i++)
Tom Tromey committed
447
    {
448
      dataVector.insertElementAt(removed.get(i), toIndex);
Tom Tromey committed
449 450 451 452 453 454 455 456 457
    }
    int firstRow = Math.min(startIndex, toIndex);
    int lastRow = Math.max(endIndex, toIndex + (endIndex - startIndex));
    fireTableRowsUpdated(firstRow, lastRow);
  }

  /**
   * Removes a row from the table and sends a {@link TableModelEvent} to
   * all registered listeners.
458
   *
Tom Tromey committed
459 460
   * @param row the row index.
   */
461
  public void removeRow(int row)
462
  {
Tom Tromey committed
463
    dataVector.remove(row);
464
    fireTableRowsDeleted(row, row);
Tom Tromey committed
465 466 467 468
  }

  /**
   * Returns the number of rows in the model.
469
   *
Tom Tromey committed
470 471
   * @return The row count.
   */
472
  public int getRowCount()
473
  {
Tom Tromey committed
474 475 476 477 478
    return dataVector.size();
  }

  /**
   * Returns the number of columns in the model.
479
   *
Tom Tromey committed
480 481
   * @return The column count.
   */
482
  public int getColumnCount()
483 484
  {
    return columnIdentifiers == null ? 0 : columnIdentifiers.size();
Tom Tromey committed
485 486 487
  }

  /**
488 489 490 491
   * Get the name of the column. If the column has the column identifier set,
   * the return value is the result of the .toString() method call on that
   * identifier. If the identifier is not explicitly set, the returned value
   * is calculated by {@link AbstractTableModel#getColumnName(int)}.
492
   *
Tom Tromey committed
493
   * @param column the column index.
494
   *
Tom Tromey committed
495 496
   * @return The column name.
   */
497 498
  public String getColumnName(int column)
  {
Tom Tromey committed
499
    String result = "";
500
    if (columnIdentifiers == null)
Tom Tromey committed
501
      result = super.getColumnName(column);
502
    else
Tom Tromey committed
503 504
    {
      if (column < getColumnCount())
505 506
      {
        checkSize();
Tom Tromey committed
507
        Object id = columnIdentifiers.get(column);
508
        if (id != null)
Tom Tromey committed
509 510 511 512 513 514 515 516 517 518 519 520 521 522
          result = id.toString();
        else
          result = super.getColumnName(column);
      }
      else
        result = super.getColumnName(column);
    }
    return result;
  }

  /**
   * Returns <code>true</code> if the specified cell can be modified, and
   * <code>false</code> otherwise.  For this implementation, the method
   * always returns <code>true</code>.
523
   *
Tom Tromey committed
524 525
   * @param row the row index.
   * @param column the column index.
526
   *
Tom Tromey committed
527 528
   * @return <code>true</code> in all cases.
   */
529
  public boolean isCellEditable(int row, int column)
530
  {
Tom Tromey committed
531 532 533 534 535
    return true;
  }

  /**
   * Returns the value at the specified cell in the table.
536
   *
Tom Tromey committed
537 538
   * @param row the row index.
   * @param column the column index.
539 540
   *
   * @return The value (<code>Object</code>, possibly <code>null</code>) at
Tom Tromey committed
541 542
   *         the specified cell in the table.
   */
543
  public Object getValueAt(int row, int column)
544
  {
Tom Tromey committed
545 546 547 548
    return ((Vector) dataVector.get(row)).get(column);
  }

  /**
549
   * Sets the value for the specified cell in the table and sends a
Tom Tromey committed
550
   * {@link TableModelEvent} to all registered listeners.
551
   *
Tom Tromey committed
552 553 554 555
   * @param value the value (<code>Object</code>, <code>null</code> permitted).
   * @param row the row index.
   * @param column the column index.
   */
556
  public void setValueAt(Object value, int row, int column)
557
  {
Tom Tromey committed
558
    ((Vector) dataVector.get(row)).set(column, value);
559
    fireTableCellUpdated(row, column);
Tom Tromey committed
560 561 562 563
  }

  /**
   * Converts the data array to a <code>Vector</code>.
564
   *
Tom Tromey committed
565
   * @param data the data array (<code>null</code> permitted).
566 567
   *
   * @return A vector (or <code>null</code> if the data array
Tom Tromey committed
568 569
   *         is <code>null</code>).
   */
570
  protected static Vector convertToVector(Object[] data)
571
  {
Tom Tromey committed
572 573 574
    if (data == null)
      return null;
    Vector vector = new Vector(data.length);
575
    for (int i = 0; i < data.length; i++)
Tom Tromey committed
576
      vector.add(data[i]);
577
    return vector;
Tom Tromey committed
578
  }
579

Tom Tromey committed
580 581
  /**
   * Converts the data array to a <code>Vector</code> of rows.
582
   *
Tom Tromey committed
583
   * @param data the data array (<code>null</code> permitted).
584 585
   *
   * @return A vector (or <code>null</code> if the data array
Tom Tromey committed
586 587
   *         is <code>null</code>.
   */
588
  protected static Vector convertToVector(Object[][] data)
589
  {
Tom Tromey committed
590 591 592 593 594 595 596
    if (data == null)
      return null;
    Vector vector = new Vector(data.length);
    for (int i = 0; i < data.length; i++)
      vector.add(convertToVector(data[i]));
    return vector;
  }
597 598 599 600 601 602 603 604 605

  /**
   * This method adds some rows to <code>dataVector</code>.
   *
   * @param rowsToAdd number of rows to add
   * @param nbColumns size of the added rows
   */
  private void addExtraRows(int rowsToAdd, int nbColumns)
  {
606
    for (int i = 0; i < rowsToAdd; i++)
607 608 609 610
      {
        Vector tmp = new Vector();
        tmp.setSize(columnIdentifiers.size());
        dataVector.add(tmp);
611
      }
612 613 614 615 616 617 618 619 620 621 622 623
  }

  /**
   * Checks the real columns/rows sizes against the ones returned by
   * <code>getColumnCount()</code> and <code>getRowCount()</code>.
   * If the supposed one are bigger, then we grow <code>columIdentifiers</code>
   * and <code>dataVector</code> to their expected size.
   */
  private void checkSize()
  {
    int columnCount = getColumnCount();
    int rowCount = getRowCount();
624

625 626
    if (columnCount > columnIdentifiers.size())
      columnIdentifiers.setSize(columnCount);
627

628
    if (dataVector != null && rowCount > dataVector.size())
629 630 631 632 633
      {
        int rowsToAdd = rowCount - dataVector.size();
        addExtraRows(rowsToAdd, columnCount);
      }
  }
Tom Tromey committed
634
}