19 package org.sleuthkit.autopsy.contentviewers;
21 import java.awt.BorderLayout;
22 import java.awt.Component;
23 import java.awt.Cursor;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.sql.Connection;
28 import java.sql.DriverManager;
29 import java.sql.ResultSet;
30 import java.sql.ResultSetMetaData;
31 import java.sql.SQLException;
32 import java.sql.Statement;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collections;
36 import java.util.LinkedHashMap;
37 import java.util.List;
39 import java.util.Objects;
40 import java.util.TreeMap;
41 import java.util.logging.Level;
42 import javax.swing.JComboBox;
43 import javax.swing.JFileChooser;
44 import javax.swing.JOptionPane;
45 import javax.swing.filechooser.FileNameExtensionFilter;
46 import org.apache.commons.io.FilenameUtils;
47 import org.openide.util.NbBundle;
48 import org.openide.windows.WindowManager;
64 @SuppressWarnings(
"PMD.SingularField")
65 class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
67 private static final long serialVersionUID = 1L;
68 public static final String[] SUPPORTED_MIMETYPES =
new String[]{
"application/x-sqlite3"};
69 private static final int ROWS_PER_PAGE = 100;
70 private static final Logger logger = Logger.getLogger(FileViewer.class.getName());
71 private final SQLiteTableView selectedTableView =
new SQLiteTableView();
72 private AbstractFile sqliteDbFile;
73 private File tmpDbFile;
74 private Connection connection;
76 private int currPage = 0;
81 public SQLiteViewer() {
83 jTableDataPanel.add(selectedTableView, BorderLayout.CENTER);
91 @SuppressWarnings(
"unchecked")
93 private
void initComponents() {
95 jHdrPanel =
new javax.swing.JPanel();
96 tablesDropdownList =
new javax.swing.JComboBox<>();
97 jLabel1 =
new javax.swing.JLabel();
98 numEntriesField =
new javax.swing.JTextField();
99 jLabel2 =
new javax.swing.JLabel();
100 currPageLabel =
new javax.swing.JLabel();
101 jLabel3 =
new javax.swing.JLabel();
102 numPagesLabel =
new javax.swing.JLabel();
103 prevPageButton =
new javax.swing.JButton();
104 nextPageButton =
new javax.swing.JButton();
105 exportCsvButton =
new javax.swing.JButton();
106 jTableDataPanel =
new javax.swing.JPanel();
108 jHdrPanel.setPreferredSize(
new java.awt.Dimension(536, 40));
110 tablesDropdownList.setModel(
new javax.swing.DefaultComboBoxModel<>(
new String[] {
"Item 1",
"Item 2",
"Item 3",
"Item 4" }));
111 tablesDropdownList.addActionListener(
new java.awt.event.ActionListener() {
112 public void actionPerformed(java.awt.event.ActionEvent evt) {
113 tablesDropdownListActionPerformed(evt);
117 org.openide.awt.Mnemonics.setLocalizedText(jLabel1,
org.openide.util.NbBundle.getMessage(SQLiteViewer.class,
"SQLiteViewer.jLabel1.text"));
119 numEntriesField.setEditable(
false);
120 numEntriesField.setText(
org.openide.util.NbBundle.getMessage(SQLiteViewer.class,
"SQLiteViewer.numEntriesField.text"));
121 numEntriesField.setBorder(null);
123 org.openide.awt.Mnemonics.setLocalizedText(jLabel2,
org.openide.util.NbBundle.getMessage(SQLiteViewer.class,
"SQLiteViewer.jLabel2.text"));
125 org.openide.awt.Mnemonics.setLocalizedText(currPageLabel,
org.openide.util.NbBundle.getMessage(SQLiteViewer.class,
"SQLiteViewer.currPageLabel.text"));
127 org.openide.awt.Mnemonics.setLocalizedText(jLabel3,
org.openide.util.NbBundle.getMessage(SQLiteViewer.class,
"SQLiteViewer.jLabel3.text"));
129 org.openide.awt.Mnemonics.setLocalizedText(numPagesLabel,
org.openide.util.NbBundle.getMessage(SQLiteViewer.class,
"SQLiteViewer.numPagesLabel.text"));
131 prevPageButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_back.png")));
132 org.openide.awt.Mnemonics.setLocalizedText(prevPageButton,
org.openide.util.NbBundle.getMessage(SQLiteViewer.class,
"SQLiteViewer.prevPageButton.text"));
133 prevPageButton.setBorderPainted(
false);
134 prevPageButton.setContentAreaFilled(
false);
135 prevPageButton.setDisabledSelectedIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png")));
136 prevPageButton.setMargin(
new java.awt.Insets(2, 0, 2, 0));
137 prevPageButton.setPreferredSize(
new java.awt.Dimension(23, 23));
138 prevPageButton.addActionListener(
new java.awt.event.ActionListener() {
139 public void actionPerformed(java.awt.event.ActionEvent evt) {
140 prevPageButtonActionPerformed(evt);
144 nextPageButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png")));
145 org.openide.awt.Mnemonics.setLocalizedText(nextPageButton,
org.openide.util.NbBundle.getMessage(SQLiteViewer.class,
"SQLiteViewer.nextPageButton.text"));
146 nextPageButton.setBorderPainted(
false);
147 nextPageButton.setContentAreaFilled(
false);
148 nextPageButton.setDisabledSelectedIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png")));
149 nextPageButton.setMargin(
new java.awt.Insets(2, 0, 2, 0));
150 nextPageButton.setPreferredSize(
new java.awt.Dimension(23, 23));
151 nextPageButton.addActionListener(
new java.awt.event.ActionListener() {
152 public void actionPerformed(java.awt.event.ActionEvent evt) {
153 nextPageButtonActionPerformed(evt);
157 org.openide.awt.Mnemonics.setLocalizedText(exportCsvButton,
org.openide.util.NbBundle.getMessage(SQLiteViewer.class,
"SQLiteViewer.exportCsvButton.text"));
158 exportCsvButton.addActionListener(
new java.awt.event.ActionListener() {
159 public void actionPerformed(java.awt.event.ActionEvent evt) {
160 exportCsvButtonActionPerformed(evt);
164 javax.swing.GroupLayout jHdrPanelLayout =
new javax.swing.GroupLayout(jHdrPanel);
165 jHdrPanel.setLayout(jHdrPanelLayout);
166 jHdrPanelLayout.setHorizontalGroup(
167 jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
168 .addGroup(jHdrPanelLayout.createSequentialGroup()
170 .addComponent(jLabel1)
171 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
172 .addComponent(tablesDropdownList, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
174 .addComponent(numEntriesField, javax.swing.GroupLayout.PREFERRED_SIZE, 71, javax.swing.GroupLayout.PREFERRED_SIZE)
176 .addComponent(jLabel2)
177 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
178 .addComponent(currPageLabel)
179 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
180 .addComponent(jLabel3)
181 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
182 .addComponent(numPagesLabel)
184 .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
186 .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
188 .addComponent(exportCsvButton)
189 .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
191 jHdrPanelLayout.setVerticalGroup(
192 jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
193 .addGroup(jHdrPanelLayout.createSequentialGroup()
195 .addGroup(jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
196 .addComponent(exportCsvButton)
197 .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
198 .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
199 .addGroup(jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
200 .addComponent(tablesDropdownList, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
201 .addComponent(jLabel1)
202 .addComponent(numEntriesField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
203 .addComponent(jLabel2)
204 .addComponent(currPageLabel)
205 .addComponent(jLabel3)
206 .addComponent(numPagesLabel)))
210 jTableDataPanel.setLayout(
new java.awt.BorderLayout());
212 javax.swing.GroupLayout layout =
new javax.swing.GroupLayout(
this);
213 this.setLayout(layout);
214 layout.setHorizontalGroup(
215 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
216 .addComponent(jHdrPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 569, Short.MAX_VALUE)
217 .addComponent(jTableDataPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
219 layout.setVerticalGroup(
220 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
221 .addGroup(layout.createSequentialGroup()
222 .addComponent(jHdrPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE)
224 .addComponent(jTableDataPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 317, Short.MAX_VALUE))
228 private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {
229 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
231 if (currPage * ROWS_PER_PAGE > numRows) {
232 nextPageButton.setEnabled(
false);
234 currPageLabel.setText(Integer.toString(currPage));
235 prevPageButton.setEnabled(
true);
238 String tableName = (String) this.tablesDropdownList.getSelectedItem();
239 readTable(tableName, (currPage - 1) * ROWS_PER_PAGE + 1, ROWS_PER_PAGE);
240 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
243 private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {
245 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
248 prevPageButton.setEnabled(
false);
250 currPageLabel.setText(Integer.toString(currPage));
251 nextPageButton.setEnabled(
true);
254 String tableName = (String) this.tablesDropdownList.getSelectedItem();
255 readTable(tableName, (currPage - 1) * ROWS_PER_PAGE + 1, ROWS_PER_PAGE);
256 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
259 private void tablesDropdownListActionPerformed(java.awt.event.ActionEvent evt) {
260 JComboBox<?> cb = (JComboBox<?>) evt.getSource();
261 String tableName = (String) cb.getSelectedItem();
262 if (null == tableName) {
265 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
266 selectTable(tableName);
267 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
277 @NbBundle.Messages({
"SQLiteViewer.csvExport.fileName.empty=Please input a file name for exporting.",
278 "SQLiteViewer.csvExport.title=Export to csv file",
279 "SQLiteViewer.csvExport.confirm.msg=Do you want to overwrite the existing file?"})
280 private void exportCsvButtonActionPerformed(java.awt.event.ActionEvent evt) {
281 Case openCase = Case.getCurrentCase();
282 File caseDirectory =
new File(openCase.getExportDirectory());
283 JFileChooser fileChooser =
new JFileChooser();
284 fileChooser.setDragEnabled(
false);
285 fileChooser.setCurrentDirectory(caseDirectory);
287 FileNameExtensionFilter csvFilter =
new FileNameExtensionFilter(
"*.csv",
"csv");
288 fileChooser.addChoosableFileFilter(csvFilter);
289 fileChooser.setAcceptAllFileFilterUsed(
true);
290 fileChooser.setFileFilter(csvFilter);
291 fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
292 int choice = fileChooser.showSaveDialog((Component) evt.getSource());
293 if (JFileChooser.APPROVE_OPTION == choice) {
294 boolean overwrite =
false;
295 File file = fileChooser.getSelectedFile();
297 JOptionPane.showMessageDialog(
this,
298 Bundle.SQLiteViewer_csvExport_fileName_empty(),
299 Bundle.SQLiteViewer_csvExport_title(),
300 JOptionPane.WARNING_MESSAGE);
302 }
else if (file.exists() && FilenameUtils.getExtension(file.getName()).equalsIgnoreCase(
"csv")) {
303 if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(
this,
304 Bundle.SQLiteViewer_csvExport_confirm_msg(),
305 Bundle.SQLiteViewer_csvExport_title(),
306 JOptionPane.YES_NO_OPTION)) {
313 exportTableToCsv(file, overwrite);
318 private javax.swing.JLabel currPageLabel;
319 private javax.swing.JButton exportCsvButton;
320 private javax.swing.JPanel jHdrPanel;
321 private javax.swing.JLabel jLabel1;
322 private javax.swing.JLabel jLabel2;
323 private javax.swing.JLabel jLabel3;
324 private javax.swing.JPanel jTableDataPanel;
325 private javax.swing.JButton nextPageButton;
326 private javax.swing.JTextField numEntriesField;
327 private javax.swing.JLabel numPagesLabel;
328 private javax.swing.JButton prevPageButton;
329 private javax.swing.JComboBox<String> tablesDropdownList;
333 public List<String> getSupportedMIMETypes() {
334 return Arrays.asList(SUPPORTED_MIMETYPES);
338 public void setFile(AbstractFile file) {
339 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
342 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
346 public Component getComponent() {
351 public void resetComponent() {
352 tablesDropdownList.setEnabled(
true);
353 tablesDropdownList.removeAllItems();
354 numEntriesField.setText(
"");
357 if (null != connection) {
361 }
catch (SQLException ex) {
362 logger.log(Level.SEVERE,
"Failed to close DB connection to file.", ex);
373 "SQLiteViewer.comboBox.noTableEntry=No tables found",
374 "SQLiteViewer.errorMessage.interrupted=The processing of the file was interrupted.",
375 "SQLiteViewer.errorMessage.noCurrentCase=The case has been closed.",
376 "SQLiteViewer.errorMessage.failedToExtractFile=The file could not be extracted from the data source.",
377 "SQLiteViewer.errorMessage.failedToQueryDatabase=The database tables in the file could not be read.",
378 "SQLiteViewer.errorMessage.failedToinitJDBCDriver=The JDBC driver for SQLite could not be loaded.",
379 "# {0} - exception message",
"SQLiteViewer.errorMessage.unexpectedError=An unexpected error occurred:\n{0).",})
380 private void processSQLiteFile() {
382 tablesDropdownList.removeAllItems();
385 String tmpDBPathName;
387 tmpDBPathName = Case.getCurrentCaseThrows().getTempDirectory() + File.separator + sqliteDbFile.getName();
388 }
catch (NoCurrentCaseException ex) {
389 logger.log(Level.SEVERE,
"Current case has been closed", ex);
390 MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_noCurrentCase());
394 tmpDbFile =
new File(tmpDBPathName);
395 if (! tmpDbFile.exists()) {
397 ContentUtils.writeToFile(sqliteDbFile, tmpDbFile);
400 findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() +
"-wal");
401 findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() +
"-shm");
402 }
catch (IOException | NoCurrentCaseException | TskCoreException ex) {
403 logger.log(Level.SEVERE, String.format(
"Failed to create temp copy of DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
404 MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToExtractFile());
411 Class.forName(
"org.sqlite.JDBC");
412 connection = DriverManager.getConnection(
"jdbc:sqlite:" + tmpDBPathName);
414 Map<String, String> dbTablesMap = getTables();
415 if (dbTablesMap.isEmpty()) {
416 tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry());
417 tablesDropdownList.setEnabled(
false);
419 dbTablesMap.keySet().forEach((tableName) -> {
420 tablesDropdownList.addItem(tableName);
423 }
catch (ClassNotFoundException ex) {
424 logger.log(Level.SEVERE, String.format(
"Failed to initialize JDBC SQLite '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
425 MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToinitJDBCDriver());
426 }
catch (SQLException ex) {
427 logger.log(Level.SEVERE, String.format(
"Failed to get tables from DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
428 MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase());
439 private void findAndCopySQLiteMetaFile(AbstractFile sqliteFile, String metaFileName)
throws NoCurrentCaseException, TskCoreException, IOException {
440 Case openCase = Case.getCurrentCaseThrows();
441 SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase();
442 Services services =
new Services(sleuthkitCase);
443 FileManager fileManager = services.getFileManager();
444 List<AbstractFile> metaFiles = fileManager.findFiles(sqliteFile.getDataSource(), metaFileName, sqliteFile.getParent().getName());
445 if (metaFiles != null) {
446 for (AbstractFile metaFile : metaFiles) {
447 String tmpMetafilePathName = openCase.getTempDirectory() + File.separator + metaFile.getName();
448 File tmpMetafile =
new File(tmpMetafilePathName);
449 ContentUtils.writeToFile(metaFile, tmpMetafile);
459 private Map<String, String> getTables() throws SQLException {
460 Map<String, String> dbTablesMap =
new TreeMap<>();
461 Statement statement = null;
462 ResultSet resultSet = null;
464 statement = connection.createStatement();
465 resultSet = statement.executeQuery(
466 "SELECT name, sql FROM sqlite_master "
467 +
" WHERE type= 'table' "
468 +
" ORDER BY name;");
469 while (resultSet.next()) {
470 String tableName = resultSet.getString(
"name");
471 String tableSQL = resultSet.getString(
"sql");
472 dbTablesMap.put(tableName, tableSQL);
475 if (null != resultSet) {
478 if (null != statement) {
485 @NbBundle.Messages({
"# {0} - tableName",
486 "SQLiteViewer.selectTable.errorText=Error getting row count for table: {0}"
488 private void selectTable(String tableName) {
490 try (Statement statement = connection.createStatement();
491 ResultSet resultSet = statement.executeQuery(
492 "SELECT count (*) as count FROM " + tableName)) {
494 numRows = resultSet.getInt(
"count");
495 numEntriesField.setText(numRows +
" entries");
498 currPageLabel.setText(Integer.toString(currPage));
499 numPagesLabel.setText(Integer.toString((numRows / ROWS_PER_PAGE) + 1));
501 prevPageButton.setEnabled(
false);
504 exportCsvButton.setEnabled(
true);
505 nextPageButton.setEnabled(((numRows > ROWS_PER_PAGE)));
506 readTable(tableName, (currPage - 1) * ROWS_PER_PAGE + 1, ROWS_PER_PAGE);
508 exportCsvButton.setEnabled(
false);
509 nextPageButton.setEnabled(
false);
510 selectedTableView.setupTable(Collections.emptyList());
513 }
catch (SQLException ex) {
514 logger.log(Level.SEVERE, String.format(
"Failed to load table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
515 MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_selectTable_errorText(tableName));
519 @NbBundle.Messages({
"# {0} - tableName",
520 "SQLiteViewer.readTable.errorText=Error getting rows for table: {0}"})
521 private void readTable(String tableName,
int startRow,
int numRowsToRead) {
524 Statement statement = connection.createStatement();
525 ResultSet resultSet = statement.executeQuery(
526 "SELECT * FROM " + tableName
527 +
" LIMIT " + Integer.toString(numRowsToRead)
528 +
" OFFSET " + Integer.toString(startRow - 1))) {
530 ArrayList<Map<String, Object>> rows = resultSetToArrayList(resultSet);
531 if (Objects.nonNull(rows)) {
532 selectedTableView.setupTable(rows);
534 selectedTableView.setupTable(Collections.emptyList());
536 }
catch (SQLException ex) {
537 logger.log(Level.SEVERE, String.format(
"Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
538 MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName));
542 @NbBundle.Messages(
"SQLiteViewer.BlobNotShown.message=BLOB Data not shown")
543 private ArrayList<Map<String, Object>> resultSetToArrayList(ResultSet rs)
throws SQLException {
544 ResultSetMetaData metaData = rs.getMetaData();
545 int columns = metaData.getColumnCount();
546 ArrayList<Map<String, Object>> rowlist =
new ArrayList<>();
548 Map<String, Object> row =
new LinkedHashMap<>(columns);
549 for (
int i = 1; i <= columns; ++i) {
550 if (rs.getObject(i) == null) {
551 row.put(metaData.getColumnName(i),
"");
553 if (metaData.getColumnTypeName(i).compareToIgnoreCase(
"blob") == 0) {
554 row.put(metaData.getColumnName(i), Bundle.SQLiteViewer_BlobNotShown_message());
556 row.put(metaData.getColumnName(i), rs.getObject(i));
566 @NbBundle.Messages({
"SQLiteViewer.exportTableToCsv.write.errText=Failed to export table content to csv file.",
567 "SQLiteViewer.exportTableToCsv.FileName=File name: ",
568 "SQLiteViewer.exportTableToCsv.TableName=Table name: "
570 private void exportTableToCsv(File file,
boolean overwrite) {
571 String tableName = (String) this.tablesDropdownList.getSelectedItem();
572 String csvFileSuffix =
"_" + tableName +
"_" + TimeStampUtils.createTimeStamp() +
".csv";
574 Statement statement = connection.createStatement();
575 ResultSet resultSet = statement.executeQuery(
"SELECT * FROM " + tableName)) {
576 List<Map<String, Object>> currentTableRows = resultSetToArrayList(resultSet);
578 if (Objects.isNull(currentTableRows) || currentTableRows.isEmpty()) {
579 logger.log(Level.INFO, String.format(
"The table %s is empty. (objId=%d)", tableName, sqliteDbFile.getId()));
581 String fileName = file.getName();
585 }
else if (FilenameUtils.getExtension(fileName).equalsIgnoreCase(
"csv")) {
586 csvFile =
new File(file.getParentFile(), FilenameUtils.removeExtension(fileName) + csvFileSuffix);
588 csvFile =
new File(file.toString() + csvFileSuffix);
590 FileOutputStream out =
new FileOutputStream(csvFile,
false);
592 out.write((Bundle.SQLiteViewer_exportTableToCsv_FileName() + csvFile.getName() +
"\n").getBytes());
593 out.write((Bundle.SQLiteViewer_exportTableToCsv_TableName() + tableName +
"\n").getBytes());
595 Map<String, Object> row = currentTableRows.get(0);
596 StringBuffer header =
new StringBuffer();
597 for (Map.Entry<String, Object> col : row.entrySet()) {
598 String colName = col.getKey();
599 if (header.length() > 0) {
600 header.append(
',').append(colName);
602 header.append(colName);
605 out.write(header.append(
'\n').toString().getBytes());
607 for (Map<String, Object> maps : currentTableRows) {
608 StringBuffer valueLine =
new StringBuffer();
609 maps.values().forEach((value) -> {
610 if (valueLine.length() > 0) {
611 valueLine.append(
',').append(value.toString());
613 valueLine.append(value.toString());
616 out.write(valueLine.append(
'\n').toString().getBytes());
619 }
catch (SQLException ex) {
620 logger.log(Level.SEVERE, String.format(
"Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
621 MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName));
622 }
catch (IOException ex) {
623 logger.log(Level.SEVERE, String.format(
"Failed to export table %s to file '%s'", tableName, file.getName()), ex);
624 MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_exportTableToCsv_write_errText());