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    }