Hi!

My question is how could I add the checkbox column to my table. Well, I've tried the following solution, however it provides error message (null), if the table field is empty:

tableModel = new QueryTableModel();
        tableAttributesFormTypes = new JTable();
        tableAttributesFormTypes.setModel(tableModel);
        tableModel.setQuery("select at_code, at_title, at_type from Attributes");

class QueryTableModel extends AbstractTableModel {
...  
public Class getColumnClass(int col) {
   return getValueAt(0, col).getClass();
}
...
  public void setQuery(String q) {
    cache = new Vector();
    try {
      // Execute the query and store the result set and its metadata
      ResultSet rs = statement.executeQuery(q);
      ResultSetMetaData meta = (ResultSetMetaData) rs.getMetaData();
      colCount = meta.getColumnCount();

      headers = new String[colCount];
      for (int h = 1; h <= colCount; h++) {
        headers[h - 1] = meta.getColumnName(h);
      }

      while (rs.next()) {
        String[] record = new String[colCount];
        for (int i = 0; i < colCount; i++) {
          record[i] = rs.getString(i + 1);
        }
        cache.addElement(record);
      }
      fireTableChanged(null);
    } catch (Exception e) {
      cache = new Vector();
      System.out.println(e.getMessage());  // THIS EXCEPTION IS EXECUTED!!!
    }
  }
}

So, how could I change my code to allow empty fields in the table?! Thanks!

Do you know what line of code is causing the exception? Try doing e.printStackTrace(), rather than System.out.println(e.getMessage()).

This is the error message:

java.lang.NullPointerException
        at SystClasses.QueryTableModel.getColumnClass(QueryTableModel.java:48)
        at javax.swing.table.TableRowSorter.useToString(TableRowSorter.java:224)
        at javax.swing.DefaultRowSorter.updateUseToString(DefaultRowSorter.java:607)
        at javax.swing.DefaultRowSorter.sort(DefaultRowSorter.java:556)
        at javax.swing.DefaultRowSorter.allChanged(DefaultRowSorter.java:816)
        at javax.swing.DefaultRowSorter.modelStructureChanged(DefaultRowSorter.java:826)
        at javax.swing.JTable.tableChanged(JTable.java:4364)
        at javax.swing.table.AbstractTableModel.fireTableChanged(AbstractTableModel.java:280)
        at SystClasses.QueryTableModel.setQuery(QueryTableModel.java:92)
        at SystClasses.Form.createAdminTable(Form.java:421)
        at SystClasses.Form.createAdminRightPanel(Form.java:372)
        at SystClasses.Form.createBase(Form.java:189)
        at SystClasses.Form.access$000(Form.java:44)
        at SystClasses.Form$2.actionPerformed(Form.java:143)
        at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
        at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
        at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387)
        at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242)
        at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236)
        at java.awt.Component.processMouseEvent(Component.java:6263)
        at javax.swing.JComponent.processMouseEvent(JComponent.java:3267)
        at java.awt.Component.processEvent(Component.java:6028)
        at java.awt.Container.processEvent(Container.java:2041)
        at java.awt.Component.dispatchEventImpl(Component.java:4630)
        at java.awt.Container.dispatchEventImpl(Container.java:2099)
        at java.awt.Component.dispatchEvent(Component.java:4460)
        at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4574)
        at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4238)
        at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4168)
        at java.awt.Container.dispatchEventImpl(Container.java:2085)
        at java.awt.Window.dispatchEventImpl(Window.java:2478)
        at java.awt.Component.dispatchEvent(Component.java:4460)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

OK. On line 10 of the stack trace it says the error occurs on line 92 in your setQuery method. So what is on line 92?

Line 92 is fireTableChanged(null); So, what could be the problem? It works without checkboxes.

I'm not sure what the problem is, but maybe you shouldn't be passing null into fireTableChanged? Try creating a valid TableModelEvent to pass instead.

You are getting a null value from getColumnClass, so I would look closer at what this method is returning from your model

public Class getColumnClass(int col) {
   return getValueAt(0, col).getClass();
}

Ok, how could I check the output? Something like this?:

public Class getColumnClass(int col) {
    Class a = getValueAt(0, col).getClass();
    System.out.println(a.getName());
    return a;
  }

I suppose that fireTableChanged(null) is ok... The problem is exactly with the empty field in a table. However, I don't know how and where to check if the field is empty or not.

Ezzaral found on line 2 of the stack trace that the error is coming from getColumnClass, so the suggestion is to look at what's happening in that method. Maybe you can step through the code in a debugger, or put some print statements in there to try to see what's going on.

You can either explicitly return the class for that column, or you can make sure that getValueAt() always returns a value for that column. I assume it's a boolean for the checkbox, so it needs to return true or false instead of a null.

Ok. I'll try.

Yes, getValueAt returns null. I've changed the code to:

public Object getValueAt(int row, int col) {
    String str = ((String[]) cache.elementAt(row))[col];
    System.out.println(str);
    if (str != null)
        return str;
    else return "";
  }

Now I don't have an error message. But there are 1 or 0 instead of checkboxes... Why it could happen?

You probably need that method to return a Boolean object for the check box renderer.

Should I modify getColumnClass or getValueAt? I suppose that I should put somewhere the "if..then" statement and something like "return Boolean.class;". However, what should be in the if (???)?

Well, it looks like all of your columns are currently String, because you have built them from the ResultSet using Strings for every value.

You could change the method that builds your table model to read the data type from the metadata and create an object of the appropriate type as you fill in the data. You also would need to change your 'record' array to Object[] instead of String[].

Using rs.getObject() instead of getString() might be viable as well, but if you are using 0 and 1 to represent your boolean values, they will most likely be read as Char or Integer and the default renderer will not be a check box.

As you can see, the tricky part is to keep your QueryTableModel as generic as possible and still translate those columns to the desired data type. If you hard-code the Boolean column in a particular location, you've tied your model to a specific data set - which is not necessarily a problem, but it will obviously limit it's re-use.

Well, my code is very similar to that one: http://www.kodejava.org/examples/699.html. The only difference is that in my case it doesn't work

I've tried playing with "return Boolean.class" in getColumnClass, but it didn't help me solving the problem...

I'm sorry that I cannot write a working code based on suggestions of Ezzaral, because I'm beginner. Please, could someone be more specific. Thanks!

Please post your (complete) code and also what error message you are getting. What is it that doesn't work? Be as specific as you can.

You cannot simply make getColumnClass() return Boolean.class for that column because your data is a String value of "1" or "0". You need to store those values as booleans in your table model.

Ok, I read these data from my database's table, where I have one column of boolean type. In this case, what do you mean by storing those values as booleans? Should I copy the data from database into some "Object[][] arr" and then redefine boolean column like Boolean.true or Boolean.false?

You might try changing this part of your model creation code

while (rs.next()) {
        Object[] record = new Object[colCount];
        for (int i = 0; i < colCount; i++) {
          record[i] = rs.getObject(i + 1);
        }
        cache.addElement(record);
      }

I'm not certain if you have an actual boolean coming from your rs.

I've tried your code and received the error shown below. So, as I understand from the error message, the column from database's table contains integers. But why?? I've used correct mapping of SQL data types into Java (http://download.oracle.com/javase/1.3/docs/guide/jdbc/spec/jdbc-spec.frame8.html). In other words, I've defined the BIT value for the column in the database's table.

Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Boolean
        at javax.swing.JTable$BooleanRenderer.getTableCellRendererComponent(JTable.java:5396)
        at javax.swing.JTable.prepareRenderer(JTable.java:5719)
        at javax.swing.plaf.basic.BasicTableUI.paintCell(BasicTableUI.java:2072)
        at javax.swing.plaf.basic.BasicTableUI.paintCells(BasicTableUI.java:1974)
        at javax.swing.plaf.basic.BasicTableUI.paint(BasicTableUI.java:1770)

I've added System.out.println:

while (rs.next()) {
        Object[] record = new Object[colCount];
        for (int i = 0; i < colCount; i++) {
          System.out.println(i + ", " + rs.getObject(i + 1));
          record[i] = rs.getObject(i + 1);
        }
        cache.addElement(record);
      }

Output results are:
0, 1
1, 1
2, 20
3, true

So, you may see that column Nr 3 includes boolean variable. But then I completely don't understand the problem reasons.

Did you make any changes in your getColumnClass() method? What prompted the class cast exception?

Hi! Well, finally it works:

@Override
  public Class getColumnClass(int col) {
    if (col == 3) {
       return Boolean.class;
    } else {
       return super.getColumnClass(col);
    }
  }

...

     while (rs.next()) {
        Object[] record = new Object[colCount];
        for (int i = 0; i < colCount; i++) {
          record[i] = rs.getObject(i + 1);
        }
        cache.addElement(record);
      }
      fireTableChanged(null);
    } catch (Exception e) {
      cache = new Vector();
      e.printStackTrace();
    }
...

  @Override
  public boolean isCellEditable(int row, int col) {
    return true;
  }

But the problem is that I cannot change Checkbox value...It seems to be unaccessible. Could you please suggest me something?

Ups, I just forgot to write procedure setValueAt:

public void setValueAt(Object value, int row, int col) {
      ((Object[]) cache.elementAt(row))[col] = value;
      fireTableCellUpdated(row, col);
  }

So, now everything works correctly. Thanks!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.