Different cell editor in single column - Bug?
Hello all,
In a single column, I wish to have different cell editor. By refering to,
http://groups.google.com.my/group/comp.lang.java.gui/browse_thread/thread/9944682df9c2ca5f/2adbd3e46e19e521?lnk=st&q=Tablemodel+cell+class+type&rnum=7#2adbd3e46e19e521
http://forum.java.sun.com/thread.jspa?forumID=257&threadID=211873
I manage to do the following implementation :
package org.yccheok.jstock.gui;
import javax.swing.event.*;
import javax.swing.table.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import org.yccheok.jstock.engine.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* @author yccheok
*/
publicclass IndicatorJTableextends JTable{
/** Creates a new instance of IndicatorJTable */
public IndicatorJTable(){
}
public TableCellRenderer getCellRenderer(int row,int column){
finalint modelRow = this.convertRowIndexToModel(row);
finalint modelcolumn = this.convertColumnIndexToModel(column);
return this.getDefaultRenderer(getModel().getValueAt(modelRow, modelcolumn).getClass());
}
@Override
public TableCellEditor getCellEditor(int row,int column){
finalint modelRow = this.convertRowIndexToModel(row);
finalint modelcolumn = this.convertColumnIndexToModel(column);
return this.getDefaultEditor(getModel().getValueAt(modelRow, modelcolumn).getClass());
}
privatestaticfinal Log log = LogFactory.getLog(IndicatorJTable.class);
}
with my own custom Table model.
When the JTable first display, it look fine. The cell which is being specific as Double.class type, the display is correct :
Double, Float ?same as Number, but the object-to-text translation is performed by a NumberFormat instance (using the default number format for the current locale).
When I double click on the cell specific as Double.class, and try to enter non-numerical value and press enter, there suppose to be a red border around, and doesn't allow my newly enter non-double value to be written to the table. However, that is not the case, I am able to write non-double value into the cell which is specific as Double.class type.
I realize that my overridden getCellRenderer and getCellEditor in JTable, able to return me a correct Double type renderer and cell editor. However, the getColumnClass in table model, is unable to return class specific to a particular cell. When editing process start, JTable will also try to query information from Table Model getColumnClass, which in turn will return String (Of course, I just cann't make the particular column to return class Double. There are other cell which is not Double)
public Class getColumnClass(int c){
return super.getColumnClass(c);
}
This make the editing process behave incorrect. Please refer to the following bug database.
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4801881
May I know does any of you figure out a workaround on this?
Thank you very much!
cheok
[4700 byte] By [
KwangHooia] at [2007-11-27 5:56:10]

# 4
OK. Let's take the following examples. In my JTable, all the cells should be Object, except one of it (at col=1, row=1) should be Double.
When I mean a cell should be Double, it means the following :
1. I key in a non double value in the particular cell and try to press enter to update my table model. It is not allowed and will display red border around the cell as follow:
http://yancheng.cheok.googlepages.com/invalid-double-when-press-enter.JPG
2. I key in a double value in the particular cell and try to press enter. Everything is ok as follow:
http://yancheng.cheok.googlepages.com/double-when-press-enter.JPG
I try two methods to achieve my objective. However, neither one does work. When I key in non double value and press enter, the following exception will be thrown :
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Cannot format given Object as a Number
at java.text.DecimalFormat.format(DecimalFormat.java:487)
at java.text.Format.format(Format.java:140)
First method
package javaapplication7;
import javax.swing.*;
import javax.swing.table.*;
/**
*
* @author YC Cheok
*/
public class MyTable extends JTable {
/** Creates a new instance of MyTable */
public MyTable() {
}
public TableCellEditor getCellEditor(int row, int column) {
if(row == 1 && column == 1) {
return this.getDefaultEditor(Double.class);
}
return super.getCellEditor(row, column);
}
public TableCellRenderer getCellRenderer(int row, int column) {
if(row == 1 && column == 1) {
return this.getDefaultRenderer(Double.class);
}
return super.getCellRenderer(row, column);
}
}
package javaapplication7;
/**
*
* @author YC Cheok
*/
public class NewJFrame extends javax.swing.JFrame {
/** Creates new form NewJFrame */
public NewJFrame() {
initComponents();
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc=" Generated Code ">
private void initComponents() {
jScrollPane1 = new javax.swing.JScrollPane();
jTable1 = new MyTable();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jTable1.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
{null, null, null, null},
{null, null, null, null},
{null, null, null, null},
{null, null, null, null}
},
new String [] {
"Title 1", "Title 2", "Title 3", "Title 4"
}
));
jScrollPane1.setViewportView(jTable1);
getContentPane().add(jScrollPane1, java.awt.BorderLayout.CENTER);
pack();
}// </editor-fold>
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new NewJFrame().setVisible(true);
}
});
}
// Variables declaration - do not modify
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTable jTable1;
// End of variables declaration
}
Second Method
-
package javaapplication7;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.table.DefaultTableCellRenderer;
/**
*
* @author YC Cheok
*/
public class MyTable extends JTable {
/** Creates a new instance of MyTable */
public MyTable() {
final TableCellEditor oldEditor = this.getDefaultEditor(Object.class);
final TableCellRenderer oldRenderer = this.getDefaultRenderer(Object.class);
javax.swing.DefaultCellEditor editor = new javax.swing.DefaultCellEditor(new JTextField()) {
public java.awt.Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
if(row == 1 && column == 1) {
TableCellEditor e = table.getDefaultEditor(Double.class);
return e.getTableCellEditorComponent(table, value, isSelected, row, column);
}
return oldEditor.getTableCellEditorComponent(table, value, isSelected, row, column);
}
};
javax.swing.table.DefaultTableCellRenderer renderer = new javax.swing.table.DefaultTableCellRenderer() {
public java.awt.Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if(row == 1 && column == 1) {
TableCellRenderer e = table.getDefaultRenderer(Double.class);
return e.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
return oldRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
};
this.setDefaultEditor(Object.class, editor);
this.setDefaultRenderer(Object.class, renderer);
}
}
Message was edited by:
KwangHooi
# 6
Yes. I try to overriden the JTable getColumnClass couple with getSelectedRow. It "almost" work. But, still have flaw.
The flaw can be easily reproduced using the following senario:
1. Double click on the cell of the first row, first col, make it editable (You can see a cursor blinking)
2. Do not press enter.
3. Perform a single click on the cell of the second row, second col (The one we intend to make it as Double)
Once again, we can see the following error :
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Cannot format given Object as a Number
at java.text.DecimalFormat.format(DecimalFormat.java:487)
at java.text.Format.format(Format.java:140)
at javax.swing.JTable$DoubleRenderer.setValue(JTable.java:5301)
...
package javaapplication7;
import javax.swing.*;
import javax.swing.table.*;
/**
*
* @author YC Cheok
*/
public class MyTable extends JTable {
/** Creates a new instance of MyTable */
public MyTable() {
}
public TableCellEditor getCellEditor(int row, int column) {
if(row == 1 && column == 1) {
return this.getDefaultEditor(Double.class);
}
return super.getCellEditor(row, column);
}
public Class getColumnClass(int column) {
// if(this.getSelectedRow() == 1 && this.getSelectedColumn() == 1) {
if(this.getSelectedRow() == 1 && column == 1) {
System.out.println("hacking");
return Double.class;
}
return this.getModel().getColumnClass(this.convertColumnIndexToModel(column));
}
public TableCellRenderer getCellRenderer(int row, int column) {
if(row == 1 && column == 1) {
return this.getDefaultRenderer(Double.class);
}
return super.getCellRenderer(row, column);
}
}
# 7
It appears the the getSelectedRow() doesn't always return the expected value. So here's a work around that seems to work:
JTable table = new JTable(data, columnNames)
{
private int renderingRow;
// Returning the Class of each column will allow different
// renderers and editors to be used based on Class
public Class getColumnClass(int column)
{
if(column == 2 && renderingRow == 0)
{
return Double.class;
}
return super.getColumnClass( column );
}
public TableCellEditor getCellEditor(int row, int column)
{
renderingRow = row;
if(row == 0 && column == 2)
{
return this.getDefaultEditor(Double.class);
}
return super.getCellEditor(row, column);
}
public TableCellRenderer getCellRenderer(int row, int column)
{
renderingRow = row;
if(row == 0 && column == 2)
{
return this.getDefaultRenderer(Double.class);
}
return super.getCellRenderer(row, column);
}
};