Question

I have an ArrayList of type modules, each module has an arraylist of Assignments. I have written the following TableModel but when the something is selected in the table I have a problem and it causes my ArrayLists to be out of bounds. Here is my table model:

public class AssignmentsTableModel extends AbstractTableModel {
private ArrayList<Module> modules;
private static final String[] COLUMN_NAMES = {"Module Identifier", "Module Name", "Assignment Title", "Author", "Date Set", "Date Due", "Weighting"};

private int moduleID;
private int assignmentID;
private Module module;
private int totalNumberAssignments;

public AssignmentsTableModel(ArrayList<Module> modules) {
    moduleID = 0;
    assignmentID = 0;
    this.modules = modules;
    module = modules.get(moduleID);
    totalNumberAssignments = getRowCount();
}

public AssignmentsTableModel(Module module) {
    modules = new ArrayList<Module>();
    modules.add(module);
}

@Override
public int getRowCount() {
    int rowCount = 0;
    for(Module mod : modules){
        rowCount += mod.getAssignments().size();
    }
    return rowCount;
}

@Override
public int getColumnCount() {
    return COLUMN_NAMES.length;  //To change body of implemented methods use File | Settings | File Templates.
}

@Override
public Object getValueAt(int row, int column) {
    if(column%getColumnCount() == 0 && getRowCount() <= totalNumberAssignments){
        if(isAtLastAssignment(row, moduleID)){
            assignmentID = 0;
            moduleID += 1;
        }
    }

    module = modules.get(moduleID);
    ArrayList<Assignment> assignments = module.getAssignments();

    Assignment assignment = assignments.get(assignmentID);

    switch (column){
        case 0: return module.getIdentifier();
        case 1: return module.getTitle();
        case 2: return assignment.getTitle();
        case 3: return assignment.getAuthor();
        case 4: return assignment.getSet();
        case 5: return assignment.getDue();
        case 6: return assignment.getWeighting();
        default: return null;
    }


}

public String getColumnName(int columnIndex) {
    return COLUMN_NAMES[columnIndex];
}

public boolean isAtLastAssignment(int row, int moduleID){
    boolean atLast = false;
    ArrayList<Assignment> assignments = modules.get(moduleID).getAssignments();
    if(moduleID == 0){
        if(row == assignments.size()){
            atLast = true;
        }
    } else {
        int assignmentsSize = assignments.size() + getCurrentRowNumber(moduleID);
        if(row == assignmentsSize){
            atLast = true;
        }
    }

    return atLast;
}

public int getCurrentRowNumber(int moduleID){
    int rowNumber = 0;

    for(int i = 0; i < moduleID; i++){
        rowNumber = modules.get(i).getAssignments().size()-1;
    }

    return rowNumber;
}
}

As you can see I store the moduleID as global variables which is fine when its first started, but once something is selected it uses the last values. What else can I do to stop this from happening?

Was it helpful?

Solution

The TableModel method getValueAt() is called by the table renderer each time it wants to get the value of the cell so that it can render it. It's not (directly or reliably) related to you clicking on the table row and having the table mark the row as selected.

In other words, you don't want to be mutating the state of your table model as a result of calling the getValueAt() method.

It would be easiest if your getValueAt() method use the row as the index into your array. However, it looks like you have a variable number of rows for each module. In that case, if you can't algorithmically generate the right index, you could "flatten" the data once in the constructor, and simplify the look-up code.

That is, your table model contains:

private List<RowData> rowData

and an inner class which contains the row data:

private static class RowData
{
  private Moddule;
  private Assignment assignment;

  ...
}

And in your constructor:

public AssignmentsTableModel(List<Module> modules)
{
  this.rowData = new ArrayList<Module>();

  for (Module module : modules)
  {
    for (Assignment assignment : module.getAssignments())
    {
      rowData.add(new RowData(module, assignment));
    }
  }
}

This simplifies your getValueAt() method:

@Override
public Object getValueAt(int row, int column) {
    final RowData row = this.rowData.get(row);

    switch (column){
        case 0: return row.getModule().getIdentifier();
        case 1: return row.getModule().getTitle();
        case 2: return row.getAssignment().getTitle();
        case 3: return row.getAssignment().Author();
        case 4: return row.getAssignment().getSet();
        case 5: return row.getAssignment().getDue();
        case 6: return row.getAssignment().getWeighting();
        default: return null;
    }
}

OTHER TIPS

  • selection basically hasn't something to do with TableModel, maybe there another issue(s) in rest of code or methods that you not posted here,

  • for basic success I miss there another methods for the AbstractTableModel required for add / modify / remove data, row or column from the Model to the View (and vice versa)

  • use DefaultTableModel, maybe there no reason use AbstractTableModel

  • you can compare required methods for basic AbstractTableModel, use Object[][] or Vector<Vector<Object>> implemented directly in the API rather another Array's or Hash's types,

  • please read turorial How JTable works

EDIT

@Dean wrote:

that I can't see another way around but when an update in the table happens

thats reasons why setXxxMethods (with fireXxxXxx methods inside) exists there, Oracle tutorial about JTable mentions the more times about that

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top