ArrayIndexOutOfBoundsException with DefaultTableModel

A

Aloys Oberthuer

Hi everyone,

i have got a problem with JTable. I created a small panel which includes
a JTable (in a JScrollPane) with a DefaultTableModel as model and 3
Buttons to add and remove rows and to clear the whole table. The problem
is after deleting a row, and clicking on the row which is now the last,
I always get ArrayIndexOutOfBoundsExceptions (see the StackTrace below).


I create the Table as follows:

DefaultTableModel dftM = new DefaultTableModel(2, 1);

tbl = new JTable();
tbl.setAutoCreateColumnsFromModel(false); // create my own ColumnModel
tbl.setShowHorizontalLines(true);
tbl.setShowVerticalLines(true);
tbl.setModel(dftM);

// build table column(s), custom renderer(s) and editor(s), etc.
// see below



All I do is invoke the following method in my code on a "delete row"
button action:
private void rowDelete() {
int row = tbl.getSelectedRow();
((DefaultTableModel)tbl.getModel()).removeRow(row);
}

The table does redraw with one row less, but as I try to select another
row, I always get the Exception. The same thing happens after clearing
the Table which I do as follows:

public void tableDataReset() {
DefaultTableModel dftM = new DefaultTableModel(1, 1);
tbl.setModel(dftM);
}

I.e. I do create a new DefaultTableModel and get the same problem. It
looks like changing the model (deleting a row, replacing the model) did
not really affect the model or leaves it in an inconsistent state, which
I find especially strange with replacing the model. Sometimes after
deleting a row and adding an empty new one the 'empty' row contains the
content of the deleted row, which points in the same direction for me.
I tried using tbl.tableChanged(new TableModelEvent(dftM)) but that did
not help either. I have included the whole (executable) class and the
stacktrace - any help would be greatly appreciated. I use J2SE 1.4.2 on
a WindowsXp Sp1 System.

Aloys




CODE:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;


public class SimpleTablePanel extends JPanel {

private JTable tbl;
private JScrollPane jsp;
private JPanel pnlTable, pnlButtons;
private JButton btAdd, btRemove, btClear;


public SimpleTablePanel(String title, int tWidth, int tHeight) {
super();

createPanelTable(title, tWidth, tHeight);
createPanelButtons();

this.setLayout(new BorderLayout());
this.add(pnlTable, BorderLayout.CENTER);
this.add(pnlButtons, BorderLayout.SOUTH);
}


private void createPanelButtons() {
// create Button Panel
pnlButtons = new JPanel(new FlowLayout(FlowLayout.LEFT));

btAdd = new JButton("Add");
btAdd.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent aE){
SimpleTablePanel.this.rowAdd();
}
});
btRemove = new JButton("Del");
btRemove.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent aE) {
SimpleTablePanel.this.rowDelete();
}
});
btClear = new JButton("Clr");
btClear.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent aE) {
SimpleTablePanel.this.tableDataReset();
}
});

pnlButtons.add(btAdd);
pnlButtons.add(btRemove);
pnlButtons.add(btClear);
}

private void createPanelTable(String title, int tWidth, int tHeight) {
pnlTable = new JPanel();

DefaultTableModel dftM = new DefaultTableModel(2, 1);

// next create Table
tbl = new JTable();
tbl.setAutoCreateColumnsFromModel(false); // create my own ColumnModel
tbl.setShowHorizontalLines(true);
tbl.setShowVerticalLines(true);
tbl.setModel(dftM);

// and build table column
TableCellRenderer renderer = new DefaultTableCellRenderer();

JTextField txt = new JTextField(20);
txt.setBorder(null);
TableCellEditor editor = new DefaultCellEditor(txt);

((DefaultCellEditor)editor).setClickCountToStart(1);

TableColumn column = new TableColumn(0, tWidth, renderer, editor);
column.setHeaderValue(title);
tbl.addColumn(column);

// and put it in a JScrollPane
jsp = new JScrollPane(tbl);
jsp.setPreferredSize(new Dimension(tWidth,tHeight));
pnlTable.add(jsp, BorderLayout.CENTER);
}

private void rowAdd() {
int row = tbl.getSelectedRow();
((DefaultTableModel)tbl.getModel()).insertRow(row+1, new String[] {""});
}
private void rowDelete() {
int row = tbl.getSelectedRow();
((DefaultTableModel)tbl.getModel()).removeRow(row);
}
public void tableDataReset() {
DefaultTableModel dftM = new DefaultTableModel(1, 1);
tbl.setModel(dftM);
}

public static void main(String[] args) {

JFrame jf = new JFrame("Testframe");
jf.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {System.exit(0);}
});

SimpleTablePanel jsF = new SimpleTablePanel("Table", 400, 300);
jf.getContentPane().add(jsF, BorderLayout.CENTER);
jf.pack();
jf.setVisible(true);
}
}


STACKTRACE:
java.lang.ArrayIndexOutOfBoundsException: 2 >= 2
at java.util.Vector.elementAt(Vector.java:431)
atjavax.swing.table.DefaultTableModel.setValueAt(DefaultTableModel.java:633)
at javax.swing.JTable.setValueAt(JTable.java:1794)
at javax.swing.JTable.editingStopped(JTable.java:3167)
atjavax.swing.AbstractCellEditor.fireEditingStopped(AbstractCellEditor.java:124)
at
javax.swing.DefaultCellEditor$EditorDelegate.stopCellEditing(DefaultCellEditor.java:329)
at javax.swing.DefaultCellEditor.stopCellEditing(DefaultCellEditor.java:214)
at javax.swing.JTable.editCellAt(JTable.java:2510)
at
javax.swing.plaf.basic.BasicTableUI$MouseInputHandler.adjustFocusAndSelection(BasicTableUI.java:510)
at
javax.swing.plaf.basic.BasicTableUI$MouseInputHandler.mousePressed(BasicTableUI.java:494)
at java.awt.AWTEventMulticaster.mousePressed(AWTEventMulticaster.java:222)
at java.awt.Component.processMouseEvent(Component.java:5097)
at java.awt.Component.processEvent(Component.java:4897)
at java.awt.Container.processEvent(Container.java:1569)
at java.awt.Component.dispatchEventImpl(Component.java:3615)
at java.awt.Container.dispatchEventImpl(Container.java:1627)
at java.awt.Component.dispatchEvent(Component.java:3477)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:3483)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3195)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3128)
at java.awt.Container.dispatchEventImpl(Container.java:1613)
at java.awt.Window.dispatchEventImpl(Window.java:1606)
at java.awt.Component.dispatchEvent(Component.java:3477)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:456)
at
java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:201)
at
java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:145)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:137)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:100)
 
M

mromarkhan

javadoc reades

that get selectedrow returns
Returns the index of the first selected row, -1 if no row is selected.
so test for the -1 condition upon deleted.

However, the problem may ly in the test case

D selecting is not the same as editng
Editing returns -1 also
to test it out for yourself
try clicking on the row the pressing the up arrow
then delete without modifying your code
it should work.

Conclusion: need to test for no selection -1 = getSelecedRow
upon delete and remember editing not same as selecting

I may be wrong, I may; I may be right.
Have a good day.
peace be unto you.
 
A

A.Buschmann

Hi Aloys,

the stacktrace says, the problem comes from the "setValueAt()"-Method- but
you are deleting,
not editig.
Or are you??
With the DefaultTableModel, the "isCellEditable(int, int)"-Method returns
simply "true".
When you select a row with your mouse, it is open for edit. (Select row 2 of
2)
By clicking the delete-button, the edited-cell is not properly saved.
The row where the edited cell is in becomes deleted.
Now there is no row selected, but the JTable remembers that the first column
in the second
row was in edit-mode.
When clicking on row one, the JTable sees: You are leaving the edited field.
So JTable tries to save your edit. (In row 2 which does no longer exist)

The exception will not occur, if you use the arrow-keys in the JTable:
1. Click in row one
2. Move with the down-key to row two
The blinking cursor will vanish and the entire row becomes an
highlighted border
3. Delete row two. (Click on "Del"-Button)
4. Click on row one.
No Exception occurs.

The DefaultTableModel is o.k., but the JTable does not realize, that the
edited row has
been removed.

If you don't need to change the cells, extend DefaultTableModel and
overwrite
simply the "isCellEditable(int, int)"-Method:

public boolean isCellEditable(int row, int column) {
return false;
}

Else,
you have to call the "stopCellEditing()"-Method from DefaultCellEditor-Class
before you start with
deleting the row.
To get the CellEditor, the JTable has the "getCellEditor()"-Method.
As far as I know, the "getCellEditor()"-Method returns "null", when
no cell is edited.
Your rowDelete() should look like this:
private void rowDelete() {
int row = tbl.getSelectedRow();
if (row > -1) {
DefaultCellEditor dce = (DefaultCellEditor)tbl.getCellEditor();
if (dce != null) dce.stopCellEditing();
((DefaultTableModel)tbl.getModel()).removeRow(row);
}
}

hope this helps.

Alex
 
A

Aloys Oberthuer

A.Buschmann said:
Hi Aloys,

the stacktrace says, the problem comes from the "setValueAt()"-Method- but
you are deleting,
not editig.
Or are you??
With the DefaultTableModel, the "isCellEditable(int, int)"-Method returns
simply "true".
When you select a row with your mouse, it is open for edit. (Select row 2 of
2)
By clicking the delete-button, the edited-cell is not properly saved.
The row where the edited cell is in becomes deleted.
Now there is no row selected, but the JTable remembers that the first column
in the second
row was in edit-mode.
When clicking on row one, the JTable sees: You are leaving the edited field.
So JTable tries to save your edit. (In row 2 which does no longer exist)

The exception will not occur, if you use the arrow-keys in the JTable:
1. Click in row one
2. Move with the down-key to row two
The blinking cursor will vanish and the entire row becomes an
highlighted border
3. Delete row two. (Click on "Del"-Button)
4. Click on row one.
No Exception occurs.

The DefaultTableModel is o.k., but the JTable does not realize, that the
edited row has
been removed.

If you don't need to change the cells, extend DefaultTableModel and
overwrite
simply the "isCellEditable(int, int)"-Method:

public boolean isCellEditable(int row, int column) {
return false;
}

Else,
you have to call the "stopCellEditing()"-Method from DefaultCellEditor-Class
before you start with
deleting the row.
To get the CellEditor, the JTable has the "getCellEditor()"-Method.
As far as I know, the "getCellEditor()"-Method returns "null", when
no cell is edited.
Your rowDelete() should look like this:
private void rowDelete() {
int row = tbl.getSelectedRow();
if (row > -1) {
DefaultCellEditor dce = (DefaultCellEditor)tbl.getCellEditor();
if (dce != null) dce.stopCellEditing();
((DefaultTableModel)tbl.getModel()).removeRow(row);
}
}

hope this helps.

Alex


Thanks a lot, invoking the stopCellEditing() method does indeed solve the problem. Now my rowDelete() method does look
like this, which actually does pretty much the same (I'd say your's is even a little more elegant) :
private void rowDelete() {
if(tbl.getCellEditor() != null)
((DefaultCellEditor)tbl.getCellEditor()).stopCellEditing();

int row = tbl.getSelectedRow();
if((row >= 0) && (tbl.getRowCount()>1)) {
DefaultTableModel tm = (DefaultTableModel)tbl.getModel();
tm.removeRow(row);
tbl.repaint();
}
}

Searching the web I got the idea that quite a lot had the same problem, wonder why some things have to be so complicated.
Thanks, Aloys
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top