1 /* 2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.swing.table; 27 28 import java.io.Serializable; 29 import java.util.Vector; 30 import java.util.Enumeration; 31 import javax.swing.event.TableModelEvent; 32 33 34 /** 35 * This is an implementation of <code>TableModel</code> that 36 * uses a <code>Vector</code> of <code>Vectors</code> to store the 37 * cell value objects. 38 * <p> 39 * <strong>Warning:</strong> <code>DefaultTableModel</code> returns a 40 * column class of <code>Object</code>. When 41 * <code>DefaultTableModel</code> is used with a 42 * <code>TableRowSorter</code> this will result in extensive use of 43 * <code>toString</code>, which for non-<code>String</code> data types 44 * is expensive. If you use <code>DefaultTableModel</code> with a 45 * <code>TableRowSorter</code> you are strongly encouraged to override 46 * <code>getColumnClass</code> to return the appropriate type. 47 * <p> 48 * <strong>Warning:</strong> 49 * Serialized objects of this class will not be compatible with 50 * future Swing releases. The current serialization support is 51 * appropriate for short term storage or RMI between applications running 52 * the same version of Swing. As of 1.4, support for long term storage 53 * of all JavaBeans™ 54 * has been added to the <code>java.beans</code> package. 55 * Please see {@link java.beans.XMLEncoder}. 56 * 57 * @author Philip Milne 58 * 59 * @see TableModel 60 * @see #getDataVector 61 */ 62 @SuppressWarnings("serial") // Same-version serialization only 63 public class DefaultTableModel extends AbstractTableModel implements Serializable { 64 65 // 66 // Instance Variables 67 // 68 69 /** 70 * The <code>Vector</code> of <code>Vectors</code> of 71 * <code>Object</code> values. 72 */ 73 protected Vector<Vector<Object>> dataVector; 74 75 /** The <code>Vector</code> of column identifiers. */ 76 protected Vector<Object> columnIdentifiers; 77 78 // 79 // Constructors 80 // 81 82 /** 83 * Constructs a default <code>DefaultTableModel</code> 84 * which is a table of zero columns and zero rows. 85 */ 86 public DefaultTableModel() { 87 this(0, 0); 88 } 89 90 private static <E> Vector<E> newVector(int size) { 91 Vector<E> v = new Vector<>(size); 92 v.setSize(size); 93 return v; 94 } 95 96 /** 97 * Constructs a <code>DefaultTableModel</code> with 98 * <code>rowCount</code> and <code>columnCount</code> of 99 * <code>null</code> object values. 100 * 101 * @param rowCount the number of rows the table holds 102 * @param columnCount the number of columns the table holds 103 * 104 * @see #setValueAt 105 */ 106 public DefaultTableModel(int rowCount, int columnCount) { 107 this(newVector(columnCount), rowCount); 108 } 109 110 /** 111 * Constructs a <code>DefaultTableModel</code> with as many columns 112 * as there are elements in <code>columnNames</code> 113 * and <code>rowCount</code> of <code>null</code> 114 * object values. Each column's name will be taken from 115 * the <code>columnNames</code> vector. 116 * 117 * @param columnNames <code>vector</code> containing the names 118 * of the new columns; if this is 119 * <code>null</code> then the model has no columns 120 * @param rowCount the number of rows the table holds 121 * @see #setDataVector 122 * @see #setValueAt 123 */ 124 public DefaultTableModel(Vector<Object> columnNames, int rowCount) { 125 setDataVector(newVector(rowCount), columnNames); 126 } 127 128 /** 129 * Constructs a <code>DefaultTableModel</code> with as many 130 * columns as there are elements in <code>columnNames</code> 131 * and <code>rowCount</code> of <code>null</code> 132 * object values. Each column's name will be taken from 133 * the <code>columnNames</code> array. 134 * 135 * @param columnNames <code>array</code> containing the names 136 * of the new columns; if this is 137 * <code>null</code> then the model has no columns 138 * @param rowCount the number of rows the table holds 139 * @see #setDataVector 140 * @see #setValueAt 141 */ 142 public DefaultTableModel(Object[] columnNames, int rowCount) { 143 this(convertToVector(columnNames), rowCount); 144 } 145 146 /** 147 * Constructs a <code>DefaultTableModel</code> and initializes the table 148 * by passing <code>data</code> and <code>columnNames</code> 149 * to the <code>setDataVector</code> method. 150 * 151 * @param data the data of the table, a <code>Vector</code> 152 * of <code>Vector</code>s of <code>Object</code> 153 * values 154 * @param columnNames <code>vector</code> containing the names 155 * of the new columns 156 * @see #getDataVector 157 * @see #setDataVector 158 */ 159 public DefaultTableModel(Vector<Vector<Object>> data, Vector<Object> columnNames) { 160 setDataVector(data, columnNames); 161 } 162 163 /** 164 * Constructs a <code>DefaultTableModel</code> and initializes the table 165 * by passing <code>data</code> and <code>columnNames</code> 166 * to the <code>setDataVector</code> 167 * method. The first index in the <code>Object[][]</code> array is 168 * the row index and the second is the column index. 169 * 170 * @param data the data of the table 171 * @param columnNames the names of the columns 172 * @see #getDataVector 173 * @see #setDataVector 174 */ 175 public DefaultTableModel(Object[][] data, Object[] columnNames) { 176 setDataVector(data, columnNames); 177 } 178 179 /** 180 * Returns the <code>Vector</code> of <code>Vectors</code> 181 * that contains the table's 182 * data values. The vectors contained in the outer vector are 183 * each a single row of values. In other words, to get to the cell 184 * at row 1, column 5: <p> 185 * 186 * <code>((Vector)getDataVector().elementAt(1)).elementAt(5);</code> 187 * 188 * @return the vector of vectors containing the tables data values 189 * 190 * @see #newDataAvailable 191 * @see #newRowsAdded 192 * @see #setDataVector 193 */ 194 public Vector<Vector<Object>> getDataVector() { 195 return dataVector; 196 } 197 198 private static <E> Vector<E> nonNullVector(Vector<E> v) { 199 return (v != null) ? v : new Vector<>(); 200 } 201 202 /** 203 * Replaces the current <code>dataVector</code> instance variable 204 * with the new <code>Vector</code> of rows, <code>dataVector</code>. 205 * Each row is represented in <code>dataVector</code> as a 206 * <code>Vector</code> of <code>Object</code> values. 207 * <code>columnIdentifiers</code> are the names of the new 208 * columns. The first name in <code>columnIdentifiers</code> is 209 * mapped to column 0 in <code>dataVector</code>. Each row in 210 * <code>dataVector</code> is adjusted to match the number of 211 * columns in <code>columnIdentifiers</code> 212 * either by truncating the <code>Vector</code> if it is too long, 213 * or adding <code>null</code> values if it is too short. 214 * <p>Note that passing in a <code>null</code> value for 215 * <code>dataVector</code> results in unspecified behavior, 216 * an possibly an exception. 217 * 218 * @param dataVector the new data vector 219 * @param columnIdentifiers the names of the columns 220 * @see #getDataVector 221 */ 222 public void setDataVector(Vector<Vector<Object>> dataVector, 223 Vector<Object> columnIdentifiers) { 224 this.dataVector = nonNullVector(dataVector); 225 this.columnIdentifiers = nonNullVector(columnIdentifiers); 226 justifyRows(0, getRowCount()); 227 fireTableStructureChanged(); 228 } 229 230 /** 231 * Replaces the value in the <code>dataVector</code> instance 232 * variable with the values in the array <code>dataVector</code>. 233 * The first index in the <code>Object[][]</code> 234 * array is the row index and the second is the column index. 235 * <code>columnIdentifiers</code> are the names of the new columns. 236 * 237 * @param dataVector the new data vector 238 * @param columnIdentifiers the names of the columns 239 * @see #setDataVector(Vector, Vector) 240 */ 241 public void setDataVector(Object[][] dataVector, Object[] columnIdentifiers) { 242 setDataVector(convertToVector(dataVector), convertToVector(columnIdentifiers)); 243 } 244 245 /** 246 * Equivalent to <code>fireTableChanged</code>. 247 * 248 * @param event the change event 249 * 250 */ 251 public void newDataAvailable(TableModelEvent event) { 252 fireTableChanged(event); 253 } 254 255 // 256 // Manipulating rows 257 // 258 259 private void justifyRows(int from, int to) { 260 // Sometimes the DefaultTableModel is subclassed 261 // instead of the AbstractTableModel by mistake. 262 // Set the number of rows for the case when getRowCount 263 // is overridden. 264 dataVector.setSize(getRowCount()); 265 266 for (int i = from; i < to; i++) { 267 if (dataVector.elementAt(i) == null) { 268 dataVector.setElementAt(new Vector<>(), i); 269 } 270 ((Vector)dataVector.elementAt(i)).setSize(getColumnCount()); 271 } 272 } 273 274 /** 275 * Ensures that the new rows have the correct number of columns. 276 * This is accomplished by using the <code>setSize</code> method in 277 * <code>Vector</code> which truncates vectors 278 * which are too long, and appends <code>null</code>s if they 279 * are too short. 280 * This method also sends out a <code>tableChanged</code> 281 * notification message to all the listeners. 282 * 283 * @param e this <code>TableModelEvent</code> describes 284 * where the rows were added. 285 * If <code>null</code> it assumes 286 * all the rows were newly added 287 * @see #getDataVector 288 */ 289 public void newRowsAdded(TableModelEvent e) { 290 justifyRows(e.getFirstRow(), e.getLastRow() + 1); 291 fireTableChanged(e); 292 } 293 294 /** 295 * Equivalent to <code>fireTableChanged</code>. 296 * 297 * @param event the change event 298 * 299 */ 300 public void rowsRemoved(TableModelEvent event) { 301 fireTableChanged(event); 302 } 303 304 /** 305 * Obsolete as of Java 2 platform v1.3. Please use <code>setRowCount</code> instead. 306 */ 307 /* 308 * Sets the number of rows in the model. If the new size is greater 309 * than the current size, new rows are added to the end of the model 310 * If the new size is less than the current size, all 311 * rows at index <code>rowCount</code> and greater are discarded. 312 * 313 * @param rowCount the new number of rows 314 * @see #setRowCount 315 */ 316 public void setNumRows(int rowCount) { 317 int old = getRowCount(); 318 if (old == rowCount) { 319 return; 320 } 321 dataVector.setSize(rowCount); 322 if (rowCount <= old) { 323 fireTableRowsDeleted(rowCount, old-1); 324 } 325 else { 326 justifyRows(old, rowCount); 327 fireTableRowsInserted(old, rowCount-1); 328 } 329 } 330 331 /** 332 * Sets the number of rows in the model. If the new size is greater 333 * than the current size, new rows are added to the end of the model 334 * If the new size is less than the current size, all 335 * rows at index <code>rowCount</code> and greater are discarded. 336 * 337 * @see #setColumnCount 338 * @since 1.3 339 * 340 * @param rowCount number of rows in the model 341 */ 342 public void setRowCount(int rowCount) { 343 setNumRows(rowCount); 344 } 345 346 /** 347 * Adds a row to the end of the model. The new row will contain 348 * <code>null</code> values unless <code>rowData</code> is specified. 349 * Notification of the row being added will be generated. 350 * 351 * @param rowData optional data of the row being added 352 */ 353 public void addRow(Vector<Object> rowData) { 354 insertRow(getRowCount(), rowData); 355 } 356 357 /** 358 * Adds a row to the end of the model. The new row will contain 359 * <code>null</code> values unless <code>rowData</code> is specified. 360 * Notification of the row being added will be generated. 361 * 362 * @param rowData optional data of the row being added 363 */ 364 public void addRow(Object[] rowData) { 365 addRow(convertToVector(rowData)); 366 } 367 368 /** 369 * Inserts a row at <code>row</code> in the model. The new row 370 * will contain <code>null</code> values unless <code>rowData</code> 371 * is specified. Notification of the row being added will be generated. 372 * 373 * @param row the row index of the row to be inserted 374 * @param rowData optional data of the row being added 375 * @exception ArrayIndexOutOfBoundsException if the row was invalid 376 */ 377 public void insertRow(int row, Vector<Object> rowData) { 378 dataVector.insertElementAt(rowData, row); 379 justifyRows(row, row+1); 380 fireTableRowsInserted(row, row); 381 } 382 383 /** 384 * Inserts a row at <code>row</code> in the model. The new row 385 * will contain <code>null</code> values unless <code>rowData</code> 386 * is specified. Notification 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, Object[] rowData) { 393 insertRow(row, convertToVector(rowData)); 394 } 395 396 private static int gcd(int i, int j) { 397 return (j == 0) ? i : gcd(j, i%j); 398 } 399 400 private static <E> void rotate(Vector<E> v, int a, int b, int shift) { 401 int size = b - a; 402 int r = size - shift; 403 int g = gcd(size, r); 404 for(int i = 0; i < g; i++) { 405 int to = i; 406 E tmp = v.elementAt(a + to); 407 for(int from = (to + r) % size; from != i; from = (to + r) % size) { 408 v.setElementAt(v.elementAt(a + from), a + to); 409 to = from; 410 } 411 v.setElementAt(tmp, a + to); 412 } 413 } 414 415 /** 416 * Moves one or more rows from the inclusive range <code>start</code> to 417 * <code>end</code> to the <code>to</code> position in the model. 418 * After the move, the row that was at index <code>start</code> 419 * will be at index <code>to</code>. 420 * This method will send a <code>tableChanged</code> notification 421 message to all the listeners. 422 * 423 * <pre> 424 * Examples of moves: 425 * 426 * 1. moveRow(1,3,5); 427 * a|B|C|D|e|f|g|h|i|j|k - before 428 * a|e|f|g|h|B|C|D|i|j|k - after 429 * 430 * 2. moveRow(6,7,1); 431 * a|b|c|d|e|f|G|H|i|j|k - before 432 * a|G|H|b|c|d|e|f|i|j|k - after 433 * </pre> 434 * 435 * @param start the starting row index to be moved 436 * @param end the ending row index to be moved 437 * @param to the destination of the rows to be moved 438 * @exception ArrayIndexOutOfBoundsException if any of the elements 439 * would be moved out of the table's range 440 * 441 */ 442 public void moveRow(int start, int end, int to) { 443 int shift = to - start; 444 int first, last; 445 if (shift < 0) { 446 first = to; 447 last = end; 448 } 449 else { 450 first = start; 451 last = to + end - start; 452 } 453 rotate(dataVector, first, last + 1, shift); 454 455 fireTableRowsUpdated(first, last); 456 } 457 458 /** 459 * Removes the row at <code>row</code> from the model. Notification 460 * of the row being removed will be sent to all the listeners. 461 * 462 * @param row the row index of the row to be removed 463 * @exception ArrayIndexOutOfBoundsException if the row was invalid 464 */ 465 public void removeRow(int row) { 466 dataVector.removeElementAt(row); 467 fireTableRowsDeleted(row, row); 468 } 469 470 // 471 // Manipulating columns 472 // 473 474 /** 475 * Replaces the column identifiers in the model. If the number of 476 * <code>newIdentifier</code>s is greater than the current number 477 * of columns, new columns are added to the end of each row in the model. 478 * If the number of <code>newIdentifier</code>s is less than the current 479 * number of columns, all the extra columns at the end of a row are 480 * discarded. 481 * 482 * @param columnIdentifiers vector of column identifiers. If 483 * <code>null</code>, set the model 484 * to zero columns 485 * @see #setNumRows 486 */ 487 public void setColumnIdentifiers(Vector<Object> columnIdentifiers) { 488 setDataVector(dataVector, columnIdentifiers); 489 } 490 491 /** 492 * Replaces the column identifiers in the model. If the number of 493 * <code>newIdentifier</code>s is greater than the current number 494 * of columns, new columns are added to the end of each row in the model. 495 * If the number of <code>newIdentifier</code>s is less than the current 496 * number of columns, all the extra columns at the end of a row are 497 * discarded. 498 * 499 * @param newIdentifiers array of column identifiers. 500 * If <code>null</code>, set 501 * the model to zero columns 502 * @see #setNumRows 503 */ 504 public void setColumnIdentifiers(Object[] newIdentifiers) { 505 setColumnIdentifiers(convertToVector(newIdentifiers)); 506 } 507 508 /** 509 * Sets the number of columns in the model. If the new size is greater 510 * than the current size, new columns are added to the end of the model 511 * with <code>null</code> cell values. 512 * If the new size is less than the current size, all columns at index 513 * <code>columnCount</code> and greater are discarded. 514 * 515 * @param columnCount the new number of columns in the model 516 * 517 * @see #setColumnCount 518 * @since 1.3 519 */ 520 public void setColumnCount(int columnCount) { 521 columnIdentifiers.setSize(columnCount); 522 justifyRows(0, getRowCount()); 523 fireTableStructureChanged(); 524 } 525 526 /** 527 * Adds a column to the model. The new column will have the 528 * identifier <code>columnName</code>, which may be null. This method 529 * will send a 530 * <code>tableChanged</code> notification message to all the listeners. 531 * This method is a cover for <code>addColumn(Object, Vector)</code> which 532 * uses <code>null</code> as the data vector. 533 * 534 * @param columnName the identifier of the column being added 535 */ 536 public void addColumn(Object columnName) { 537 addColumn(columnName, (Vector<Object>)null); 538 } 539 540 /** 541 * Adds a column to the model. The new column will have the 542 * identifier <code>columnName</code>, which may be null. 543 * <code>columnData</code> is the 544 * optional vector of data for the column. If it is <code>null</code> 545 * the column is filled with <code>null</code> values. Otherwise, 546 * the new data will be added to model starting with the first 547 * element going to row 0, etc. This method will send a 548 * <code>tableChanged</code> notification message to all the listeners. 549 * 550 * @param columnName the identifier of the column being added 551 * @param columnData optional data of the column being added 552 */ 553 public void addColumn(Object columnName, Vector<Object> columnData) { 554 columnIdentifiers.addElement(columnName); 555 if (columnData != null) { 556 int columnSize = columnData.size(); 557 if (columnSize > getRowCount()) { 558 dataVector.setSize(columnSize); 559 } 560 justifyRows(0, getRowCount()); 561 int newColumn = getColumnCount() - 1; 562 for(int i = 0; i < columnSize; i++) { 563 Vector<Object> row = dataVector.elementAt(i); 564 row.setElementAt(columnData.elementAt(i), newColumn); 565 } 566 } 567 else { 568 justifyRows(0, getRowCount()); 569 } 570 571 fireTableStructureChanged(); 572 } 573 574 /** 575 * Adds a column to the model. The new column will have the 576 * identifier <code>columnName</code>. <code>columnData</code> is the 577 * optional array of data for the column. If it is <code>null</code> 578 * the column is filled with <code>null</code> values. Otherwise, 579 * the new data will be added to model starting with the first 580 * element going to row 0, etc. This method will send a 581 * <code>tableChanged</code> notification message to all the listeners. 582 * 583 * @param columnName identifier of the newly created column 584 * @param columnData new data to be added to the column 585 * 586 * @see #addColumn(Object, Vector) 587 */ 588 public void addColumn(Object columnName, Object[] columnData) { 589 addColumn(columnName, convertToVector(columnData)); 590 } 591 592 // 593 // Implementing the TableModel interface 594 // 595 596 /** 597 * Returns the number of rows in this data table. 598 * @return the number of rows in the model 599 */ 600 public int getRowCount() { 601 return dataVector.size(); 602 } 603 604 /** 605 * Returns the number of columns in this data table. 606 * @return the number of columns in the model 607 */ 608 public int getColumnCount() { 609 return columnIdentifiers.size(); 610 } 611 612 /** 613 * Returns the column name. 614 * 615 * @return a name for this column using the string value of the 616 * appropriate member in <code>columnIdentifiers</code>. 617 * If <code>columnIdentifiers</code> does not have an entry 618 * for this index, returns the default 619 * name provided by the superclass. 620 */ 621 public String getColumnName(int column) { 622 Object id = null; 623 // This test is to cover the case when 624 // getColumnCount has been subclassed by mistake ... 625 if (column < columnIdentifiers.size() && (column >= 0)) { 626 id = columnIdentifiers.elementAt(column); 627 } 628 return (id == null) ? super.getColumnName(column) 629 : id.toString(); 630 } 631 632 /** 633 * Returns true regardless of parameter values. 634 * 635 * @param row the row whose value is to be queried 636 * @param column the column whose value is to be queried 637 * @return true 638 * @see #setValueAt 639 */ 640 public boolean isCellEditable(int row, int column) { 641 return true; 642 } 643 644 /** 645 * Returns an attribute value for the cell at <code>row</code> 646 * and <code>column</code>. 647 * 648 * @param row the row whose value is to be queried 649 * @param column the column whose value is to be queried 650 * @return the value Object at the specified cell 651 * @exception ArrayIndexOutOfBoundsException if an invalid row or 652 * column was given 653 */ 654 public Object getValueAt(int row, int column) { 655 Vector<Object> rowVector = dataVector.elementAt(row); 656 return rowVector.elementAt(column); 657 } 658 659 /** 660 * Sets the object value for the cell at <code>column</code> and 661 * <code>row</code>. <code>aValue</code> is the new value. This method 662 * will generate a <code>tableChanged</code> notification. 663 * 664 * @param aValue the new value; this can be null 665 * @param row the row whose value is to be changed 666 * @param column the column whose value is to be changed 667 * @exception ArrayIndexOutOfBoundsException if an invalid row or 668 * column was given 669 */ 670 public void setValueAt(Object aValue, int row, int column) { 671 Vector<Object> rowVector = dataVector.elementAt(row); 672 rowVector.setElementAt(aValue, column); 673 fireTableCellUpdated(row, column); 674 } 675 676 // 677 // Protected Methods 678 // 679 680 /** 681 * Returns a vector that contains the same objects as the array. 682 * @param anArray the array to be converted 683 * @return the new vector; if <code>anArray</code> is <code>null</code>, 684 * returns <code>null</code> 685 */ 686 protected static Vector<Object> convertToVector(Object[] anArray) { 687 if (anArray == null) { 688 return null; 689 } 690 Vector<Object> v = new Vector<>(anArray.length); 691 for (Object o : anArray) { 692 v.addElement(o); 693 } 694 return v; 695 } 696 697 /** 698 * Returns a vector of vectors that contains the same objects as the array. 699 * @param anArray the double array to be converted 700 * @return the new vector of vectors; if <code>anArray</code> is 701 * <code>null</code>, returns <code>null</code> 702 */ 703 protected static Vector<Vector<Object>> convertToVector(Object[][] anArray) { 704 if (anArray == null) { 705 return null; 706 } 707 Vector<Vector<Object>> v = new Vector<>(anArray.length); 708 for (Object[] o : anArray) { 709 v.addElement(convertToVector(o)); 710 } 711 return v; 712 } 713 714 } // End of class DefaultTableModel