001 /* DefaultTableModel.java --
002 Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package javax.swing.table;
040
041 import java.io.Serializable;
042 import java.util.Vector;
043
044 import 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 */
052 public 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 }