1

Only the last Vector instance of rowData in loadDefaultTableModel is actually being displayed in the actual JTable. Why is that?

Other vectors, with other data, are being instantiated but are not getting added to the model -- apparently. At least they are not being displayed.

How do I ensure that the model contains the desired data? The desired data is output by the Logger, and is in Vector form already. I do not understand the inconsistency.

package net.bounceme.dur.nntp.swing;

import java.util.Map;
import java.util.Map.Entry;
import java.util.Vector;
import java.util.logging.Logger;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import net.bounceme.dur.nntp.gnu.PageMetaData;
import net.bounceme.dur.nntp.gnu.Page;
import net.bounceme.dur.nntp.gnu.Usenet;

public class ArticlesTable extends JScrollPane {

    private static final Logger LOG = Logger.getLogger(ArticlesTable.class.getName());
    private static final long serialVersionUID = 1L;
    private JTable jTable = new JTable();
    private DefaultTableModel defaultTableModel = new DefaultTableModel();
    private Page page;
    private Usenet usenetConnection = Usenet.INSTANCE;

    public ArticlesTable() throws Exception {
        page = new Page();//eh, throws Exception -- fix later
        LOG.fine(page.toString());  //empty Page
        initComponents();
    }

    @SuppressWarnings("unchecked")
    private void initComponents() {
        defaultTableModel = new DefaultTableModel(new Object[][]{
                    {"some", "text"}, {"any", "text"}, {"even", "more"},
                    {"text", "strings"}, {"and", "other"}, {"text", "values"}},
                new Object[]{"Column 1", "Column 2"});
        jTable.setModel(defaultTableModel);
        jTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        jTable.addMouseListener(new java.awt.event.MouseAdapter() {

            public void mouseReleased(java.awt.event.MouseEvent evt) {
                itemSelected();
            }
        });
        jTable.addKeyListener(new java.awt.event.KeyAdapter() {

            public void keyReleased(java.awt.event.KeyEvent evt) {
                itemSelected();
            }
        });

        setSize(2000, 2000);  //not sure these lines are quite right, but ok for now
        setViewportView(jTable);
        jTable.setVisible(true);
        setVisible(true);
    }

    private void itemSelected() {
        int row = jTable.getSelectedRow();
        int column = 0;
        Object selectedObject = jTable.getValueAt(row, column);
        int i = (int) selectedObject;
        Map<Integer, Message> messages = page.getMessages();
        Message message = messages.get(i);
        firePropertyChange("message", null, message);
    }

    public final void nextPage() throws Exception {
        LOG.fine("trying to get next page..." + page);
        PageMetaData pageMetaData = page.getPageMetaData();
        pageMetaData.next();  //only changes meta data
        page = usenetConnection.getPage(pageMetaData); //fetches actual data
        LOG.fine("was the page advanced?" + page);  //yes, logs show this
        loadDefaultTableModel();
        LOG.fine(page.toString());
    }

    @SuppressWarnings("unchecked")
    private void loadDefaultTableModel() throws MessagingException {
        LOG.fine("trying to load...");
        defaultTableModel = new DefaultTableModel();
        defaultTableModel.addColumn("index");
        defaultTableModel.addColumn("message subject");
        Vector rowData = new Vector(); //vector?! nothing better?
        Message message = null;
        Map<Integer, Message> messages = page.getMessages(); //sort this
        int key = 0;
        LOG.info("trying to traverse..." + messages.size()); //page meta data determines the size
        for (Entry<Integer, Message> entry : messages.entrySet()) {
            rowData.clear();
            key = entry.getKey();
            message = messages.get(key);
            rowData.add(key);
            rowData.add(message.getSubject());
            LOG.info("vector\t" + key + "\t" + message.getSubject());
            //the vector logs the data which **should** get added
            //to the table model
            defaultTableModel.addRow(rowData);  //adds same row over and over
        }
        jTable.setModel(defaultTableModel);
    }

    private Message getMessage(int i) {
        LOG.fine("trying to get\t\t" + i);
        Map<Integer, Message> messages = page.getMessages();
        Message m = messages.get(i);
        return m;
    }
}

image: enter image description here

and some of the output:

INFO: trying to traverse...5
Apr 15, 2013 2:05:13 AM net.bounceme.dur.nntp.swing.ArticlesTable loadDefaultTableModel
INFO: vector    25  Re: Netbeans question?
Apr 15, 2013 2:05:13 AM net.bounceme.dur.nntp.swing.ArticlesTable loadDefaultTableModel
INFO: vector    27  Re: Helpppp plzzz
Apr 15, 2013 2:05:13 AM net.bounceme.dur.nntp.swing.ArticlesTable loadDefaultTableModel
INFO: vector    26  Re: writing Unix files on Windows
Apr 15, 2013 2:05:13 AM net.bounceme.dur.nntp.swing.ArticlesTable loadDefaultTableModel
INFO: vector    29  JTextPane.modelToView() exception
Apr 15, 2013 2:05:13 AM net.bounceme.dur.nntp.swing.ArticlesTable loadDefaultTableModel
INFO: vector    28  Java 6 update 39 to 41 balloon

As can be seen, each vector instance has the correct data. However, only the first instance seems to end up in the GUI output. Why?

Is it not getting added to the model correctly? Is the model not updating the table correctly?

Why is the data in the model, as seen in the GUI, mismatched from the logged output?

Thufir
  • 8,216
  • 28
  • 125
  • 273
  • instead of rowData.clear() I changed it to rowData = new Vector(); and now get the desired output. However, 1.) it's inefficient, and, 2.) I don't see difference it makes. Either way the Vector is emptied. It must be a scope thing -- somehow the model is keeping track of the Vector and as the Vector changes it changes those rows. Hmm, a bit too dynamic for my tastes. I don't get it – Thufir Apr 15 '13 at 09:58
  • The DefaultTableModel uses a Vector of Vectors to store the data. That is why you need to keep creating a new Vector. This is a requirement for any TableModel. You need a data structure to keep the data of every row. How you do this is up to you. The DefaultTableModel chooses to use a Vector to represent the data for a given row. If you think this is inefficient then create a custom TableModel. – camickr Apr 15 '13 at 15:48
  • I mean that it's inefficient to instantiate objects in a loop, as a general rule. Generally the mutable object is declared outside the loop and modified within the loop. Otherwise you could potentially end up with many, many objects waiting for garbage collection. I think it's a topic in Effective Java by Joshua Bloch -- but I could be mistaken. – Thufir Apr 15 '13 at 19:12
  • If you have 100 rows of data then you need 100 objects to hold that data. You can't get away from creating the object inside the loop. When you use the addRow(...) method the actual object is added to the model (the data is not copied). Maybe that is your confusion. If you use the addRow(...) method that takes an Array as a parameter, then the model with create a Vector and copy the data from the Array to the vector. – camickr Apr 15 '13 at 19:46
  • @camickr hmm, when you say that the actual object is added to the model and that the data is not copied, is that "normal"? I'm going to have to look at http://stackoverflow.com/questions/40480/is-java-pass-by-reference and related docs carefully. But I'm still confused. – Thufir Apr 15 '13 at 21:45

2 Answers2

2

How do I ensure that the model contains the desired data?

DefaultTableModel is a basic implementation of TableModel that uses Vector internally. Vector is a synchronized legacy class that uses System.arraycopy() extensively.

As you may need finer control over the internal data structure, AbstractTableModel provides the required event plumbing and lets you manage the data structure directly.

Creating a Table Model compares the two approaches, and this example that illustrates Map may suggest the benefit.

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • The behaviour of the Vector seems different from, say, a List or array. This is because of the synchronization? so that each row of the model is synchronized with the Vector? That's kind of interesting, seems like a "cheap and easy" way updating the table data on the fly. But odd -- at least to me. – Thufir Apr 15 '13 at 19:10
  • The behaviour of the Vector is the same as a List or Array. All updates to the model should be done on the EDT. The Vector does do synchronization, but it is unnecessary. – camickr Apr 15 '13 at 19:49
1

Your mistake is here: for (Entry entry : messages.entrySet()) { rowData.clear(); <-- you are always deleting all the row data, so only the last row data is present.

redc0w
  • 101
  • 4
  • However, I **want** to delete the row data -- otherwise each row would be an accumulation of all the previous rows. A new Vector seems to work. – Thufir Apr 15 '13 at 10:02