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