5

I have created a simple GUI which includes a JTable. This table may be saved & loaded via the appropriate Object Stream.

At this point the Save function is working as intended, and I can see the table object is stored in a file when looking in the save directory.

However, when I try to load the table from the file, the GUI never displays the loaded table. The actionlistener function is called, as I have a system.out "Data loaded", but the table never displays updated data.

I have tried to call repaint(), but to no avail. To anyone who can shed some light on what I may be doing wrong, I would be most grateful.

A look at some code

import javax.swing.*;
import java.awt.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.imageio.*;
import java.io.*;
import java.awt.image.BufferedImage;
import java.util.*;
import javax.swing.table.*;
import java.awt.event.*;


public class Save3 extends JFrame implements ActionListener{

public int PADDING = 10;

public JMenuItem menuNew;
public JMenuItem menuOpen;
public JMenuItem menuSave;
public JMenuItem menuExit;

public JPanel       container;

public DefaultTableModel    model;
public JScrollPane          scrollPane;
public JTable               table;

public FileInputStream      fis;
public ObjectInputStream    in;
public FileOutputStream     fos;
public ObjectOutputStream   out;
public String               filename;


public Save3(){
    fis         = null;
    in          = null;
    fos         = null;
    out         = null;
    filename    = "test.ref";
    initGUI();
}

public void initGUI(){
    setTitle("WIM Reference Data Comparison Tool");
    setSize(500, 400);
    setVisible(true);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setLocationRelativeTo(null);

    // Menu Bar and Menu Items setup
    JMenuBar    menuBar;
    JMenu       menu;
    JMenuItem   menuItem;

    menuBar = new JMenuBar();
    menu = new JMenu("Menu");
    menuBar.add(menu);

    menuNew     = new JMenuItem("New");
    menuOpen    = new JMenuItem("Open");
    menuSave    = new JMenuItem("Save");
    menuExit    = new JMenuItem("Exit");

    menuNew.addActionListener(this);
    menuOpen.addActionListener(this);
    menuSave.addActionListener(this);
    menuExit.addActionListener(this);

    menu.add(menuNew);
    menu.add(menuOpen);
    menu.add(menuSave);
    menu.add(menuExit);
    setJMenuBar(menuBar);               


    container       = new JPanel(new BorderLayout());

    String[] columnNames = {"", "MotorBike", " Car"}; 
    Object[][] data = { {"Vehicle Summary", new Integer(100), new Integer(200)},  // Header 1: Green
                        {"Axle Numbers", new Integer(100), new Integer(200)},
                        {"Axle Code", new Integer(100), new Integer(200)},
                        {"Axle Distances (cm)", new Integer(100), new Integer(200)},
                        {"Vehicle Speed (km/h)", new Integer(100), new Integer(200)},
                        {"Gross Weight", new Integer(100), new Integer(200)}, 
                        {"Axle Weight 1", new Integer(100), new Integer(200)},
                        {"Axle Weight 2", new Integer(100), new Integer(200)},
                        };

    model = new DefaultTableModel(data, columnNames);
    table = new JTable(data, columnNames);
    table.setPreferredScrollableViewportSize(new Dimension(500, 70)); 
    table.setFillsViewportHeight(true); 
    scrollPane = new JScrollPane(table); 


    container.add(scrollPane, BorderLayout.CENTER);
    add(container);
}



public void setPanel(JTable table){

    scrollPane.remove(table);
    container.remove(scrollPane);

    scrollPane  = new JScrollPane(table); 
    container.add(scrollPane, BorderLayout.CENTER);

    repaint();
}

public void actionPerformed(ActionEvent e){
    System.out.println("Clicked item!");
    // NEW
    if(e.getSource() == menuNew){
        System.out.println("New File");
    }
    // SAVE
    if(e.getSource() == menuSave){
        System.out.println("Save!");
        try{
            model.fireTableDataChanged();               
            table = new JTable(model);
            fos     = new FileOutputStream(filename);
            out     = new ObjectOutputStream(fos);
            out.writeObject(table);
        }catch(IOException e3){
            e3.printStackTrace();
        }
    }
    if(e.getSource() == menuOpen){
        System.out.println("Open!");

        JTable table = new JTable();
        try{
            fis     = new FileInputStream(filename);
            in      = new ObjectInputStream(fis);

            table   = new JTable();
            table   = (JTable) in.readObject();
            in.close(); 

            setPanel(table);

            System.out.println("data loaded");

        }catch(IOException e1){
            e1.printStackTrace();
        }catch(ClassNotFoundException e2){
            e2.printStackTrace();
        }
    }
    if(e.getSource() == menuExit){
        System.out.println("Exit!");
    }
}



   public static void main(String[] args) {

    SwingUtilities.invokeLater(new Runnable() {

        public void run() {

            try {
                UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
            } catch (Exception e) {
                e.printStackTrace();
            }

            Save3 ex = new Save3();
            ex.setVisible(true);
        }
    });
}

}

Php Pete
  • 762
  • 3
  • 14
  • 29
  • 4
    I don't think it is a good idea to save the `JTable`; saving the `Model` is more flawless. And even for the model I wouldn't use object serialisation (because of `Listener`s). But then I don't know your specific use case, it may be appropriate. – Hauke Ingmar Schmidt Aug 20 '12 at 10:03
  • the idea of an SSCCE is (as you could have learned if you had googled or followed the link given by @brimborium) to implement a runnable example with the most minimal lines (typically around 50 or less) of code that demonstrate the problem. So go ahead and throw out everything not directly related to the table update ... – kleopatra Aug 21 '12 at 11:08
  • Hello kleopatra, I have now updated the code to SSCCE standard.. – Php Pete Aug 21 '12 at 11:42

3 Answers3

6

Your new table is not added anywhere, so it will not show up. Try something like this:

public void actionPerformed(ActionEvent e){
  JTable oldTable = table;

  // your stuff, loading the table from file

  thePanelHoldingYourTable.remove(oldTable);
  thePanelHoldingYourTable.add(table);
  // if there are other components in that panel, make sure, your table is in the right spot
  // maybe refresh your layout by using invalidate()
}

EDIT: Ok, serializing the table is not really advised, it is better to only save the table model. Here is your edited SSCCE (thanks to kleopatra for the help):

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;

public class Save3 extends JFrame implements ActionListener {
    private static final long serialVersionUID = 1L;

    public int PADDING = 10;

    public JMenuItem menuNew;
    public JMenuItem menuOpen;
    public JMenuItem menuSave;
    public JMenuItem menuExit;

    public JPanel container;

    public DefaultTableModel model;
    public JScrollPane scrollPane;
    public JTable table;

    public FileInputStream fis;
    public ObjectInputStream in;
    public FileOutputStream fos;
    public ObjectOutputStream out;
    public String filename;

    String[] columnNames = {"", "MotorBike", " Car"};
    Object[][] data = { {"Vehicle Summary", new Integer(100), new Integer(200)},  // Header 1: Green
    {"Axle Numbers", new Integer(100), new Integer(200)}, {"Axle Code", new Integer(100), new Integer(200)}, {"Axle Distances (cm)", new Integer(100), new Integer(200)}, {"Vehicle Speed (km/h)", new Integer(100), new Integer(200)}, {"Gross Weight", new Integer(100), new Integer(200)}, {"Axle Weight 1", new Integer(100), new Integer(200)}, {"Axle Weight 2", new Integer(100), new Integer(200)},};

    public Save3() {
        fis = null;
        in = null;
        fos = null;
        out = null;
        filename = "test.ref";
        initGUI();
    }

    public void initGUI() {
        setTitle("WIM Reference Data Comparison Tool");
        setSize(500, 400);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);

        // Menu Bar and Menu Items setup
        JMenuBar menuBar;
        JMenu menu;

        menuBar = new JMenuBar();
        menu = new JMenu("Menu");
        menuBar.add(menu);

        menuNew = new JMenuItem("New");
        menuOpen = new JMenuItem("Open");
        menuSave = new JMenuItem("Save");
        menuExit = new JMenuItem("Exit");

        menuNew.addActionListener(this);
        menuOpen.addActionListener(this);
        menuSave.addActionListener(this);
        menuExit.addActionListener(this);

        menu.add(menuNew);
        menu.add(menuOpen);
        menu.add(menuSave);
        menu.add(menuExit);
        setJMenuBar(menuBar);

        container = new JPanel(new BorderLayout());

        model = new DefaultTableModel(data, columnNames);
        table = new JTable();
        table.setModel(model);
        table.setPreferredScrollableViewportSize(new Dimension(500, 70));
        table.setFillsViewportHeight(true);
        scrollPane = new JScrollPane(table);

        container.add(scrollPane, BorderLayout.CENTER);
        add(container);
    }

    public void setModel(DefaultTableModel writeModel) {
        table.setModel(writeModel);
    }

    public void actionPerformed(ActionEvent e) {
        System.out.println("Clicked item!");
        if (e.getSource() == menuNew) {
            System.out.println("New File");
        } else if (e.getSource() == menuSave) {
            System.out.println("Save!");
            try {
                fos = new FileOutputStream(filename);
                out = new ObjectOutputStream(fos);
                table.clearSelection();
                table.setModel(new DefaultTableModel());
                out.writeObject(model);
                table.setModel(model);
            } catch (IOException e3) {
                e3.printStackTrace();
            } finally {
                try {
                    out.close();
                    fos.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        } else if (e.getSource() == menuOpen) {
            System.out.println("Open!");
            try {
                fis = new FileInputStream(filename);
                in = new ObjectInputStream(fis);
                DefaultTableModel modelRead = (DefaultTableModel) in.readObject();
                setModel(modelRead);
                System.out.println("data loaded");
                in.close();
                fis.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            } catch (ClassNotFoundException e2) {
                e2.printStackTrace();
            }
        } else if (e.getSource() == menuExit) {
            System.out.println("Exit!");
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                Save3 ex = new Save3();
                ex.setVisible(true);
            }
        });
    }
}

Please notice that I use the following three lines to serialize the model:

table.setModel(new DefaultTableModel());
out.writeObject(model);
table.setModel(model);

So the model is detached from the table while serializing. Unfortunately the model tries to serialize its listeners as well (which fails). That's why this step is neccessary. Once saved, the model can be applied to the table again.

brimborium
  • 9,362
  • 9
  • 48
  • 76
  • Thanks, I cant believe I overlooked something so small. Too add further to your answer, it was required I used to repaint() after the new table has been added. – Php Pete Aug 21 '12 at 08:32
  • Glad I could help. Try to use `invalidate()` instead of `repaint()`. – brimborium Aug 21 '12 at 08:46
  • It seems I was a bit too quick to mark this correct, the table is still not reset even when I remove and add the components to the frame! – Php Pete Aug 21 '12 at 10:30
  • Can you show us an SSCCE that illustrates your problem? I think you have another problem then. – brimborium Aug 21 '12 at 10:32
  • Sure, I have updated the original post with the code im using. The Actionlistener "Open" calls for the table to be read from the file and then added to the panel. What is actually happening is the original table is constantly added to the panel.. – Php Pete Aug 21 '12 at 10:42
  • Wow, I wouldn't call that an [SSCCE](http://sscce.org/). ;) I will look at it tonight, though. If possible, it would be nice if you could make an SSCCE out of that. ;) – brimborium Aug 21 '12 at 10:45
  • cool man, I have updated the code to a much simpiler probelm if it makes it easier to read. – Php Pete Aug 21 '12 at 11:42
  • @Jnanathan Ok, I posted a solution. It just saves the underlying data vector, which seems to be the most reliable and easiest solution. You could also have a go at saving the header vector as well. I leave that to you (if the number of collumns don't change/switch, there is no point in doing that though). – brimborium Aug 22 '12 at 09:33
  • _cell editors (that are linked with the table model since Java 6)_ - plain wrong ... the DefaultTableModel is (and must be!) completely unrelated to the view – kleopatra Aug 22 '12 at 09:38
  • 1
    @kleopatra Well it isn't. I am using jdk1.6.0_30 and if I try `out.writeObject(model);` in the above code (with `model` being a standard `DefaultTableModel`), I get a `java.io.NotSerializableException: javax.swing.JTable$CellEditorRemover` when something in the table is selected during save... – brimborium Aug 22 '12 at 09:52
  • @kleopatra btw: I would agree that this should be the case, but I can not explain this exception otherwise. Can you? – brimborium Aug 22 '12 at 09:53
  • 1
    interesting ... could be that the model tries to serialize its listeners (which it shouldn't, IMO, because that indirectly serializes the whole table). You could check that assumption by removing the model from the table before serializing it – kleopatra Aug 22 '12 at 10:06
  • @kleopatra Oh, that works perfectly. Thanks. But I agree, the model should not serialize its listeners. Anyway, I just shortly assign a different model to the table, save the model and then assign it back to the table. – brimborium Aug 22 '12 at 10:20
  • Thank you brimborium & kleopatra for providing this solution, much appreciated. – Php Pete Aug 27 '12 at 08:32
3
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • 2
    If I'm not mistaken, the first point applies to `DefaultTableModel`; `AbstractTableModel`, for [example](http://stackoverflow.com/a/9134371/230513), permits other data structures. – trashgod Aug 20 '12 at 10:15
2

In the actionPerformed code, the imported table (this is a new instance) isn't added into a container.

gontard
  • 28,720
  • 11
  • 94
  • 117