View Javadoc
1   /*******************************************************************************
2    * Copyhacked (H) 2012-2025.
3    * This program and the accompanying materials
4    * are made available under no term at all, use it like
5    * you want, but share and discuss it
6    * every time possible with every body.
7    * 
8    * Contributors:
9    *      ron190 at ymail dot com - initial implementation
10   ******************************************************************************/
11  package com.jsql.view.swing.action;
12  
13  import com.jsql.util.LogLevelUtil;
14  import com.jsql.view.swing.dialog.ReplaceFileChooser;
15  import com.jsql.view.swing.table.PanelTable;
16  import com.jsql.view.swing.util.MediatorHelper;
17  import org.apache.logging.log4j.LogManager;
18  import org.apache.logging.log4j.Logger;
19  
20  import javax.swing.*;
21  import javax.swing.text.JTextComponent;
22  import java.awt.event.ActionEvent;
23  import java.awt.event.InputEvent;
24  import java.awt.event.KeyEvent;
25  import java.io.BufferedWriter;
26  import java.io.FileWriter;
27  import java.io.IOException;
28  import java.nio.charset.StandardCharsets;
29  import java.nio.file.InvalidPathException;
30  
31  /**
32   * Save the content of tab in a file.
33   */
34  public class ActionSaveTab extends AbstractAction {
35      
36      private static final Logger LOGGER = LogManager.getRootLogger();
37      
38      private ReplaceFileChooser replaceFileChooser;
39  
40      public ActionSaveTab() {
41          // Unhandled NoSuchMethodError #82561 on constructor: NoSuchMethodError
42          // Unhandled InternalError #93015 on constructor: InvocationTargetException
43          // Unhandled NullPointerException #95805 on constructor: desktop null on Windows
44          // Unhandled IllegalArgumentException #95985 on constructor: Comparison method violates its general contract!
45          try {
46              this.replaceFileChooser = new ReplaceFileChooser(
47                  MediatorHelper.model().getMediatorUtils().getPreferencesUtil().getPathFile()
48              );
49          } catch (IllegalArgumentException | NoSuchMethodError | InternalError | NullPointerException e) {
50              LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Internal error in JFileChooser, verify your system and see stacktrace in tab Java: {}", e.getMessage());
51              LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
52          }
53          this.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK));
54          this.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_S);
55          this.putValue(Action.NAME, "Save Tab As...");
56      }
57      
58      @Override
59      public void actionPerformed(ActionEvent e) {
60          this.replaceFileChooser.setDialogTitle("Save Tab As");
61          var componentResult = MediatorHelper.tabResults().getSelectedComponent();
62          if (componentResult instanceof PanelTable) {
63              JTable table = ((PanelTable) componentResult).getTableValues();
64              this.saveToFile(table);
65          } else if (
66              componentResult instanceof JScrollPane
67              && ((JScrollPane) componentResult).getViewport().getView() instanceof JTextComponent
68          ) {
69              JTextComponent textarea = (JTextComponent) ((JScrollPane) componentResult).getViewport().getView();
70              this.saveToFile(textarea);
71          }
72      }
73      
74      private void saveToFile(JComponent textarea) {
75          if (textarea == null) {
76              return;
77          }
78  
79          this.replaceFileChooser.updateUI();  // required when changing dark/light mode
80          try {  // Fix #96109: InvalidPathException on showSaveDialog()
81              int stateSave = this.replaceFileChooser.showSaveDialog(MediatorHelper.frame());
82              if (stateSave == JFileChooser.APPROVE_OPTION) {
83                  var folderSelectedFile = this.replaceFileChooser.getCurrentDirectory().toString();
84                  MediatorHelper.model().getMediatorUtils().getPreferencesUtil().set(folderSelectedFile);
85                  if (textarea instanceof JTextComponent) {
86                      this.saveTextToFile((JTextComponent) textarea);
87                  } else if (textarea instanceof JTable) {
88                      this.saveTableToFile((JTable) textarea);
89                  }
90              }
91          } catch (InvalidPathException e) {
92              LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
93          }
94      }
95  
96      private void saveTableToFile(JTable tableResults) {
97          var fileSelected = this.replaceFileChooser.getSelectedFile();
98          
99          try (var fileWriter = new FileWriter(fileSelected, StandardCharsets.UTF_8)) {
100             var tableModel = tableResults.getModel();
101             for (var i = 2 ; i < tableModel.getColumnCount() ; i++) {
102                 fileWriter.write(tableModel.getColumnName(i) + "\t");
103             }
104             fileWriter.write("\n");
105             
106             for (var i = 0 ; i < tableModel.getRowCount() ; i++) {
107                 for (var j = 2 ; j < tableModel.getColumnCount() ; j++) {
108                     // Cell empty when string was too long to be injected (columnTooLong|cellEmpty|cellEmpty).
109                     if (tableModel.getValueAt(i, j) == null) {
110                         fileWriter.write("\t");
111                     } else {
112                         var line = tableModel.getValueAt(i, j).toString();  // Encode line break.
113                         line = line
114                             .replace("\n", "\\n")
115                             .replace("\t", "\\t");
116                         line = line + "\t";
117                         fileWriter.write(line);
118                     }
119                 }
120                 fileWriter.write("\n");
121             }
122         } catch (IOException e) {
123             LOGGER.log(
124                 LogLevelUtil.CONSOLE_ERROR,
125                 String.format("Error writing to %s", fileSelected.getName()),
126                 e.getMessage()  // full stacktrace not required
127             );
128         }
129     }
130 
131     private void saveTextToFile(JTextComponent textarea) {
132         var fileSelected = this.replaceFileChooser.getSelectedFile();
133         try (
134             var fileWriter = new FileWriter(fileSelected, StandardCharsets.UTF_8);
135             var fileOut = new BufferedWriter(fileWriter)
136         ) {
137             textarea.write(fileOut);
138         } catch (IOException e) {
139             LOGGER.log(
140                 LogLevelUtil.CONSOLE_ERROR,
141                 String.format("Error writing to %s", fileSelected.getName()),
142                 e
143             );
144         }
145     }
146 }