sabato 11 settembre 2010

Adding row to a JTable that uses AbstractTableModel (Java 1.6)

I'm writing a Java program that uses JTable and AbstractTableModel. As usual, I like to improvise, I like to add features following the inspiration. This time I wanted to create a table with static data, but at a certain point...hey, why not adding data dynamically? 
This way of life is funny but can create a lot of problems: I'm stubborn and I want to fix them.
"Every table object uses a table model object to manage the actual table data. A table model object must implement the TableModel interface. If the programmer does not provide a table model object, JTable automatically creates an instance of DefaultTableModel."
DefaultTableModel can easily add/remove a row, but what happens if we want to use AbstractTableModel?
I browsed the internet but I wasn't able to find a solution: people suggest to use DefaultTableModel (too easy), to call the superclass method fireTableRowsInserted(), or fireTableDataChanged() and fireTableRowsUpdated(int firstRow, int lastRow)...
These solutions don't work for me.
I found a fix experimenting a lot of ideas but the solution is quite easy: we have to pass the new data to the AbstractTableModel and call:

table.revalidate();
table.repaint();
tableModel.fireTableDataChanged();

That's all! Below a concrete example, the code I'm  using:

import java.stuff...

private JTable table;

private Object[][] newData, oldData;

MyTableModel myTableModel;

     public myClass() {

         newData = {{"Kathy", "Smith",
                     "Snowboarding", new Integer(5), new Boolean(false)},
                    {"John", "Doe",
                     "Rowing", new Integer(3), new              Boolean(true)},
                    {"Sue", "Black",
                     "Knitting", new Integer(2), new  Boolean(false)},
                    {"Jane", "White",
                     "Speed reading", new Integer(20), new Boolean(true)},
                    {"Joe", "Brown",
                     "Pool", new Integer(10), new Boolean(false)}


          myTableModel = new MyTableModel();
          myTableModel.setData(newData);

          table = new JTable(myTableModel);
          // add features to the table here
     }

     /**
      * This method adds a row to the table. We have to
      * append the new row to the existing data, pass new
      * data to AbstractTableModel
      */
      private void addRow() {
          oldData = newData;
          newData = new Object[oldData.length + 1][];

          // Copy old data to new data
          for (int x = 0; x < oldData.length; x++) { 

              newData[x] = oldData[x]; 
          } 

         // Append new row 
         newData[oldData.length] = new Object[]{new Boolean(true)}; 

         // Pass the new data to the table model
         myTableModel.setModelData(newData); 

         // Update the table 
         table.revalidate(); 
         table.repaint();

         myTableModel.fireTableDataChanged();
    } 

    class MyTableModel extends AbstractTableModel { 
       // Static column Names 
      private String[] columnNames = {"bla", "bla", "bla", 
                                      "bla", "bla"}; 
      // Data to populate the table 
      private Object[][] data; 

      public void setData(Object[][] data){ 
          this.data = data; 
      } 

      @Override 
      public int getColumnCount() { 
          return columnNames.length; 
      } 

      @Override 
      public int getRowCount() { 
          return data.length; 
      } 

      @Override 
      public String getColumnName(int col) { 
          return columnNames[col]; 
      } 

      @Override 
      public Object getValueAt(int row, int col) { 
          return data[row][col]; 
      }  

      @Override 
      public Class getColumnClass(int c) { 
          return getValueAt(0, c).getClass(); 
      }  

      @Override 
      public boolean isCellEditable(int row, int col) { 
           return true; 
      } 
      
      @Override 
      public void setValueAt(Object value, int row, int col) { 
          data[row][col] = value; 
          fireTableCellUpdated(row, col); 
      } 
    }

I apologize if blogspot doesn't format the code in the right way and I hope this solution will work for you as it does for me.

Best regards.