aspose file tools*
The moose likes Swing / AWT / SWT and the fly likes TableModel - why getColumnClass? Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Swing / AWT / SWT
Bookmark "TableModel - why getColumnClass?" Watch "TableModel - why getColumnClass?" New topic
Author

TableModel - why getColumnClass?

Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19670
    
  18

I've just been busy on a project where I am using a JTable to let users edit several parameters. Now of course, I want users to edit boolean values using a check box, and numbers using a right-aligned label and text field.

I've done this by creating a custom JTable, and overriding the getCellRenderer(int row, int col) and getCellEditor(int row, int col) methods as follows:

Of course, with more cases and the same code for getCellEditor. This is working fine, so no real problems.

However, this made me think: why does TableModel have a method to get the class of an entire column (getColumnClass(int col)), and not a method to get the class of a single cell (getCellClass(int row, int col))? It wouldn't make the code in JTable that much harder, and if an entire column needs the same class then the row parameter can just be ignored.

Now the only part that will be a bit harder is TableColumn.setCellRenderer and TableColumn.setCellEditor, but surely that could have been solved as well?

Just my 2 cents on another flaw in the Java API


SCJP 1.4 - SCJP 6 - SCWCD 5 - OCEEJBD 6
How To Ask Questions How To Answer Questions
Brian Cole
Author
Ranch Hand

Joined: Sep 20, 2005
Posts: 862
Originally posted by Rob Prime:

I've done this by creating a custom JTable, and overriding the getCellRenderer(int row, int col) and getCellEditor(int row, int col) methods...

However, this made me think: why does TableModel have a method to get the class of an entire column (getColumnClass(int col)),))?

If you override getCellRenderer() and getCellEditor() then getColumnClass() is irrelevant. It is only used by getCellRenderer() and getCellEditor(). [edit: It seems it is also sometimes used by JTable's Accessibility code, which is frankly a bit odd but probably not relevant here.]

Personally, I think it was a mistake to have getColumnClass() return a Class object, as its common to have two columns with the same class of values but for which you want different renderers/editors. In this case one must either have this method lie about the actual class of one of the columns (which is perfectly ok to do, btw) or use one of the other two methods for assigning renderers/editors.

and not a method to get the class of a single cell (getCellClass(int row, int col))?

I agree that sometimes it would be handy to have this method, but it's usually not too hard to override getCellRenderer()/getCellEditor() whenever one wishes to use multiple renderers/editors within a single column.

earlier in the post:
I want users to edit boolean values using a check box, and numbers using a right-aligned label and text field.

FYI, JTable with automatically provide those renderers/editors for you so long as you return Boolean.class and Number.class (or Integer.class, Double.class, etc.) from you table model's getColumnClass() and you're ok with using those renderers/editors for the entire column. There's no need to create your own renderers/editors for something simple like this. [edit: I realize Mr. Prime is aware of this, but I mention it anyway for others who may be reading as this isn't as widely known as it shoud be.]
[ December 09, 2007: Message edited by: Brian Cole ]

bitguru blog
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19670
    
  18

Originally posted by Brian Cole:
If you override getCellRenderer() and getCellEditor() then getColumnClass() is irrelevant. It is only used by getCellRenderer() and getCellEditor().

I know, the thing is, I just don't want to create a JTable subclass every time I want different renderers / editors for different rows.

Personally, I think it was a mistake to have getColumnClass() return a Class object, as its common to have two columns with the same class of objects but for which you want different renderers/editors. In this case one must either have this method lie about the actual class of one of the columns (which is perfectly ok to do, btw) or use one of the other two methods for assigning renderers/editors.

I agree, I hardly ever use default renderers / editors unless I use them for my custom classes which are used by a single column only. In all other cases, I do it through the column directly.

Well ok, I'm making exceptions for Boolean.class and Integer.class.

FYI, JTable with automatically provide those renderers/editors for you so long as you return Boolean.class and Number.class (or Integer.class, Double.class, etc.) from you table model's getColumnClass() and you're ok with using those renderers/editors for the entire column. There's no need to create your own renderers/editors for something simple like this.

I know, that's what I'm doing with "getDefaultRenderer(cls)". This returns me instances of the package private classes javax.swing.JTable.BooleanRenderer and javax.swing.JTable.NumberRenderer for Boolean.class and Integer.class respectively.

The thing is, some rows need BooleanRenderer while others need NumberRenderer, and others need just javax.swing.table.DefaultTableCellRenderer$UIResource (the default for Object.class). If I need check boxes for the entire column I'm not going to put much effort into it and just specify the model to return Boolean.class.



The weird thing is, they have thought about this whole issue for the "isCellEditable" method, which does take both a row and column number. This is the method I've been ignoring the row parameter for most however.
Brian Cole
Author
Ranch Hand

Joined: Sep 20, 2005
Posts: 862
It seems you responded before I added my perenthetical (brackenthetical?) comment. Sorry about that.

Originally posted by Rob Prime:
I know, the thing is, I just don't want to create a JTable subclass every time I want different renderers / editors for different rows.
Understood, but since you usually have to do other stuff to every table you create [for example: putClientProperty("terminateEditOnFocusLost", Boolean.TRUE)] often you make one named JTable subclass that you use everywhere anyway.

So you just override getCellRenderer() and getCellEditor() in this one class to do something like


Of course you would have to define your AugmentedTableModel interface to have a getColumnClass(row, col) method. It could have other methods too. For example, it can be handy to throw in a getCellBackground(row, col) method and write your custom renders [or override prepareRenderer()] to call it.

QUOTE: Personally, I think it was a mistake to have getColumnClass() return a Class object, as ...

I agree,

The thing that's cute about using Class, though, is how it respects the superclass relationship. So you can define a general renderer/editor for Number.class and then a specific one for Byte.class, and it will use the specific one for Byte columns and the general one for all other Number columns. Or more to the point, you can define one for Object.class that will be used for everything without a more specific assignment. I think I might still prefer a method that could return any Object, though.

[edit: correct munged QUOTE tags]
[ December 09, 2007: Message edited by: Brian Cole ]
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19670
    
  18

Well, I caved, and wrote my own generic classes. I also wrote support in the JTable to set renderers and editors for specific cells, kind of like the way TableColumn can be used to overwrite the default renderer for a column in the existing setup.

It will check a cell specific renderer / editor first, then the TableColumn, then the getCellClass value.
Gaurav Arora
Ranch Hand

Joined: Aug 13, 2007
Posts: 35
Originally posted by Rob Prime:

However, this made me think: why does TableModel have a method to get the class of an entire column (getColumnClass(int col)), and not a method to get the class of a single cell (getCellClass(int row, int col))? It wouldn't make the code in JTable that much harder, and if an entire column needs the same class then the row parameter can just be ignored.

Now the only part that will be a bit harder is TableColumn.setCellRenderer and TableColumn.setCellEditor, but surely that could have been solved as well?

Just my 2 cents on another flaw in the Java API


I can give you a real world example of this. I recently had to build a table which showed 2 string columns, a currency column and a double column with a specific number of decimal places. All of these require that the data be displayed as a string. Automatic sorting on these columns which are not actually strings would return the wrong result. By overriding the getColumnClass() method, I was able to return the correct class (who's compare method was called) so that the sorting was correct.

I doubt that the method has anything to do with the renderers.
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19670
    
  18

Ok, sorting is something that only works well if the entire column has the same class. But sorting hasn't been added until Java 6. Before that time, the column class had little purpose other than making sure the right renderer and editor were used.

But now, with Java 6, you have a valid point. Sorting will produce strange results, possibly even throw errors (ClassCastException), if you don't provide a valid column class.
 
It is sorta covered in the JavaRanch Style Guide.
 
subject: TableModel - why getColumnClass?