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.Collection;
36 import java.util.Collections;
37 import java.util.LinkedHashMap;
38 import java.util.LinkedList;
39 import java.util.List;
41 import java.util.Objects;
42 import java.util.logging.Level;
43 import javax.swing.JComboBox;
44 import javax.swing.JFileChooser;
45 import javax.swing.JOptionPane;
46 import javax.swing.filechooser.FileNameExtensionFilter;
47 import org.apache.commons.io.FilenameUtils;
48 import org.openide.util.NbBundle;
49 import org.openide.windows.WindowManager;
61 @SuppressWarnings(
"PMD.SingularField")
62 class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
64 private static final long serialVersionUID = 1L;
65 public static final String[] SUPPORTED_MIMETYPES =
new String[]{
"application/x-sqlite3"};
66 private static final int ROWS_PER_PAGE = 100;
67 private static final Logger logger = Logger.getLogger(FileViewer.class.getName());
68 private final SQLiteTableView selectedTableView =
new SQLiteTableView();
69 private AbstractFile sqliteDbFile;
70 private File tmpDbFile;
71 private Connection connection;
73 private int currPage = 0;
78 public SQLiteViewer() {
80 jTableDataPanel.add(selectedTableView, BorderLayout.CENTER);
88 @SuppressWarnings(
"unchecked")
90 private
void initComponents() {
92 jHdrPanel =
new javax.swing.JPanel();
93 tablesDropdownList =
new javax.swing.JComboBox<>();
94 jLabel1 =
new javax.swing.JLabel();
95 numEntriesField =
new javax.swing.JTextField();
96 jLabel2 =
new javax.swing.JLabel();
97 currPageLabel =
new javax.swing.JLabel();
98 jLabel3 =
new javax.swing.JLabel();
99 numPagesLabel =
new javax.swing.JLabel();
100 prevPageButton =
new javax.swing.JButton();
101 nextPageButton =
new javax.swing.JButton();
102 exportCsvButton =
new javax.swing.JButton();
103 jTableDataPanel =
new javax.swing.JPanel();
105 jHdrPanel.setPreferredSize(
new java.awt.Dimension(536, 40));
107 tablesDropdownList.setModel(
new javax.swing.DefaultComboBoxModel<>(
new String[] {
"Item 1",
"Item 2",
"Item 3",
"Item 4" }));
108 tablesDropdownList.addActionListener(
new java.awt.event.ActionListener() {
109 public void actionPerformed(java.awt.event.ActionEvent evt) {
110 tablesDropdownListActionPerformed(evt);
114 org.openide.awt.Mnemonics.setLocalizedText(jLabel1,
org.openide.util.NbBundle.getMessage(SQLiteViewer.class,
"SQLiteViewer.jLabel1.text"));
116 numEntriesField.setEditable(
false);
117 numEntriesField.setText(
org.openide.util.NbBundle.getMessage(SQLiteViewer.class,
"SQLiteViewer.numEntriesField.text"));
118 numEntriesField.setBorder(null);
120 org.openide.awt.Mnemonics.setLocalizedText(jLabel2,
org.openide.util.NbBundle.getMessage(SQLiteViewer.class,
"SQLiteViewer.jLabel2.text"));
122 org.openide.awt.Mnemonics.setLocalizedText(currPageLabel,
org.openide.util.NbBundle.getMessage(SQLiteViewer.class,
"SQLiteViewer.currPageLabel.text"));
124 org.openide.awt.Mnemonics.setLocalizedText(jLabel3,
org.openide.util.NbBundle.getMessage(SQLiteViewer.class,
"SQLiteViewer.jLabel3.text"));
126 org.openide.awt.Mnemonics.setLocalizedText(numPagesLabel,
org.openide.util.NbBundle.getMessage(SQLiteViewer.class,
"SQLiteViewer.numPagesLabel.text"));
128 prevPageButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_back.png")));
129 org.openide.awt.Mnemonics.setLocalizedText(prevPageButton,
org.openide.util.NbBundle.getMessage(SQLiteViewer.class,
"SQLiteViewer.prevPageButton.text"));
130 prevPageButton.setBorderPainted(
false);
131 prevPageButton.setContentAreaFilled(
false);
132 prevPageButton.setDisabledSelectedIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png")));
133 prevPageButton.setMargin(
new java.awt.Insets(2, 0, 2, 0));
134 prevPageButton.setPreferredSize(
new java.awt.Dimension(23, 23));
135 prevPageButton.addActionListener(
new java.awt.event.ActionListener() {
136 public void actionPerformed(java.awt.event.ActionEvent evt) {
137 prevPageButtonActionPerformed(evt);
141 nextPageButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png")));
142 org.openide.awt.Mnemonics.setLocalizedText(nextPageButton,
org.openide.util.NbBundle.getMessage(SQLiteViewer.class,
"SQLiteViewer.nextPageButton.text"));
143 nextPageButton.setBorderPainted(
false);
144 nextPageButton.setContentAreaFilled(
false);
145 nextPageButton.setDisabledSelectedIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png")));
146 nextPageButton.setMargin(
new java.awt.Insets(2, 0, 2, 0));
147 nextPageButton.setPreferredSize(
new java.awt.Dimension(23, 23));
148 nextPageButton.addActionListener(
new java.awt.event.ActionListener() {
149 public void actionPerformed(java.awt.event.ActionEvent evt) {
150 nextPageButtonActionPerformed(evt);
154 org.openide.awt.Mnemonics.setLocalizedText(exportCsvButton,
org.openide.util.NbBundle.getMessage(SQLiteViewer.class,
"SQLiteViewer.exportCsvButton.text"));
155 exportCsvButton.addActionListener(
new java.awt.event.ActionListener() {
156 public void actionPerformed(java.awt.event.ActionEvent evt) {
157 exportCsvButtonActionPerformed(evt);
161 javax.swing.GroupLayout jHdrPanelLayout =
new javax.swing.GroupLayout(jHdrPanel);
162 jHdrPanel.setLayout(jHdrPanelLayout);
163 jHdrPanelLayout.setHorizontalGroup(
164 jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
165 .addGroup(jHdrPanelLayout.createSequentialGroup()
167 .addComponent(jLabel1)
168 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
169 .addComponent(tablesDropdownList, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
171 .addComponent(numEntriesField, javax.swing.GroupLayout.PREFERRED_SIZE, 71, javax.swing.GroupLayout.PREFERRED_SIZE)
173 .addComponent(jLabel2)
174 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
175 .addComponent(currPageLabel)
176 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
177 .addComponent(jLabel3)
178 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
179 .addComponent(numPagesLabel)
181 .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
183 .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
185 .addComponent(exportCsvButton)
186 .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
188 jHdrPanelLayout.setVerticalGroup(
189 jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
190 .addGroup(jHdrPanelLayout.createSequentialGroup()
192 .addGroup(jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
193 .addComponent(exportCsvButton)
194 .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
195 .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
196 .addGroup(jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
197 .addComponent(tablesDropdownList, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
198 .addComponent(jLabel1)
199 .addComponent(numEntriesField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
200 .addComponent(jLabel2)
201 .addComponent(currPageLabel)
202 .addComponent(jLabel3)
203 .addComponent(numPagesLabel)))
207 jTableDataPanel.setLayout(
new java.awt.BorderLayout());
209 javax.swing.GroupLayout layout =
new javax.swing.GroupLayout(
this);
210 this.setLayout(layout);
211 layout.setHorizontalGroup(
212 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
213 .addComponent(jHdrPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 569, Short.MAX_VALUE)
214 .addComponent(jTableDataPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
216 layout.setVerticalGroup(
217 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
218 .addGroup(layout.createSequentialGroup()
219 .addComponent(jHdrPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE)
221 .addComponent(jTableDataPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 317, Short.MAX_VALUE))
225 private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {
226 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
228 if (currPage * ROWS_PER_PAGE > numRows) {
229 nextPageButton.setEnabled(
false);
231 currPageLabel.setText(Integer.toString(currPage));
232 prevPageButton.setEnabled(
true);
235 String tableName = (String) this.tablesDropdownList.getSelectedItem();
236 readTable(tableName, (currPage - 1) * ROWS_PER_PAGE + 1, ROWS_PER_PAGE);
237 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
240 private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {
242 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
245 prevPageButton.setEnabled(
false);
247 currPageLabel.setText(Integer.toString(currPage));
248 nextPageButton.setEnabled(
true);
251 String tableName = (String) this.tablesDropdownList.getSelectedItem();
252 readTable(tableName, (currPage - 1) * ROWS_PER_PAGE + 1, ROWS_PER_PAGE);
253 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
256 private void tablesDropdownListActionPerformed(java.awt.event.ActionEvent evt) {
257 JComboBox<?> cb = (JComboBox<?>) evt.getSource();
258 String tableName = (String) cb.getSelectedItem();
259 if (null == tableName) {
262 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
263 selectTable(tableName);
264 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
274 @NbBundle.Messages({
"SQLiteViewer.csvExport.fileName.empty=Please input a file name for exporting.",
275 "SQLiteViewer.csvExport.title=Export to csv file",
276 "SQLiteViewer.csvExport.confirm.msg=Do you want to overwrite the existing file?"})
277 private void exportCsvButtonActionPerformed(java.awt.event.ActionEvent evt) {
278 Case openCase = Case.getCurrentCase();
279 File caseDirectory =
new File(openCase.getExportDirectory());
280 JFileChooser fileChooser =
new JFileChooser();
281 fileChooser.setDragEnabled(
false);
282 fileChooser.setCurrentDirectory(caseDirectory);
284 FileNameExtensionFilter csvFilter =
new FileNameExtensionFilter(
"*.csv",
"csv");
285 fileChooser.addChoosableFileFilter(csvFilter);
286 fileChooser.setAcceptAllFileFilterUsed(
true);
287 fileChooser.setFileFilter(csvFilter);
288 fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
289 String defaultFileName = (String) this.tablesDropdownList.getSelectedItem();
290 fileChooser.setSelectedFile(
new File(defaultFileName));
291 int choice = fileChooser.showSaveDialog((Component) evt.getSource());
292 if (JFileChooser.APPROVE_OPTION == choice) {
293 File file = fileChooser.getSelectedFile();
294 if (file.exists() && FilenameUtils.getExtension(file.getName()).equalsIgnoreCase(
"csv")) {
295 if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(
this,
296 Bundle.SQLiteViewer_csvExport_confirm_msg(),
297 Bundle.SQLiteViewer_csvExport_title(),
298 JOptionPane.YES_NO_OPTION)) {
304 exportTableToCsv(file);
309 private javax.swing.JLabel currPageLabel;
310 private javax.swing.JButton exportCsvButton;
311 private javax.swing.JPanel jHdrPanel;
312 private javax.swing.JLabel jLabel1;
313 private javax.swing.JLabel jLabel2;
314 private javax.swing.JLabel jLabel3;
315 private javax.swing.JPanel jTableDataPanel;
316 private javax.swing.JButton nextPageButton;
317 private javax.swing.JTextField numEntriesField;
318 private javax.swing.JLabel numPagesLabel;
319 private javax.swing.JButton prevPageButton;
320 private javax.swing.JComboBox<String> tablesDropdownList;
324 public List<String> getSupportedMIMETypes() {
325 return Arrays.asList(SUPPORTED_MIMETYPES);
329 public void setFile(AbstractFile file) {
330 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
333 WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
337 public Component getComponent() {
342 public void resetComponent() {
343 tablesDropdownList.setEnabled(
true);
344 tablesDropdownList.removeAllItems();
345 numEntriesField.setText(
"");
348 if (null != connection) {
352 }
catch (SQLException ex) {
353 logger.log(Level.SEVERE,
"Failed to close DB connection to file.", ex);
364 "SQLiteViewer.comboBox.noTableEntry=No tables found",
365 "SQLiteViewer.errorMessage.interrupted=The processing of the file was interrupted.",
366 "SQLiteViewer.errorMessage.noCurrentCase=The case has been closed.",
367 "SQLiteViewer.errorMessage.failedToExtractFile=The file could not be extracted from the data source.",
368 "SQLiteViewer.errorMessage.failedToQueryDatabase=The database tables in the file could not be read.",
369 "SQLiteViewer.errorMessage.failedToinitJDBCDriver=The JDBC driver for SQLite could not be loaded.",
370 "# {0} - exception message",
"SQLiteViewer.errorMessage.unexpectedError=An unexpected error occurred:\n{0).",})
371 private void processSQLiteFile() {
373 tablesDropdownList.removeAllItems();
376 String localDiskPath = SqliteUtil.writeAbstractFileToLocalDisk(sqliteDbFile);
377 SqliteUtil.findAndCopySQLiteMetaFile(sqliteDbFile);
379 Class.forName(
"org.sqlite.JDBC");
380 connection = DriverManager.getConnection(
"jdbc:sqlite:" + localDiskPath);
382 Collection<String> dbTablesMap = getTables();
383 if (dbTablesMap.isEmpty()) {
384 tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry());
385 tablesDropdownList.setEnabled(
false);
387 dbTablesMap.forEach((tableName) -> {
388 tablesDropdownList.addItem(tableName);
391 }
catch (ClassNotFoundException ex) {
392 logger.log(Level.SEVERE, String.format(
"Failed to initialize JDBC SQLite '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
393 MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToinitJDBCDriver());
394 }
catch (SQLException ex) {
395 logger.log(Level.SEVERE, String.format(
"Failed to get tables from DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
396 MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase());
397 }
catch (IOException | NoCurrentCaseException | TskCoreException ex) {
398 logger.log(Level.SEVERE, String.format(
"Failed to create temp copy of DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
399 MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToExtractFile());
408 private Collection<String> getTables() throws SQLException {
409 Collection<String> tableNames =
new LinkedList<>();
410 try (Statement statement = connection.createStatement();
411 ResultSet resultSet = statement.executeQuery(
412 "SELECT name FROM sqlite_master "
413 +
" WHERE type= 'table' ")){
414 while (resultSet.next()) {
415 tableNames.add(resultSet.getString(
"name"));
421 @NbBundle.Messages({
"# {0} - tableName",
422 "SQLiteViewer.selectTable.errorText=Error getting row count for table: {0}"
424 private void selectTable(String tableName) {
426 try (Statement statement = connection.createStatement();
427 ResultSet resultSet = statement.executeQuery(
428 "SELECT count (*) as count FROM " +
"\"" + tableName +
"\"")) {
430 numRows = resultSet.getInt(
"count");
431 numEntriesField.setText(numRows +
" entries");
434 currPageLabel.setText(Integer.toString(currPage));
435 numPagesLabel.setText(Integer.toString((numRows / ROWS_PER_PAGE) + 1));
437 prevPageButton.setEnabled(
false);
440 exportCsvButton.setEnabled(
true);
441 nextPageButton.setEnabled(((numRows > ROWS_PER_PAGE)));
442 readTable(tableName, (currPage - 1) * ROWS_PER_PAGE + 1, ROWS_PER_PAGE);
444 exportCsvButton.setEnabled(
false);
445 nextPageButton.setEnabled(
false);
446 selectedTableView.setupTable(Collections.emptyList());
449 }
catch (SQLException ex) {
450 logger.log(Level.SEVERE, String.format(
"Failed to load table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
451 MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_selectTable_errorText(tableName));
455 @NbBundle.Messages({
"# {0} - tableName",
456 "SQLiteViewer.readTable.errorText=Error getting rows for table: {0}"})
457 private void readTable(String tableName,
int startRow,
int numRowsToRead) {
460 Statement statement = connection.createStatement();
461 ResultSet resultSet = statement.executeQuery(
462 "SELECT * FROM " +
"\"" + tableName +
"\""
463 +
" LIMIT " + Integer.toString(numRowsToRead)
464 +
" OFFSET " + Integer.toString(startRow - 1))) {
466 List<Map<String, Object>> rows = resultSetToArrayList(resultSet);
467 if (Objects.nonNull(rows)) {
468 selectedTableView.setupTable(rows);
470 selectedTableView.setupTable(Collections.emptyList());
472 }
catch (SQLException ex) {
473 logger.log(Level.SEVERE, String.format(
"Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
474 MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName));
478 @NbBundle.Messages(
"SQLiteViewer.BlobNotShown.message=BLOB Data not shown")
479 private List<Map<String, Object>> resultSetToArrayList(ResultSet resultSet)
throws SQLException {
480 ResultSetMetaData metaData = resultSet.getMetaData();
481 int columns = metaData.getColumnCount();
482 ArrayList<Map<String, Object>> rowlist =
new ArrayList<>();
483 while (resultSet.next()) {
484 Map<String, Object> row =
new LinkedHashMap<>(columns);
485 for (
int i = 1; i <= columns; ++i) {
486 if (resultSet.getObject(i) == null) {
487 row.put(metaData.getColumnName(i),
"");
489 if (metaData.getColumnTypeName(i).compareToIgnoreCase(
"blob") == 0) {
490 row.put(metaData.getColumnName(i), Bundle.SQLiteViewer_BlobNotShown_message());
492 row.put(metaData.getColumnName(i), resultSet.getObject(i));
502 @NbBundle.Messages({
"SQLiteViewer.exportTableToCsv.write.errText=Failed to export table content to csv file.",
503 "SQLiteViewer.exportTableToCsv.FileName=File name: ",
504 "SQLiteViewer.exportTableToCsv.TableName=Table name: "
506 private void exportTableToCsv(File file) {
507 String tableName = (String) this.tablesDropdownList.getSelectedItem();
509 Statement statement = connection.createStatement();
510 ResultSet resultSet = statement.executeQuery(
"SELECT * FROM " +
"\"" + tableName +
"\"")) {
511 List<Map<String, Object>> currentTableRows = resultSetToArrayList(resultSet);
513 if (Objects.isNull(currentTableRows) || currentTableRows.isEmpty()) {
514 logger.log(Level.INFO, String.format(
"The table %s is empty. (objId=%d)", tableName, sqliteDbFile.getId()));
517 String fileName = file.getName();
518 if (FilenameUtils.getExtension(fileName).equalsIgnoreCase(
"csv")) {
521 csvFile =
new File(file.toString() +
".csv");
524 try (FileOutputStream out =
new FileOutputStream(csvFile,
false)) {
526 out.write((Bundle.SQLiteViewer_exportTableToCsv_FileName() + csvFile.getName() +
"\n").getBytes());
527 out.write((Bundle.SQLiteViewer_exportTableToCsv_TableName() + tableName +
"\n").getBytes());
529 Map<String, Object> row = currentTableRows.get(0);
530 StringBuffer header =
new StringBuffer();
531 for (Map.Entry<String, Object> col : row.entrySet()) {
532 String colName = col.getKey();
533 if (header.length() > 0) {
534 header.append(
',').append(colName);
536 header.append(colName);
539 out.write(header.append(
'\n').toString().getBytes());
541 for (Map<String, Object> maps : currentTableRows) {
542 StringBuffer valueLine =
new StringBuffer();
543 maps.values().forEach((value) -> {
544 if (valueLine.length() > 0) {
545 valueLine.append(
',').append(value.toString());
547 valueLine.append(value.toString());
550 out.write(valueLine.append(
'\n').toString().getBytes());
554 }
catch (SQLException ex) {
555 logger.log(Level.SEVERE, String.format(
"Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
556 MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName));
557 }
catch (IOException ex) {
558 logger.log(Level.SEVERE, String.format(
"Failed to export table %s to file '%s'", tableName, file.getName()), ex);
559 MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_exportTableToCsv_write_errText());