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.util.ArrayList;
 
   28 import java.util.Arrays;
 
   29 import java.util.Collection;
 
   30 import java.util.Collections;
 
   31 import java.util.LinkedHashMap;
 
   32 import java.util.List;
 
   34 import java.util.Objects;
 
   35 import java.util.function.Consumer;
 
   36 import java.util.logging.Level;
 
   37 import javax.swing.JComboBox;
 
   38 import javax.swing.JFileChooser;
 
   39 import javax.swing.JOptionPane;
 
   40 import javax.swing.filechooser.FileNameExtensionFilter;
 
   41 import org.apache.commons.io.FilenameUtils;
 
   42 import org.openide.util.NbBundle;
 
   43 import org.openide.windows.WindowManager;
 
   54 @SuppressWarnings(
"PMD.SingularField") 
 
   55 class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
 
   57     private static final long serialVersionUID = 1L;
 
   58     public static final String[] SUPPORTED_MIMETYPES = 
new String[]{
"application/x-sqlite3"};
 
   59     private static final int ROWS_PER_PAGE = 100;
 
   60     private static final Logger logger = Logger.getLogger(FileViewer.class.getName());
 
   61     private final SQLiteTableView selectedTableView = 
new SQLiteTableView();
 
   62     private AbstractFile sqliteDbFile;
 
   64     private SQLiteTableReader viewReader;
 
   66     private Map<String, Object> row = 
new LinkedHashMap<>();
 
   67     private List<Map<String, Object>> pageOfTableRows = 
new ArrayList<>();
 
   68     private List<String> currentTableHeader = 
new ArrayList<>();
 
   69     private String prevTableName;
 
   72     private int currPage = 0; 
 
   77     public SQLiteViewer() {
 
   79         jTableDataPanel.add(selectedTableView, BorderLayout.CENTER);
 
   87     @SuppressWarnings(
"unchecked")
 
   89     private 
void initComponents() {
 
   91         jHdrPanel = 
new javax.swing.JPanel();
 
   92         tablesDropdownList = 
new javax.swing.JComboBox<>();
 
   93         jLabel1 = 
new javax.swing.JLabel();
 
   94         numEntriesField = 
new javax.swing.JTextField();
 
   95         jLabel2 = 
new javax.swing.JLabel();
 
   96         currPageLabel = 
new javax.swing.JLabel();
 
   97         jLabel3 = 
new javax.swing.JLabel();
 
   98         numPagesLabel = 
new javax.swing.JLabel();
 
   99         prevPageButton = 
new javax.swing.JButton();
 
  100         nextPageButton = 
new javax.swing.JButton();
 
  101         exportCsvButton = 
new javax.swing.JButton();
 
  102         jTableDataPanel = 
new javax.swing.JPanel();
 
  104         jHdrPanel.setPreferredSize(
new java.awt.Dimension(536, 40));
 
  106         tablesDropdownList.setModel(
new javax.swing.DefaultComboBoxModel<>(
new String[] { 
"Item 1", 
"Item 2", 
"Item 3", 
"Item 4" }));
 
  107         tablesDropdownList.addActionListener(
new java.awt.event.ActionListener() {
 
  108             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
  109                 tablesDropdownListActionPerformed(evt);
 
  113         org.openide.awt.Mnemonics.setLocalizedText(jLabel1, 
org.openide.util.NbBundle.getMessage(SQLiteViewer.class, 
"SQLiteViewer.jLabel1.text")); 
 
  115         numEntriesField.setEditable(
false);
 
  116         numEntriesField.setText(
org.openide.util.NbBundle.getMessage(SQLiteViewer.class, 
"SQLiteViewer.numEntriesField.text")); 
 
  117         numEntriesField.setBorder(null);
 
  119         org.openide.awt.Mnemonics.setLocalizedText(jLabel2, 
org.openide.util.NbBundle.getMessage(SQLiteViewer.class, 
"SQLiteViewer.jLabel2.text")); 
 
  121         org.openide.awt.Mnemonics.setLocalizedText(currPageLabel, 
org.openide.util.NbBundle.getMessage(SQLiteViewer.class, 
"SQLiteViewer.currPageLabel.text")); 
 
  123         org.openide.awt.Mnemonics.setLocalizedText(jLabel3, 
org.openide.util.NbBundle.getMessage(SQLiteViewer.class, 
"SQLiteViewer.jLabel3.text")); 
 
  125         org.openide.awt.Mnemonics.setLocalizedText(numPagesLabel, 
org.openide.util.NbBundle.getMessage(SQLiteViewer.class, 
"SQLiteViewer.numPagesLabel.text")); 
 
  127         prevPageButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); 
 
  128         org.openide.awt.Mnemonics.setLocalizedText(prevPageButton, 
org.openide.util.NbBundle.getMessage(SQLiteViewer.class, 
"SQLiteViewer.prevPageButton.text")); 
 
  129         prevPageButton.setBorderPainted(
false);
 
  130         prevPageButton.setContentAreaFilled(
false);
 
  131         prevPageButton.setDisabledSelectedIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); 
 
  132         prevPageButton.setMargin(
new java.awt.Insets(2, 0, 2, 0));
 
  133         prevPageButton.setPreferredSize(
new java.awt.Dimension(23, 23));
 
  134         prevPageButton.addActionListener(
new java.awt.event.ActionListener() {
 
  135             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
  136                 prevPageButtonActionPerformed(evt);
 
  140         nextPageButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); 
 
  141         org.openide.awt.Mnemonics.setLocalizedText(nextPageButton, 
org.openide.util.NbBundle.getMessage(SQLiteViewer.class, 
"SQLiteViewer.nextPageButton.text")); 
 
  142         nextPageButton.setBorderPainted(
false);
 
  143         nextPageButton.setContentAreaFilled(
false);
 
  144         nextPageButton.setDisabledSelectedIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); 
 
  145         nextPageButton.setMargin(
new java.awt.Insets(2, 0, 2, 0));
 
  146         nextPageButton.setPreferredSize(
new java.awt.Dimension(23, 23));
 
  147         nextPageButton.addActionListener(
new java.awt.event.ActionListener() {
 
  148             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
  149                 nextPageButtonActionPerformed(evt);
 
  153         org.openide.awt.Mnemonics.setLocalizedText(exportCsvButton, 
org.openide.util.NbBundle.getMessage(SQLiteViewer.class, 
"SQLiteViewer.exportCsvButton.text")); 
 
  154         exportCsvButton.addActionListener(
new java.awt.event.ActionListener() {
 
  155             public void actionPerformed(java.awt.event.ActionEvent evt) {
 
  156                 exportCsvButtonActionPerformed(evt);
 
  160         javax.swing.GroupLayout jHdrPanelLayout = 
new javax.swing.GroupLayout(jHdrPanel);
 
  161         jHdrPanel.setLayout(jHdrPanelLayout);
 
  162         jHdrPanelLayout.setHorizontalGroup(
 
  163             jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  164             .addGroup(jHdrPanelLayout.createSequentialGroup()
 
  166                 .addComponent(jLabel1)
 
  167                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
 
  168                 .addComponent(tablesDropdownList, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
 
  170                 .addComponent(numEntriesField, javax.swing.GroupLayout.PREFERRED_SIZE, 71, javax.swing.GroupLayout.PREFERRED_SIZE)
 
  172                 .addComponent(jLabel2)
 
  173                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
 
  174                 .addComponent(currPageLabel)
 
  175                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
 
  176                 .addComponent(jLabel3)
 
  177                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
 
  178                 .addComponent(numPagesLabel)
 
  180                 .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
 
  182                 .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
 
  184                 .addComponent(exportCsvButton)
 
  185                 .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
 
  187         jHdrPanelLayout.setVerticalGroup(
 
  188             jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  189             .addGroup(jHdrPanelLayout.createSequentialGroup()
 
  191                 .addGroup(jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  192                     .addComponent(exportCsvButton)
 
  193                     .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
 
  194                     .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
 
  195                     .addGroup(jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
 
  196                         .addComponent(tablesDropdownList, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
 
  197                         .addComponent(jLabel1)
 
  198                         .addComponent(numEntriesField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
 
  199                         .addComponent(jLabel2)
 
  200                         .addComponent(currPageLabel)
 
  201                         .addComponent(jLabel3)
 
  202                         .addComponent(numPagesLabel)))
 
  206         jTableDataPanel.setLayout(
new java.awt.BorderLayout());
 
  208         javax.swing.GroupLayout layout = 
new javax.swing.GroupLayout(
this);
 
  209         this.setLayout(layout);
 
  210         layout.setHorizontalGroup(
 
  211             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  212             .addComponent(jHdrPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 569, Short.MAX_VALUE)
 
  213             .addComponent(jTableDataPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
 
  215         layout.setVerticalGroup(
 
  216             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  217             .addGroup(layout.createSequentialGroup()
 
  218                 .addComponent(jHdrPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE)
 
  220                 .addComponent(jTableDataPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 317, Short.MAX_VALUE))
 
  224     private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {
 
  225         WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
 
  227         if (currPage * ROWS_PER_PAGE > numRows) {
 
  228             nextPageButton.setEnabled(
false);
 
  230         currPageLabel.setText(Integer.toString(currPage));
 
  231         prevPageButton.setEnabled(
true);
 
  234         String tableName = (String) this.tablesDropdownList.getSelectedItem();
 
  235         readTable(tableName, (currPage - 1) * ROWS_PER_PAGE + 1, ROWS_PER_PAGE);
 
  236         WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 
  239     private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {
 
  241         WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
 
  244             prevPageButton.setEnabled(
false);
 
  246         currPageLabel.setText(Integer.toString(currPage));
 
  247         nextPageButton.setEnabled(
true);
 
  250         String tableName = (String) this.tablesDropdownList.getSelectedItem();
 
  251         readTable(tableName, (currPage - 1) * ROWS_PER_PAGE + 1, ROWS_PER_PAGE);
 
  252         WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 
  255     private void tablesDropdownListActionPerformed(java.awt.event.ActionEvent evt) {
 
  256         JComboBox<?> cb = (JComboBox<?>) evt.getSource();
 
  257         String tableName = (String) cb.getSelectedItem();
 
  258         if (null == tableName) {
 
  261         WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
 
  262         selectTable(tableName);
 
  263         WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 
  273     @NbBundle.Messages({
"SQLiteViewer.csvExport.fileName.empty=Please input a file name for exporting.",
 
  274         "SQLiteViewer.csvExport.title=Export to csv file",
 
  275         "SQLiteViewer.csvExport.confirm.msg=Do you want to overwrite the existing file?"})
 
  276     private void exportCsvButtonActionPerformed(java.awt.event.ActionEvent evt) {
 
  277         Case openCase = Case.getCurrentCase();
 
  278         File caseDirectory = 
new File(openCase.getExportDirectory());
 
  279         JFileChooser fileChooser = 
new JFileChooser();
 
  280         fileChooser.setDragEnabled(
false);
 
  281         fileChooser.setCurrentDirectory(caseDirectory);
 
  283         FileNameExtensionFilter csvFilter = 
new FileNameExtensionFilter(
"*.csv", 
"csv");
 
  284         fileChooser.addChoosableFileFilter(csvFilter);
 
  285         fileChooser.setAcceptAllFileFilterUsed(
true);
 
  286         fileChooser.setFileFilter(csvFilter);
 
  287         fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
 
  288         String defaultFileName = (String) this.tablesDropdownList.getSelectedItem();
 
  289         fileChooser.setSelectedFile(
new File(defaultFileName));
 
  290         int choice = fileChooser.showSaveDialog((Component) evt.getSource()); 
 
  291         if (JFileChooser.APPROVE_OPTION == choice) {
 
  292             File file = fileChooser.getSelectedFile();
 
  293             if (file.exists() && FilenameUtils.getExtension(file.getName()).equalsIgnoreCase(
"csv")) {
 
  294                 if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(
this,
 
  295                         Bundle.SQLiteViewer_csvExport_confirm_msg(),
 
  296                         Bundle.SQLiteViewer_csvExport_title(),
 
  297                         JOptionPane.YES_NO_OPTION)) {
 
  303             exportTableToCsv(file);
 
  308     private javax.swing.JLabel currPageLabel;
 
  309     private javax.swing.JButton exportCsvButton;
 
  310     private javax.swing.JPanel jHdrPanel;
 
  311     private javax.swing.JLabel jLabel1;
 
  312     private javax.swing.JLabel jLabel2;
 
  313     private javax.swing.JLabel jLabel3;
 
  314     private javax.swing.JPanel jTableDataPanel;
 
  315     private javax.swing.JButton nextPageButton;
 
  316     private javax.swing.JTextField numEntriesField;
 
  317     private javax.swing.JLabel numPagesLabel;
 
  318     private javax.swing.JButton prevPageButton;
 
  319     private javax.swing.JComboBox<String> tablesDropdownList;
 
  323     public List<String> getSupportedMIMETypes() {
 
  324         return Arrays.asList(SUPPORTED_MIMETYPES);
 
  328     public void setFile(AbstractFile file) {
 
  329         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(
"");
 
  349         } 
catch (SQLiteTableReaderException ex) {
 
  352         row = 
new LinkedHashMap<>();
 
  353         pageOfTableRows = 
new ArrayList<>();
 
  354         currentTableHeader = 
new ArrayList<>();
 
  363         "SQLiteViewer.comboBox.noTableEntry=No tables found",
 
  364         "SQLiteViewer.errorMessage.interrupted=The processing of the file was interrupted.",
 
  365         "SQLiteViewer.errorMessage.noCurrentCase=The case has been closed.",
 
  366         "SQLiteViewer.errorMessage.failedToExtractFile=The file could not be extracted from the data source.",
 
  367         "SQLiteViewer.errorMessage.failedToQueryDatabase=The database tables in the file could not be read.",
 
  368         "SQLiteViewer.errorMessage.failedToinitJDBCDriver=The JDBC driver for SQLite could not be loaded.",
 
  369         "# {0} - exception message", 
"SQLiteViewer.errorMessage.unexpectedError=An unexpected error occurred:\n{0).",})
 
  370     private void processSQLiteFile() {
 
  372             tablesDropdownList.removeAllItems();
 
  374             Collection<String> dbTablesMap = viewReader.getTableNames();
 
  375             if (dbTablesMap.isEmpty()) {
 
  376                 tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry());
 
  377                 tablesDropdownList.setEnabled(
false);
 
  379                 dbTablesMap.forEach((tableName) -> {
 
  380                     tablesDropdownList.addItem(tableName);
 
  383         } 
catch (SQLiteTableReaderException ex) {
 
  384             logger.log(Level.WARNING, String.format(
"Unable to get table names " 
  385                     + 
"from sqlite file [%s] with id=[%d].", sqliteDbFile.getName(),
 
  386                     sqliteDbFile.getId(), ex.getMessage()));
 
  387             MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase());
 
  391     @NbBundle.Messages({
"# {0} - tableName",
 
  392         "SQLiteViewer.selectTable.errorText=Error getting row count for table: {0}" 
  394     private void selectTable(String tableName) {
 
  396             numRows = viewReader.getRowCount(tableName);
 
  397             numEntriesField.setText(numRows + 
" entries");
 
  400             currPageLabel.setText(Integer.toString(currPage));
 
  401             numPagesLabel.setText(Integer.toString((numRows / ROWS_PER_PAGE) + 1));
 
  403             prevPageButton.setEnabled(
false);
 
  406                 exportCsvButton.setEnabled(
true);
 
  407                 nextPageButton.setEnabled(((numRows > ROWS_PER_PAGE)));
 
  408                 readTable(tableName, (currPage - 1) * ROWS_PER_PAGE + 1, ROWS_PER_PAGE);
 
  410                 exportCsvButton.setEnabled(
false);
 
  411                 nextPageButton.setEnabled(
false);
 
  413                 currentTableHeader = 
new ArrayList<>();
 
  414                 viewReader.read(tableName);
 
  415                 Map<String, Object> columnRow = 
new LinkedHashMap<>();
 
  416                 for(
int i = 0; i< currentTableHeader.size(); i++){
 
  417                   columnRow.put(currentTableHeader.get(i), 
"");
 
  419                 selectedTableView.setupTable(Collections.singletonList(columnRow));
 
  421         } 
catch (SQLiteTableReaderException ex) {
 
  422             logger.log(Level.WARNING, String.format(
"Failed to load table %s "  
  423                     + 
"from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), 
 
  424                     sqliteDbFile.getId()), ex.getMessage());
 
  425             MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_selectTable_errorText(tableName));
 
  429     @NbBundle.Messages({
"# {0} - tableName",
 
  430         "SQLiteViewer.readTable.errorText=Error getting rows for table: {0}"})
 
  431     private void readTable(String tableName, 
int startRow, 
int numRowsToRead) {
 
  436             if (!tableName.equals(prevTableName)) {
 
  437                 prevTableName = tableName;
 
  439             currentTableHeader = 
new ArrayList<>();
 
  440             viewReader.read(tableName, numRowsToRead, startRow - 1);
 
  441             selectedTableView.setupTable(pageOfTableRows);
 
  442             pageOfTableRows = 
new ArrayList<>();
 
  443         } 
catch (SQLiteTableReaderException ex) {
 
  444             logger.log(Level.WARNING, String.format(
"Failed to read table %s from DB file '%s' "  
  445                     + 
"(objId=%d) starting at row [%d] and limit [%d]", 
 
  446                     tableName, sqliteDbFile.getName(), sqliteDbFile.getId(),
 
  447                     startRow - 1, numRowsToRead), ex.getMessage());
 
  448             MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName));
 
  458     private void initReader() {
 
  459         viewReader = 
new SQLiteTableReader.Builder(sqliteDbFile)
 
  460                 .forAllColumnNames((columnName) -> {
 
  461                     currentTableHeader.add(columnName);
 
  463                 .forAllTableValues(getForAllStrategy()).build();
 
  478     private Consumer<Object> getForAllStrategy() {
 
  479         return new Consumer<Object>() {
 
  480             private int rowIndex = 0;
 
  483             public void accept(Object t) {
 
  485                 String objectStr = (t instanceof byte[]) ? 
"BLOB Data not shown" 
  486                         : Objects.toString(t, 
"");
 
  488                 row.put(currentTableHeader.get(rowIndex - 1), objectStr);
 
  492                 if (rowIndex == currentTableHeader.size()) {
 
  493                     pageOfTableRows.add(row);
 
  494                     row = 
new LinkedHashMap<>();
 
  496                 rowIndex %= currentTableHeader.size();
 
  502     private int totalColumnCount;
 
  504     @NbBundle.Messages({
"SQLiteViewer.exportTableToCsv.write.errText=Failed to export table content to csv file.",
 
  505         "SQLiteViewer.exportTableToCsv.FileName=File name: ",
 
  506         "SQLiteViewer.exportTableToCsv.TableName=Table name: " 
  508     private void exportTableToCsv(File file) {
 
  509         File csvFile = 
new File(file.toString() + 
".csv");
 
  510         String tableName = (String) this.tablesDropdownList.getSelectedItem();
 
  511         try (FileOutputStream out = 
new FileOutputStream(csvFile, 
false)) {
 
  512             try (SQLiteTableReader sqliteStream = 
new SQLiteTableReader.Builder(sqliteDbFile)
 
  513                     .forAllColumnNames(getColumnNameCSVStrategy(out))
 
  514                     .forAllTableValues(getForAllCSVStrategy(out)).build()) {
 
  515                 totalColumnCount = sqliteStream.getColumnCount(tableName);
 
  516                 sqliteStream.read(tableName);
 
  518         } 
catch (IOException | SQLiteTableReaderException | RuntimeException ex) {
 
  519             logger.log(Level.WARNING, String.format(
"Failed to export table [%s]" 
  520                     + 
" to CSV in sqlite file '%s' (objId=%d)", tableName, sqliteDbFile.getName(),
 
  521                     sqliteDbFile.getId()), ex.getMessage()); 
 
  522             MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_exportTableToCsv_write_errText());
 
  540     private Consumer<String> getColumnNameCSVStrategy(FileOutputStream out) {
 
  541         return new Consumer<String>() {
 
  542             private int columnIndex = 0;
 
  545             public void accept(String columnName) {
 
  549                 if (columnIndex == 1) {
 
  550                     columnName = 
"\"" + columnName + 
"\"";
 
  552                     columnName = 
",\"" + columnName + 
"\"";
 
  554                 if (columnIndex == totalColumnCount) {
 
  559                     out.write(columnName.getBytes());
 
  560                 } 
catch (IOException ex) {
 
  566                     throw new RuntimeException(ex);
 
  586     private Consumer<Object> getForAllCSVStrategy(FileOutputStream out) {
 
  587         return new Consumer<Object>() {
 
  588             private int rowIndex = 0;
 
  591             public void accept(Object tableValue) {
 
  595                 String objectStr = (tableValue instanceof byte[])
 
  596                         ? 
"BLOB Data not shown" : Objects.toString(tableValue, 
"");
 
  597                 objectStr = 
"\"" + objectStr + 
"\"";
 
  600                     objectStr = 
"," + objectStr;
 
  602                 if (rowIndex == totalColumnCount) {
 
  607                     out.write(objectStr.getBytes());
 
  608                 } 
catch (IOException ex) {
 
  614                     throw new RuntimeException(ex);
 
  616                 rowIndex = rowIndex % totalColumnCount;