1. /*
  2. * @(#)DefaultTableModel.java 1.20 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.table;
  8. import java.io.Serializable;
  9. import java.util.Vector;
  10. import java.util.Enumeration;
  11. import javax.swing.event.TableModelEvent;
  12. /**
  13. * This is an implementation of TableModel that uses a Vector of Vectors
  14. * to store the cell value objects.
  15. * <p>
  16. * <b>Note:</b><br>
  17. * The DefaultTableModel's API contains the methods addColumn(),
  18. * removeColumn(), but not methods to insert a column at an index
  19. * nor methods to move the columns. This is because JTable does
  20. * not display the columns based on the order of the columns in
  21. * this model. So rearranging them here doesn't do much. See
  22. * the column ordering methods in TableColumnModel.
  23. * <p>
  24. * <strong>Warning:</strong>
  25. * Serialized objects of this class will not be compatible with
  26. * future Swing releases. The current serialization support is appropriate
  27. * for short term storage or RMI between applications running the same
  28. * version of Swing. A future release of Swing will provide support for
  29. * long term persistence.
  30. *
  31. * @version 1.20 11/29/01
  32. * @author Alan Chung
  33. * @author Philip Milne
  34. *
  35. * @see TableModel
  36. * @see #getDataVector
  37. */
  38. public class DefaultTableModel extends AbstractTableModel implements Serializable {
  39. //
  40. // Instance Variables
  41. //
  42. /** The Vector of Vector of Object values */
  43. protected Vector dataVector;
  44. /** The Vector of column identifiers */
  45. protected Vector columnIdentifiers;
  46. //
  47. // Constructors
  48. //
  49. /**
  50. * Constructs a default DefaultTableModel which is a table of
  51. * zero columns and zero rows.
  52. */
  53. public DefaultTableModel() {
  54. this((Vector)null, 0);
  55. }
  56. /**
  57. * Constructs a DefaultTableModel with <i>numRows</i> and
  58. * <i>numColumns</i> of <b>null</b> object values.
  59. *
  60. * @param numRows The number of rows the table holds
  61. * @param numColumns The number of columns the table holds
  62. *
  63. * @see #setValueAt
  64. */
  65. public DefaultTableModel(int numRows, int numColumns) {
  66. Vector names = new Vector(numColumns);
  67. names.setSize(numColumns);
  68. setColumnIdentifiers(names);
  69. dataVector = new Vector();
  70. setNumRows(numRows);
  71. }
  72. /**
  73. * Constructs a DefaultTableModel with as many columns as there are
  74. * elements in <i>columnNames</i> and <i>numRows</i> of <b>null</b>
  75. * object values. Each column's name will be taken from
  76. * the <i>columnNames</i> vector.
  77. *
  78. * @param columnNames Vector containing the names of the new columns.
  79. * If this null then the model has no columns
  80. * @param numRows The number of rows the table holds
  81. * @see #setDataVector
  82. * @see #setValueAt
  83. */
  84. public DefaultTableModel(Vector columnNames, int numRows) {
  85. setColumnIdentifiers(columnNames);
  86. dataVector = new Vector();
  87. setNumRows(numRows);
  88. }
  89. /**
  90. * Constructs a DefaultTableModel with as many columns as there are
  91. * elements in <i>columnNames</i> and <i>numRows</i> of <b>null</b>
  92. * object values. Each column's name will be taken from
  93. * the <i>columnNames</i> array.
  94. *
  95. * @param columnNames Array containing the names of the new columns.
  96. * If this null then the model has no columns
  97. * @param numRows The number of rows the table holds
  98. * @see #setDataVector
  99. * @see #setValueAt
  100. */
  101. public DefaultTableModel(Object[] columnNames, int numRows) {
  102. this(convertToVector(columnNames), numRows);
  103. }
  104. /**
  105. * Constructs a DefaultTableModel and initializes the table
  106. * by passing <i>data</i> and <i>columnNames</i> to the setDataVector()
  107. * method.
  108. *
  109. * @param data The data of the table
  110. * @param columnNames Vector containing the names of the new columns.
  111. * @see #getDataVector
  112. * @see #setDataVector
  113. */
  114. public DefaultTableModel(Vector data, Vector columnNames) {
  115. setDataVector(data, columnNames);
  116. }
  117. /**
  118. * Constructs a DefaultTableModel and initializes the table
  119. * by passing <i>data</i> and <i>columnNames</i> to the setDataVector()
  120. * method. The first index in the Object[][] is the row index and
  121. * the second is the column index.
  122. *
  123. * @param data The data of the table
  124. * @param columnNames The names of the columns.
  125. * @see #getDataVector
  126. * @see #setDataVector
  127. */
  128. public DefaultTableModel(Object[][] data, Object[] columnNames) {
  129. setDataVector(data, columnNames);
  130. }
  131. //
  132. // Querying and Modifying the data structure
  133. //
  134. /**
  135. * This returns the Vector of Vectors that contains the table's
  136. * data values. The vectors contained in the outer vector are
  137. * each a single row of values. In other words, to get to the cell
  138. * at row 1, column 5 <p>
  139. *
  140. * <code>((Vector)getDataVector().elementAt(1)).elementAt(5);</code><p>
  141. *
  142. * You can directly alter the returned Vector. You can change the cell
  143. * values, the number of rows. If you need to alter the number of columns
  144. * in the model, you can do so with addColumn(), removeColumn(), or
  145. * the setDataVector() methods. Once you have finished modifying the
  146. * dataVector, you <b>must</b> inform the model of the new data using
  147. * one of the notification methods. The notification methods
  148. * will generate the appropriate TableModelListener messages to notify
  149. * the JTable and any other listeners of this model.
  150. *
  151. * @see #newDataAvailable
  152. * @see #newRowsAdded
  153. * @see #setDataVector
  154. */
  155. public Vector getDataVector() {
  156. return dataVector;
  157. }
  158. /**
  159. * This replaces the current dataVector instance variable with the
  160. * new Vector of rows, <i>newData</i>. <i>columnNames</i> are the names
  161. * of the new columns. The first name in <i>columnNames</i> is
  162. * mapped to column 0 in <i>newData</i>. Each row in <i>newData</i>
  163. * is adjusted to match the number of columns in <i>columnNames</i>
  164. * either by truncating the Vector if it is too long, or adding
  165. * null values if it is too short.
  166. * <p>
  167. *
  168. * @param newData The new data vector
  169. * @param columnNames The names of the columns
  170. * @see #newDataAvailable
  171. * @see #getDataVector
  172. */
  173. public void setDataVector(Vector newData, Vector columnNames) {
  174. if (newData == null)
  175. throw new IllegalArgumentException("setDataVector() - Null parameter");
  176. // Clear all the previous data.
  177. dataVector = new Vector(0);
  178. // Install the new column structure, this will fireTableStructureChanged
  179. setColumnIdentifiers(columnNames);
  180. // Add the new rows.
  181. dataVector = newData;
  182. // Make all the new rows the right length and generate a notification.
  183. newRowsAdded(new TableModelEvent(this, 0, getRowCount()-1,
  184. TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT));
  185. }
  186. /**
  187. * This replaces the value in the dataVector instance variable with the
  188. * values in the array <i>newData</i>. The first index in the Object[][]
  189. * array is the row index and the second is the column index.
  190. * <i>columnNames</i> are the names of the new columns.
  191. *
  192. * @see #setDataVector(Vector, Vector)
  193. */
  194. public void setDataVector(Object[][] newData, Object[] columnNames) {
  195. setDataVector(convertToVector(newData), convertToVector(columnNames));
  196. }
  197. /**
  198. * Equivalent to fireTableChanged.
  199. *
  200. */
  201. public void newDataAvailable(TableModelEvent event) {
  202. fireTableChanged(event);
  203. }
  204. /**
  205. * This method will make sure the new rows have the correct number of columns.
  206. * It does so using the setSize method in Vector which truncates Vectors
  207. * which are too long, and appends nulls if they are too short.
  208. * This method also sends out a tableChanged() notification message
  209. * to all the listeners.
  210. *
  211. * @parameter event This TableModelEvent describes where the
  212. * rows were added. If <b>null</b> it assumes
  213. * all the rows were newly added.
  214. * @see #getDataVector
  215. */
  216. public void newRowsAdded(TableModelEvent event) {
  217. int start = event.getFirstRow();
  218. int end = event.getLastRow();
  219. if (start < 0)
  220. start = 0;
  221. if (end < 0)
  222. end = getRowCount()-1;
  223. // Have to make sure all the new columns have the correct
  224. // number of columns
  225. for (int i = start; i < end; i++)
  226. ((Vector)dataVector.elementAt(i)).setSize(getColumnCount());
  227. // Now we send the notification
  228. fireTableChanged(event);
  229. }
  230. /**
  231. * Equivalent to fireTableChanged().
  232. *
  233. */
  234. public void rowsRemoved(TableModelEvent event) {
  235. fireTableChanged(event);
  236. }
  237. /**
  238. * Replaces the column identifiers in the model.
  239. *
  240. * @param newIdentifiers Vector of column identifiers. A null means
  241. * setting the model to zero columns
  242. * @see #setNumRows
  243. */
  244. public void setColumnIdentifiers(Vector newIdentifiers) {
  245. if (newIdentifiers != null) {
  246. columnIdentifiers = newIdentifiers;
  247. }
  248. else {
  249. columnIdentifiers = new Vector();
  250. }
  251. // Generate notification
  252. fireTableStructureChanged();
  253. }
  254. /**
  255. * Replaces the column identifiers in the model. If the number of
  256. * <i>newIdentifiers</i> is greater than the current numColumns,
  257. * new columns are added to the end of each row in the model.
  258. * If the number of <i>newIdentifier</i> is less than the current
  259. * number of columns, all the extra columns at the end of a row are
  260. * discarded. <p>
  261. *
  262. * @param newIdentifiers Array of column identifiers. A null means
  263. * setting the model to zero columns
  264. * @see #setNumRows
  265. */
  266. public void setColumnIdentifiers(Object[] newIdentifiers) {
  267. setColumnIdentifiers(convertToVector(newIdentifiers));
  268. }
  269. /**
  270. * Sets the number of rows in the model. If the new size is greater
  271. * than the current size, new rows are added to the end of the model
  272. * If the new size is less than the current size, all
  273. * rows at index <i>newSize</i> and greater are discarded. <p>
  274. *
  275. * @param newSize the new number of rows
  276. * @see #setColumnIdentifiers
  277. */
  278. public void setNumRows(int newSize) {
  279. if ((newSize < 0) || (newSize == getRowCount()))
  280. return;
  281. int oldNumRows = getRowCount();
  282. if (newSize <= getRowCount()) {
  283. // newSize is smaller than our current size, so we can just
  284. // let Vector discard the extra rows
  285. dataVector.setSize(newSize);
  286. // Generate notification
  287. fireTableRowsDeleted(getRowCount(), oldNumRows-1);
  288. }
  289. else {
  290. int columnCount = getColumnCount();
  291. // We are adding rows to the model
  292. while(getRowCount() < newSize) {
  293. Vector newRow = new Vector(columnCount);
  294. newRow.setSize(columnCount);
  295. dataVector.addElement(newRow);
  296. }
  297. // Generate notification
  298. fireTableRowsInserted(oldNumRows, getRowCount()-1);
  299. }
  300. }
  301. /**
  302. * Add a column to the model. The new column will have the
  303. * idenitifier <i>columnName</i>. This method will send a
  304. * tableChanged() notification message to all the listeners.
  305. * This method is a cover for <i>addColumn(Object, Vector)</i> which
  306. * uses null as the data vector.
  307. *
  308. * @param columnName the identifier of the column being added
  309. * @exception IllegalArgumentException if columnName is null
  310. */
  311. public void addColumn(Object columnName) {
  312. addColumn(columnName, (Vector)null);
  313. }
  314. /**
  315. * Add a column to the model. The new column will have the
  316. * idenitifier <i>columnName</i>. <i>columnData</i> is the
  317. * optional Vector of data for the column. If it is <b>null</b>
  318. * the column is filled with <b>null</b> values. Otherwise,
  319. * the new data will be added to model starting with the first
  320. * element going to row 0, etc. This method will send a
  321. * tableChanged() notification message to all the listeners.
  322. *
  323. * @param columnName the identifier of the column being added
  324. * @param columnData optional data of the column being added
  325. * @exception IllegalArgumentException if columnName is null
  326. */
  327. public void addColumn(Object columnName, Vector columnData) {
  328. if (columnName == null)
  329. throw new IllegalArgumentException("addColumn() - null parameter");
  330. columnIdentifiers.addElement(columnName);
  331. // Fill in the new column, with nulls or with columnData
  332. int index = 0;
  333. Enumeration enumeration = dataVector.elements();
  334. while (enumeration.hasMoreElements()) {
  335. Object value;
  336. if ((columnData != null) && (index < columnData.size()))
  337. value = columnData.elementAt(index);
  338. else
  339. value = null;
  340. ((Vector)enumeration.nextElement()).addElement(value);
  341. index++;
  342. }
  343. // Generate notification
  344. fireTableStructureChanged();
  345. }
  346. /**
  347. * Adds a column to the model with name <i>columnName</i>.
  348. *
  349. * @see #addColumn(Object, Vector)
  350. */
  351. public void addColumn(Object columnName, Object[] columnData) {
  352. addColumn(columnName, convertToVector(columnData));
  353. }
  354. /**
  355. * Add a row to the end of the model. The new row will contain
  356. * <b>null</b> values unless <i>rowData</i> is specified. Notification
  357. * of the row being added will be generated.
  358. *
  359. * @param rowData optional data of the row being added
  360. */
  361. public void addRow(Vector rowData) {
  362. if (rowData == null) {
  363. rowData = new Vector(getColumnCount());
  364. }
  365. else {
  366. rowData.setSize(getColumnCount());
  367. }
  368. dataVector.addElement(rowData);
  369. // Generate notification
  370. newRowsAdded(new TableModelEvent(this, getRowCount()-1, getRowCount()-1,
  371. TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT));
  372. }
  373. /**
  374. * Add a row to the end of the model. The new row will contain
  375. * <b>null</b> values unless <i>rowData</i> is specified. Notification
  376. * of the row being added will be generated.
  377. *
  378. * @param rowData optional data of the row being added
  379. */
  380. public void addRow(Object[] rowData) {
  381. addRow(convertToVector(rowData));
  382. }
  383. /**
  384. * Insert a row at <i>row</i> in the model. The new row will contain
  385. * <b>null</b> values unless <i>rowData</i> is specified. Notification
  386. * of the row being added will be generated.
  387. *
  388. * @param row the row index of the row to be inserted
  389. * @param rowData optional data of the row being added
  390. * @exception ArrayIndexOutOfBoundsException if the row was invalid.
  391. */
  392. public void insertRow(int row, Vector rowData) {
  393. if (rowData == null) {
  394. rowData = new Vector(getColumnCount());
  395. }
  396. else {
  397. rowData.setSize(getColumnCount());
  398. }
  399. dataVector.insertElementAt(rowData, row);
  400. // Generate notification
  401. newRowsAdded(new TableModelEvent(this, row, row,
  402. TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT));
  403. }
  404. /**
  405. * Insert a row at <i>row</i> in the model. The new row will contain
  406. * <b>null</b> values unless <i>rowData</i> is specified. Notification
  407. * of the row being added will be generated.
  408. *
  409. * @param row the row index of the row to be inserted
  410. * @param rowData optional data of the row being added
  411. * @exception ArrayIndexOutOfBoundsException if the row was invalid.
  412. */
  413. public void insertRow(int row, Object[] rowData) {
  414. insertRow(row, convertToVector(rowData));
  415. }
  416. /**
  417. * Moves one or more rows starting at <i>startIndex</i> to <i>endIndex</i>
  418. * in the model to the <i>toIndex</i>. This method will send a
  419. * tableChanged() notification message to all the listeners. <p>
  420. *
  421. * Examples of moves:<p>
  422. * 1. moveRow(1,3,5);<p>
  423. * a|B|C|D|e|f|g|h|i|j|k - before
  424. * a|e|f|B|C|D|g|h|i|j|k - after
  425. * 2. moveRow(6,7,1);<p>
  426. * a|b|c|d|e|f|G|H|i|j|k - before
  427. * a|G|H|b|c|d|e|f|i|j|k - after
  428. *
  429. * @param startIndex the starting row index to be moved
  430. * @param endIndex the ending row index to be moved
  431. * @param toIndex the destination of the rows to be moved
  432. * @exception ArrayIndexOutOfBoundsException if any of the indices are out of
  433. * range. Or if endIndex is less than startIndex.
  434. */
  435. public void moveRow(int startIndex, int endIndex, int toIndex) {
  436. if ((startIndex < 0) || (startIndex >= getRowCount()))
  437. throw new ArrayIndexOutOfBoundsException(startIndex);
  438. if ((endIndex < 0) || (endIndex >= getRowCount()))
  439. throw new ArrayIndexOutOfBoundsException(endIndex);
  440. if (startIndex > endIndex)
  441. throw new ArrayIndexOutOfBoundsException();
  442. if ((startIndex <= toIndex) && (toIndex <= endIndex))
  443. return; // Nothing to move
  444. boolean shift = toIndex < startIndex;
  445. // Do the move by first removing the row, then reinserting it
  446. for (int i = startIndex; i <= endIndex; i++) {
  447. Object aRow = dataVector.elementAt(i);
  448. dataVector.removeElementAt(i);
  449. dataVector.insertElementAt(aRow, toIndex);
  450. if (shift)
  451. toIndex++;
  452. }
  453. // Generate notification
  454. fireTableDataChanged();
  455. }
  456. /**
  457. * Remove the row at <i>row</i> from the model. Notification
  458. * of the row being removed will be sent to all the listeners.
  459. *
  460. * @param row the row index of the row to be removed
  461. * @exception ArrayIndexOutOfBoundsException if the row was invalid.
  462. */
  463. public void removeRow(int row) {
  464. dataVector.removeElementAt(row);
  465. // Generate notification
  466. fireTableRowsDeleted(row, row);
  467. }
  468. //
  469. // Implementing the TableModel interface
  470. //
  471. /**
  472. * Returns the number of rows in this data table.
  473. * @return the number of rows in the model
  474. */
  475. public int getRowCount() {
  476. return dataVector.size();
  477. }
  478. /**
  479. * Returns the number of columns in this data table.
  480. * @return the number of columns in the model
  481. */
  482. public int getColumnCount() {
  483. return columnIdentifiers.size();
  484. }
  485. /**
  486. * Returns the column name.
  487. * @return a name for this column using the string value of the
  488. * appropriate member in <I>columnIdentfiers</I>. If <I>columnIdentfiers</I>
  489. * is null or does not have and entry for this index return the default
  490. * name provided by the superclass.
  491. */
  492. public String getColumnName(int column) {
  493. if (columnIdentifiers == null || columnIdentifiers.size() <= column) {
  494. return super.getColumnName(column);
  495. }
  496. Object id = columnIdentifiers.elementAt(column);
  497. if (id == null) {
  498. return super.getColumnName(column);
  499. }
  500. else {
  501. return id.toString();
  502. }
  503. }
  504. /**
  505. * Returns true if the cell at <I>row</I> and <I>column</I>
  506. * is editable. Otherwise, the setValueAt() on the cell will not change
  507. * the value of that cell.
  508. *
  509. * @param row the row whose value is to be looked up
  510. * @param column the column whose value is to be looked up
  511. * @return true if the cell is editable.
  512. * @see #setValueAt
  513. */
  514. public boolean isCellEditable(int row, int column) {
  515. return true;
  516. }
  517. /**
  518. * Returns an attribute value for the cell at <I>row</I>
  519. * and <I>column</I>.
  520. *
  521. * @param row the row whose value is to be looked up
  522. * @param column the column whose value is to be looked up
  523. * @return the value Object at the specified cell
  524. * @exception ArrayIndexOutOfBoundsException if an invalid row or
  525. * column was given.
  526. */
  527. public Object getValueAt(int row, int column) {
  528. Vector rowVector = (Vector)dataVector.elementAt(row);
  529. return rowVector.elementAt(column);
  530. }
  531. /**
  532. * Sets the object value for the cell at <I>column</I> and
  533. * <I>row</I>. <I>aValue</I> is the new value. This method
  534. * will generate a tableChanged() notification.
  535. *
  536. * @param aValue the new value. This can be null.
  537. * @param row the row whose value is to be changed
  538. * @param column the column whose value is to be changed
  539. * @exception ArrayIndexOutOfBoundsException if an invalid row or
  540. * column was given.
  541. */
  542. public void setValueAt(Object aValue, int row, int column) {
  543. Vector rowVector = (Vector)dataVector.elementAt(row);
  544. rowVector.setElementAt(aValue, column);
  545. // generate notification
  546. fireTableChanged(new TableModelEvent(this, row, row, column));
  547. }
  548. //
  549. // Protected Methods
  550. //
  551. /** Returns a Vector that contains the same objects as the array */
  552. protected static Vector convertToVector(Object[] anArray) {
  553. if (anArray == null)
  554. return null;
  555. Vector v = new Vector(anArray.length);
  556. for (int i=0; i < anArray.length; i++) {
  557. v.addElement(anArray[i]);
  558. }
  559. return v;
  560. }
  561. /** Returns a Vector of Vectors that contains the same objects as the array */
  562. protected static Vector convertToVector(Object[][] anArray) {
  563. if (anArray == null)
  564. return null;
  565. Vector v = new Vector(anArray.length);
  566. for (int i=0; i < anArray.length; i++) {
  567. v.addElement(convertToVector(anArray[i]));
  568. }
  569. return v;
  570. }
  571. } // End of class DefaultTableModel