Search code examples
javaswingjtablenullpointerexceptionabstracttablemodel

NullPointerException in custom TableModel


I wrote a custom table model for a JTable:

class MessageTableModel{

    private static Set<Message> messages = Collections.synchronizedSet(new TreeSet<Message>());

    .
    .
    .

    public void setMessages(List<Message> newMessages) {
        Collections.sort(newMessages);
        Iterator<Message> it = messages.iterator();
        while (it.hasNext()) {
            Message mess = it.next();
            if (!newMessages.contains(mess)) {
                it.remove();
                this.fireTableDataChanged();
            }
        }
        for (Message message : newMessages)
            if (message.isOrderStatusMessage())
                if (!messages.contains(message)) {
                    addMessage(message);
                }
        this.fireTableDataChanged();
    }

    public Message getMessageAtRow(int row){
        return (Message) messages.toArray()[row];
    }
}

The problem is that there's a thread that updates the table values, calling setMessages() method perodically. If I try to get a row during this update:

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

in this line:

return (Message) messages.toArray()[row];

There's a way to make the method getMessageAtRow() waits for the modifications to be done, or another solution for this problem?


Solution

  • Swing is single threaded. You can't modify the model on a thread outside of the event thread. The easiest way to fix this would be:

    public void setMessages(List<Message> newMessages) {
        SwingUtilities.invokeLater(new Runnable() {
          public void run() {
                Collections.sort(newMessages);
            Iterator<Message> it = messages.iterator();
            while (it.hasNext()) {
                Message mess = it.next();
                if (!newMessages.contains(mess)) {
                    it.remove();
                    this.fireTableDataChanged();
                }
            }
            for (Message message : newMessages)
                if (message.isOrderStatusMessage())
                    if (!messages.contains(message)) {
                        addMessage(message);
                    }
            this.fireTableDataChanged();
        }
      )};
    }
    

    Also,fireTableDateChanged() should also only be called on the event thread.