2

What I already have is a JTable with a MyTableModel table model attached to it. I have a tableChanged() method that writes table data to a data.csv file at the time a table cell is modified. This means that table data is written to the file every time user inputs new data. Instead I want there to be a menu item where user clicks 'Save' and then proceed with the code that saves to the file.

@Override
public void tableChanged(TableModelEvent e) {
    int row = e.getFirstRow();
    int column = e.getColumn();
    TableModel model = (TableModel) e.getSource();
    String columnName = model.getColumnName(column);
    Object data = model.getValueAt(row, column);

    BufferedWriter bw = null;
    FileWriter fw = null;
    String rivi = "";
    try {
        fw = new FileWriter("data.csv");
        bw = new BufferedWriter(fw);

        for (int i = 0; i < model.getRowCount(); i++) {
            for (int j = 0; j < model.getColumnCount(); j++) {
                if (j == 69) {
                    rivi += model.getValueAt(i, j);
                } else {
                    rivi += model.getValueAt(i, j) + ",";
                }
            }
            if (i != 69) {
                rivi += "\n";
            }
        }
        bw.write(rivi);
    } catch (Exception ex) {
        ex.printStackTrace();
    } finally {
        try {
            if (bw != null) {
                bw.close();
            }
            if (fw != null) {
                fw.close();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

}

What I need, assuming I've understood correctly, is to move the file saving code from the tableChanged() method to actionPerformed() method, but I don't know how to get the TableModelEvent e parameter from tableChanged() method and use it in the actionPerformed() method, so that I could get the table data via TableModel model = (TableModel) e.getSource() and save it to the file.

@Override
public void actionPerformed(ActionEvent e) {
    // File saving code here?
}

Full code here:

import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.BufferedWriter;
import java.io.FileWriter;
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.KeyStroke;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import static sun.misc.ClassFileTransformer.add;



public class DesktopEsimerkki extends JPanel
        implements TableModelListener, ActionListener {

    protected JTable table = null;
    protected TableModel model = null;

    public DesktopEsimerkki() {
        super(new GridLayout(1, 0));

        table = new JTable(new MyTableModel());
        table.setFillsViewportHeight(true);
        table.getModel().addTableModelListener(this);

        add(new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED));
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);

        this.model = table.getModel();
    }

    @Override
    public void tableChanged(TableModelEvent e) {}

    @Override
    public void actionPerformed(ActionEvent e) {
        BufferedWriter bw = null;
        String rivi = "";
        FileWriter fw = null;
        try {
            fw = new FileWriter("data.csv");
            bw = new BufferedWriter(fw);

            for (int i = 0; i < model.getRowCount(); i++) {
                for (int j = 0; j < model.getColumnCount(); j++) {
                    if (j == 69) {
                        rivi += model.getValueAt(i, j);
                    } else {
                        rivi += model.getValueAt(i, j) + ",";
                    }
                }
                if (i != 69) {
                    rivi += "\n";
                }
            }
            System.out.println(rivi);
            bw.write(rivi);
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (bw != null) {
                    bw.close();
                }
                if (fw != null) {
                    fw.close();
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public JMenuBar luoValikkoPalkki() {
        JMenuBar valikkopalkki = new JMenuBar();
        JMenu valikko = new JMenu("File");
        valikko.setMnemonic(KeyEvent.VK_F);
        valikko.getAccessibleContext().setAccessibleDescription(
                "File Saving MEnu");
        valikkopalkki.add(valikko);

        JMenuItem valikkoitem = new JMenuItem("Save", KeyEvent.VK_S);
        valikkoitem.setAccelerator(KeyStroke.getKeyStroke(
                java.awt.event.KeyEvent.VK_S, ActionEvent.CTRL_MASK));
        valikko.add(valikkoitem);
        valikkoitem.addActionListener(this);
        return valikkopalkki;
    }

    class MyTableModel extends AbstractTableModel {

        private String[] columnNames = {"Col 1", "Col 2", "Col 3"};
        private Object[][] data = {{"Data 1", "Data 2", "Data 3"}};

        @Override
        public int getColumnCount() {
            return columnNames.length;
        }

        @Override
        public int getRowCount() {
            return data.length;
        }

        @Override
        public String getColumnName(int col) {
            return columnNames[col];
        }

        @Override
        public Object getValueAt(int row, int col) {
            return data[row][col];
        }

        @Override
        public Class getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }

        @Override
        public boolean isCellEditable(int row, int col) {
            return true;
        }

        @Override
        public void setValueAt(Object value, int row, int col) {
            data[row][col] = value;
            fireTableCellUpdated(row, col);
        }
    }
    private static void createAndShowGUI() {
        JFrame frame = new JFrame("DesktopEsimerkki");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        DesktopEsimerkki desim = new DesktopEsimerkki();
        frame.setJMenuBar(desim.luoValikkoPalkki());

        DesktopEsimerkki newContentPane = new DesktopEsimerkki();
        newContentPane.setOpaque(true);
        frame.setContentPane(newContentPane);

        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}
aleksejjj
  • 1,405
  • 2
  • 18
  • 28
  • 1
    You don't get a TableModelEvent, it doesn't exist in this situation, and what's more important, you don't need it. All you need is a reference to the current model and nothing more. Is the table model or the JTable an instance field of one of your classes? If so, you should be all set. – Hovercraft Full Of Eels Jan 12 '19 at 17:00
  • Having `TableModel model = table.getModel();` in `actionPerformed()` does compile, but the file isn't being written to and I am unsure how to get the model instance to this method. – aleksejjj Jan 12 '19 at 18:04
  • I posted the MCV there. – aleksejjj Jan 12 '19 at 18:46
  • To reproduce the problem: change the cell value and then click 'Save'. Compare the resulting file and the text that is now visible on the cell. As you can see, the actual visible data from the model is not being sent to the file. – aleksejjj Jan 12 '19 at 18:56
  • Please see edits to answer. Nice MCVE by the way +1 to this question – Hovercraft Full Of Eels Jan 12 '19 at 19:25
  • Answer edited once again. Please ask if any questions – Hovercraft Full Of Eels Jan 12 '19 at 19:32

1 Answers1

2

Your code is working and your problem is much more basic -- you're creating too many DesktopEsimerkki objects, one whose value you're changing and the other whose values you're displaying. Change this:

    JFrame frame = new JFrame("DesktopEsimerkki");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    // this guy creates the menu
    DesktopEsimerkki desim = new DesktopEsimerkki();
    frame.setJMenuBar(desim.luoValikkoPalkki());

    // but this separate guy is used as the GUI
    DesktopEsimerkki newContentPane = new DesktopEsimerkki();
    newContentPane.setOpaque(true);
    frame.setContentPane(newContentPane);

to this:

    JFrame frame = new JFrame("DesktopEsimerkki");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    // use only one instance for **both**
    DesktopEsimerkki desim = new DesktopEsimerkki();
    frame.setJMenuBar(desim.luoValikkoPalkki());

    // DesktopEsimerkki newContentPane = new DesktopEsimerkki();
    // newContentPane.setOpaque(true);
    // frame.setContentPane(newContentPane);

    // *********** here ***********
    frame.setContentPane(desim);

Just for grins, I created my own program that does this, something like:

enter image description here

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Formatter;
import java.util.Scanner;

import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;

@SuppressWarnings("serial")
public class TableFun extends JPanel {
    public static final String DATA_FILE_PATH = "data.csv";
    private MyTableModel model = new MyTableModel();
    private JTable table = new JTable(model);
    private JTextField fileNameField = new JTextField(DATA_FILE_PATH, 30);
    private JMenuBar menuBar;
    private Action writeModelAction = new WriteModelAction(table, "Write to File", this);
    private Action readModelAction = new ReadModelAction(table, "Read from File", this);

    public TableFun() {
        JPanel btnPanel = new JPanel();
        btnPanel.add(new JLabel("File:"));
        btnPanel.add(fileNameField);
        btnPanel.add(new JButton(writeModelAction));
        btnPanel.add(new JButton(readModelAction));

        table.getColumnModel().getColumn(0).setCellRenderer(new TimeColumnRenderer());
        JScrollPane scrollpane = new JScrollPane(table);
        setLayout(new BorderLayout());
        add(scrollpane);
        add(btnPanel, BorderLayout.PAGE_START);
    }

    public String getFileName() {
        return fileNameField.getText();
    }

    public JMenuBar getMenuBar() {
        if (menuBar == null) {
            menuBar = new JMenuBar();
            JMenu fileMenu = new JMenu("File");
            fileMenu.setMnemonic(KeyEvent.VK_F);
            menuBar.add(fileMenu);

            fileMenu.add(new JMenuItem(readModelAction));
            fileMenu.add(new JMenuItem(writeModelAction));
        }
        return menuBar;
    }    

    private static void createAndShowGui() {
        TableFun mainPanel = new TableFun();

        JFrame frame = new JFrame("TableFun");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.setJMenuBar(mainPanel.getMenuBar());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

@SuppressWarnings("serial")
class WriteModelAction extends AbstractAction {
    private ReadWriteTableData readWriteTableData;
    private TableFun tableFun;

    public WriteModelAction(JTable table, String name, TableFun tableFun) {
        super(name);
        int mnemonic = name.charAt(0);
        putValue(MNEMONIC_KEY, mnemonic);
        readWriteTableData = new ReadWriteTableData(table);
        this.tableFun = tableFun;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        File file = new File(tableFun.getFileName());
        if (file.exists()) {
            String message = "File " + tableFun.getFileName() + " already exists -- do you wish to overrite?";
            String title = "File Already Exists";
            int optionType = JOptionPane.YES_NO_OPTION;
            int response = JOptionPane.showConfirmDialog(tableFun, message, title, optionType);
            if (response != JOptionPane.YES_OPTION) {
                return;
            }
        }
        try {
            readWriteTableData.write(file);
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }
}

@SuppressWarnings("serial")
class ReadModelAction extends AbstractAction {
    private ReadWriteTableData readWriteTableData;
    private TableFun tableFun;

    public ReadModelAction(JTable table, String name, TableFun tableFun) {
        super(name);
        int mnemonic = name.charAt(0);
        putValue(MNEMONIC_KEY, mnemonic);
        readWriteTableData = new ReadWriteTableData(table);
        this.tableFun = tableFun;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        File file = new File(tableFun.getFileName());
        if (!file.exists()) {
            String message = "File " + tableFun.getFileName() + " cannot be found -- aborting read";
            String title = "File Cannot Be Found";
            int optionType = JOptionPane.WARNING_MESSAGE;
            JOptionPane.showMessageDialog(tableFun, message, title, optionType);
            return;
        }
        try {
            readWriteTableData.read(file);
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }
}

class ReadWriteTableData {
    private JTable table;

    public ReadWriteTableData(JTable table) {
        this.table = table;
    }

    public void read(File file) throws FileNotFoundException {
        MyTableModel tableModel = (MyTableModel) table.getModel();
        tableModel.setRowCount(0);
        Scanner scanner = new Scanner(file);
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine().trim();
            if (!line.isEmpty()) {
                String[] tokens = line.split("\\|");
                String[] trimmedTokens = new String[tokens.length];
                for (int i = 0; i < tokens.length; i++) {
                    String text = tokens[i];
                    if (text == null || text.trim().equals("null")) {
                        text = "";
                    } else {
                        text = text.trim();
                    }
                    trimmedTokens[i] = text;
                }
                tableModel.addRow(trimmedTokens);
            }
        }
        table.setModel(tableModel);
        scanner.close();
    }

    public void write(File file) throws IOException {
        FileWriter fileWriter = new FileWriter(file);
        Formatter formatter = new Formatter(fileWriter);
        MyTableModel model = (MyTableModel) table.getModel();
        for (int row = 0; row < model.getRowCount(); row++) {
            for (int col = 0; col < model.getColumnCount(); col++) {
                String value = (String) model.getValueAt(row, col);
                value = value == null ? "" : value;
                formatter.format("%25s ", value);
                if (col != model.getColumnCount() - 1) {
                    formatter.format(" | ");
                }
            }
            formatter.format("%n");
        }
        if (formatter != null) {
            formatter.close();
        }        
    }

}

class MyTableModel extends DefaultTableModel {
    public static final String[] COLUMNS = {"Time", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"};
    private static final int START_TIME = 8;
    private static final int END_TIME = 17;

    public MyTableModel() {
        super(COLUMNS, 0);
        for (int i = START_TIME; i < END_TIME + 1; i++) {
            Object[] rowData = {String.valueOf(i), "", "", "", "", ""};
            addRow(rowData);
        }
    }

    @Override
    public boolean isCellEditable(int row, int column) {
        return column != 0;
    }
}

class TimeColumnRenderer extends DefaultTableCellRenderer {
    public TimeColumnRenderer() {
        setHorizontalAlignment(SwingConstants.TRAILING);
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
            boolean hasFocus, int row, int column) {
        int time = Integer.parseInt(value.toString());
        time = time > 12 ? time % 12 : time;
        String timeText = String.format("%d:00", time);
        return super.getTableCellRendererComponent(table, timeText, isSelected, hasFocus, row, column);
    }
}

It uses the pipe symbol, | as the csv delimiter, so a typical text file could look like:

                8  |                            |                      judy  |                            |                            |                           
                9  |                      John  |                            |                      Bill  |                            |                     Frank 
               10  |                            |                     Hello  |                            |                   Goodbye  |                           
               11  |               Donald duck  |               Frank Nitti  |                 Al Capone  |                   Johnson  |                           
               12  |                            |                            |                            |                            |                        ss 
               13  |                            |                            |                            |                            |                           
               14  |              Bill Clinton  |              Donald Trump  |                     Hello  |                     Fresh  |                           
               15  |                            |                  What the  |                      heck  |                            |                       Yes 
               16  |                            |                            |                            |                            |                           
               17  |                      Here  |                        we  |                       are  |                            |                           
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373