1

I have a simple JFrame and a JTable on the frame. Users can supply data into the rows of the table. One of the requirements is that the new data either can be saved or discarded after closing the frame. I thought that the easiest way to accomplish this behaviour is the following:

  1. override the setVisible() method of the frame and clone the DefaultTableModel's data vector.

  2. add a WindowListener to the frame and react for the WindowClosing events. This way, the window listener can decide whether the model should be reset to the model that was saved before.

Here is the relevant code snippet :

@Override
public void setVisible(boolean b) {
  //save the original models only if setVisible invoked with true (do not save the model when hiding the frame)
  if (b) {
    Vector cloned = (Vector) userTableModel.getDataVector().clone();
    Vector headerNames = new Vector();
    originalModel = new CustomTableModel(cloned, headerNames);
  }
  super.setVisible(b);
}

Actually, something weird is happening. After I clone the data vector, the table can't be rendered and this leads to the following exception :

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 0 >= 0
    at java.util.Vector.elementAt(Vector.java:470)
    at javax.swing.table.DefaultTableModel.getValueAt(DefaultTableModel.java:650)
    at asc.model.CustomTableModel.getValueAt(CustomTableModel.java:74)
    at javax.swing.JTable.getValueAt(JTable.java:2720)

The table is not in connection to the originalModel in any way. It is a surprise for me because, in theory, the cloning shouldn't affect the table's model. The intention of originalModel is to hold a reference to the newly created copy of the table model. After I commented out the creation of the originalModel, everything worked fine.

Another interesting thing is when I added an empty String to the headerNames vector, the table renderer throws almost the same ArrayIndexOutOfBounds exception, but with this ending:

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 1 >= 1

In that case, the first column of the first row rendered perfectly.

My CustomTableModel is extended from DefaultTableModel, and it does not use any special method. The constructor simply passes the data vector and the header vector to the superclass.

I hope someone could help to resolve the problem. Thanks in advance.

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
Szilárd Németh
  • 359
  • 4
  • 18

3 Answers3

1

I think that you have a mistake in Vector cloned, because you tried to returns plain Vector but JTable is based on Vector<Vector> in your case

mKorbel
  • 109,525
  • 20
  • 134
  • 319
1

The length of the headerNames vector has to match the number of columns you've got. It's confusing because the Vector is translated into an Array of header names inside the CustomTableModel class, which is why it's showing up as an AIOOBE.

So, first pass it looks in the headerNames vector/array for the first column (column at index 0) and fails, sending the 0 >= 0 version of the error. Once you add a column name string (empty though it may be) it successfully renders the entire first column, then goes to look for the second column (column at index 1). It fails, since you don't have a second element in the headerNames vector, and gives the 1 >= 1 version of the error.

Make sure the headerNames vector has the right number of values (matching the number of columns in the data vector) and you should be good to go.

Note - the clone() method is making a SHALLOW copy of the data, so changes the user makes to the data in the cells will still change the original objects. This article may help with that: http://javatechniques.com/blog/faster-deep-copies-of-java-objects/.

Benjamin Cox
  • 6,090
  • 21
  • 19
1

An implementation of AbstractTableModel will give you direct control over your data structure(s). This example illustrates Map<String, String>, but the choice is arbitrary.

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045