001package jmri.util.table; 002 003import java.awt.event.ActionEvent; 004import java.io.File; 005import java.io.FileOutputStream; 006import java.io.IOException; 007import java.io.OutputStreamWriter; 008import java.nio.charset.StandardCharsets; 009import java.util.ArrayList; 010import java.util.Arrays; 011import java.util.List; 012 013import javax.annotation.Nonnull; 014import javax.swing.AbstractAction; 015import javax.swing.filechooser.FileNameExtensionFilter; 016import javax.swing.JFileChooser; 017import javax.swing.JTable; 018import javax.swing.table.TableModel; 019 020import jmri.util.FileUtil; 021import jmri.util.swing.JmriJOptionPane; 022 023import org.apache.commons.csv.CSVFormat; 024import org.apache.commons.csv.CSVPrinter; 025 026/** 027 * Save a JTable or AbstractTableModel to CSV file after prompting for filename. 028 * <p> 029 * First line contains Column Headings. 030 * Save order can replicate current JTable sort, filters, 031 * visible columns and column order. 032 * Entire Table Model can be saved by not specifying a JTable. 033 * Can exclude specific columns ( e.g. JButtons ) from the save. 034 * 035 * @author Steve Young Copyright (C) 2020 036 * @since 4.19.5 037 */ 038public class JTableToCsvAction extends AbstractAction { 039 040 private final JFileChooser _fileChooser; 041 private File _saveFile; 042 private final JTable _table; 043 private final TableModel _model; 044 private final String _defaultFileName; 045 private String _saveFileName; 046 private final int[] _excludedCols; 047 private List<Integer> modelColList; 048 private List<Integer> modelRowList; 049 050 /** 051 * Create a new Save to CSV Action. 052 * 053 * @param actionName Action Name 054 * @param jtable to save the view, else null for whole table. 055 * @param model Table Model to use. 056 * @param defaultFileName File Name to use as default. 057 * @param excludedCols int Array of Table Model columns to exclude. 058 */ 059 public JTableToCsvAction(String actionName, JTable jtable, @Nonnull TableModel model, 060 @Nonnull String defaultFileName, @Nonnull int[] excludedCols ){ 061 super(actionName); 062 _table = jtable; 063 _model = model; 064 _defaultFileName = defaultFileName; 065 _excludedCols = excludedCols; 066 _fileChooser = new jmri.util.swing.JmriJFileChooser(FileUtil.getUserFilesPath()); 067 } 068 069 /** 070 * {@inheritDoc} 071 */ 072 @Override 073 public void actionPerformed(ActionEvent e) { 074 075 _fileChooser.setFileFilter(new FileNameExtensionFilter(Bundle.getMessage("CSVFileLabel"), "csv")); 076 _fileChooser.setSelectedFile(new File(_defaultFileName)); 077 078 // handle selection or cancel 079 if (_fileChooser.showSaveDialog(_table) == JFileChooser.APPROVE_OPTION) { 080 _saveFileName = _fileChooser.getSelectedFile().getPath(); 081 if (!_saveFileName.regionMatches(true,_saveFileName.length()-4,".csv",0,4)) { // 082 _saveFileName += ".csv"; 083 } 084 _saveFile = new File(_saveFileName); 085 if (continueIfExisting()) { 086 saveToCSV(); 087 } 088 } 089 } 090 091 private boolean continueIfExisting(){ 092 return !(_saveFile.isFile() && ( JmriJOptionPane.showConfirmDialog(_table, 093 Bundle.getMessage("ConfirmOverwriteFile"), 094 Bundle.getMessage("ConfirmQuestion"), JmriJOptionPane.YES_NO_OPTION, 095 JmriJOptionPane.QUESTION_MESSAGE) 096 != JmriJOptionPane.YES_OPTION)); 097 } 098 099 private void saveToCSV() { 100 101 createColumnsAndRows(); 102 103 try (CSVPrinter p = new CSVPrinter(new OutputStreamWriter( 104 new FileOutputStream(_saveFileName), StandardCharsets.UTF_8), CSVFormat.DEFAULT)) { 105 106 addToFile(p); 107 108 p.flush(); 109 p.close(); 110 } catch (IOException e) { 111 log.error("Error Saving Table to CSV File {}",e.toString()); 112 } 113 } 114 115 private void addToFile(CSVPrinter p) throws IOException { 116 117 // Save table per row. _saveFileName 118 // print header labels 119 List<String> headers = new ArrayList<>(); 120 for (int i = 0; i < modelColList.size(); i++) { 121 headers.add(_model.getColumnName(modelColList.get(i))); 122 } 123 p.printRecord(headers); 124 125 // print rows 126 for (int i = 0; i < modelRowList.size(); i++) { 127 List<String> row = new ArrayList<>(); 128 for (int j = 0; j < modelColList.size(); j++) { 129 String val = String.valueOf( _model.getValueAt(modelRowList.get(i), modelColList.get(j))); 130 if (val.equals("null")) { 131 val = ""; 132 } 133 row.add(val); 134 } 135 p.printRecord(row); 136 } 137 138 } 139 140 private void createColumnsAndRows(){ 141 142 modelColList = new ArrayList<>(); 143 modelRowList = new ArrayList<>(); 144 145 if ( _table != null ) { 146 for (int i = 0; i < _table.getColumnCount(); i++) { 147 addColtoListIfNotExcluded(modelColList,_table.convertColumnIndexToModel(i),_excludedCols); 148 } 149 for (int i = 0; i < _table.getRowCount(); i++) { 150 modelRowList.add(_table.convertRowIndexToModel(i)); 151 } 152 } else { 153 for (int i = 0; i < _model.getColumnCount(); i++) { 154 addColtoListIfNotExcluded(modelColList,i,_excludedCols); 155 } 156 for (int i = 0; i < _model.getRowCount(); i++) { 157 modelRowList.add(i); 158 } 159 } 160 161 } 162 163 private void addColtoListIfNotExcluded(@Nonnull List<Integer> list, 164 int modelCol, @Nonnull int[] colsToNotSave){ 165 if (! Arrays.stream(colsToNotSave).anyMatch(j -> j == modelCol)){ 166 list.add(modelCol); 167 } 168 } 169 170 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(JTableToCsvAction.class); 171 172}