From c4bb6dba614753e83cc87c20ca3f67729a0d0ec3 Mon Sep 17 00:00:00 2001 From: Raman Date: Fri, 19 Jan 2018 13:01:08 -0500 Subject: [PATCH 1/7] 3415: Show SQLite table contents - First cut. --- .../autopsy/contentviewers/Bundle.properties | 8 +- .../contentviewers/SQLiteTableRowFactory.java | 89 ++++++ .../contentviewers/SQLiteTableView.form | 34 ++ .../contentviewers/SQLiteTableView.java | 143 +++++++++ .../autopsy/contentviewers/SQLiteViewer.form | 189 +++++++---- .../autopsy/contentviewers/SQLiteViewer.java | 299 +++++++++++++----- 6 files changed, 619 insertions(+), 143 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableRowFactory.java create mode 100644 Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.form create mode 100644 Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties index 8267587395..68e938a107 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties @@ -35,5 +35,11 @@ MessageContentViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments MessageContentViewer.viewInNewWindowButton.text=View in New Window JPEGViewerDummy.jLabel1.text=You are looking at a JPEG file: JPEGViewerDummy.jTextField1.text=jTextField1 -SQLiteViewer.jLabel1.text=Table +SQLiteViewer.nextPageButton.text= +SQLiteViewer.prevPageButton.text= +SQLiteViewer.numPagesLabel.text=N +SQLiteViewer.jLabel3.text=of +SQLiteViewer.currPageLabel.text=x +SQLiteViewer.jLabel2.text=Page SQLiteViewer.numEntriesField.text=num Entries +SQLiteViewer.jLabel1.text=Table diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableRowFactory.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableRowFactory.java new file mode 100644 index 0000000000..98b21149ce --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableRowFactory.java @@ -0,0 +1,89 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.contentviewers; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.ChildFactory; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.openide.nodes.Sheet; +import org.sleuthkit.autopsy.datamodel.NodeProperty; + +public class SQLiteTableRowFactory extends ChildFactory { + + private final ArrayList> rows; + + public SQLiteTableRowFactory(ArrayList> rows) { + this.rows = rows; + } + + @Override + protected boolean createKeys(List keys) { + if (rows != null) { + for (int i = 0; i < rows.size(); i++) { + keys.add(i); + } + } + return true; + } + + @Override + protected Node createNodeForKey(Integer key) { + if (Objects.isNull(rows) || rows.isEmpty() || key >= rows.size()) { + return null; + } + + return new SQLiteTableRowNode(rows.get(key)); + } + +} + +class SQLiteTableRowNode extends AbstractNode { + + private final Map row; + + public SQLiteTableRowNode(Map row) { + super(Children.LEAF); + this.row = row; + } + + @Override + protected Sheet createSheet() { + + Sheet s = super.createSheet(); + Sheet.Set properties = s.get(Sheet.PROPERTIES); + if (properties == null) { + properties = Sheet.createPropertiesSet(); + s.put(properties); + } + + for (Map.Entry col : row.entrySet()) { + String colName = col.getKey(); + String colVal = col.getValue().toString(); + + properties.put(new NodeProperty<>(colName, colName, colName, colVal)); // NON-NLS + } + + return s; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.form b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.form new file mode 100644 index 0000000000..a34fd71d16 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.form @@ -0,0 +1,34 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java new file mode 100644 index 0000000000..b2fdc912d5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java @@ -0,0 +1,143 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.contentviewers; + +import java.util.ArrayList; +import java.util.Map; +import java.util.Objects; +import javax.swing.ListSelectionModel; +import javax.swing.table.TableColumnModel; +import org.netbeans.swing.etable.ETableColumn; +import org.netbeans.swing.etable.ETableColumnModel; +import org.netbeans.swing.outline.Outline; +import org.openide.explorer.ExplorerManager; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; + + +public class SQLiteTableView extends javax.swing.JPanel implements ExplorerManager.Provider { + + private final Outline outline; + private final org.openide.explorer.view.OutlineView outlineView; + private ExplorerManager explorerManager; + + private final ArrayList> tableRows; + + /** + * Creates new form SQLiteTableView + * @param rows + */ + public SQLiteTableView(ArrayList> rows) { + + this.tableRows = rows; + + outlineView = new org.openide.explorer.view.OutlineView(); + + initComponents(); + + outline = outlineView.getOutline(); + outlineView.setPropertyColumns(); // column headers will be set later + outline.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + customize(); + } + + private void customize() { + + tableScrollPane.setViewportView(outlineView); + this.setVisible(true); + outline.setRowSelectionAllowed(false); + outline.setRootVisible(false); + + explorerManager = new ExplorerManager(); + explorerManager.setRootContext(new AbstractNode(Children.create(new SQLiteTableRowFactory(tableRows), true))); + + setupColumns(); + } + + /** + * Sets up the columns in the display table + */ + private void setupColumns() { + if (Objects.isNull(tableRows) || tableRows.isEmpty()) + return; + + Map row = tableRows.get(0); + + // Get the columns setup with respect to names and sortability + String[] propStrings = new String[row.size() * 2]; + + int i = 0; + for (Map.Entry col: row.entrySet()) { + String colName = col.getKey(); + propStrings[2 * i] = colName; + propStrings[2 * i + 1] = colName; + i++; + } + + + outlineView.setPropertyColumns(propStrings); + + // RAMAN TBD: Set width based on actual data in the top N rows?? + // TBD: Cant seem to geta horizontal scroll bar + for (int col = 0; col< outline.getModel().getColumnCount(); col++) { + outline.getColumnModel().getColumn(col).setMinWidth(50); + } + + // Hide the 'Nodes' column + TableColumnModel columnModel = outline.getColumnModel(); + ETableColumn column = (ETableColumn) columnModel.getColumn(0); + ((ETableColumnModel) columnModel).setColumnHidden(column, true); + + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + tableScrollPane = new javax.swing.JScrollPane(); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) + ); + }// //GEN-END:initComponents + + + + @Override + public ExplorerManager getExplorerManager() { + return explorerManager; + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JScrollPane tableScrollPane; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.form index 3cdee8658c..121a96c823 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.form @@ -1,6 +1,6 @@ -
+ @@ -16,22 +16,27 @@ - - + + - - - + + + + + + + + @@ -39,11 +44,23 @@ - + - + - + + + + + + + + + + + + + @@ -55,8 +72,14 @@ + + + + + + - + @@ -97,57 +120,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
-
-
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index fff2f5d781..a60037fad1 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -1,21 +1,4 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2018 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ + package org.sleuthkit.autopsy.contentviewers; import java.awt.Component; @@ -27,12 +10,17 @@ import java.util.logging.Level; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.Statement; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.Map; +import java.util.Objects; import java.util.TreeMap; import java.util.concurrent.ExecutionException; import javax.swing.JComboBox; +import javax.swing.JPanel; import javax.swing.SwingWorker; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; @@ -44,20 +32,21 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { public static final String[] SUPPORTED_MIMETYPES = new String[]{"application/x-sqlite3"}; private static final Logger LOGGER = Logger.getLogger(FileViewer.class.getName()); private Connection connection = null; - + private String tmpDBPathName = null; private File tmpDBFile = null; - // TBD: Change the value to be a Array of ColDefs Map dbTablesMap = new TreeMap<>(); + private static final int ROWS_PER_PAGE = 100; + private int numRows; // num of rows in the selected table + private int currPage = 0; // curr page of rows being displayed + /** * Creates new form SQLiteViewer */ public SQLiteViewer() { initComponents(); - - customizeComponents(); } /** @@ -73,9 +62,15 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { tablesDropdownList = new javax.swing.JComboBox<>(); jLabel1 = new javax.swing.JLabel(); numEntriesField = new javax.swing.JTextField(); + jLabel2 = new javax.swing.JLabel(); + currPageLabel = new javax.swing.JLabel(); + jLabel3 = new javax.swing.JLabel(); + numPagesLabel = new javax.swing.JLabel(); + prevPageButton = new javax.swing.JButton(); + nextPageButton = new javax.swing.JButton(); jTableDataPanel = new javax.swing.JPanel(); - jScrollPane1 = new javax.swing.JScrollPane(); - jTable1 = new javax.swing.JTable(); + + jHdrPanel.setPreferredSize(new java.awt.Dimension(536, 40)); tablesDropdownList.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); tablesDropdownList.addActionListener(new java.awt.event.ActionListener() { @@ -89,9 +84,38 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { numEntriesField.setEditable(false); numEntriesField.setText(org.openide.util.NbBundle.getMessage(SQLiteViewer.class, "SQLiteViewer.numEntriesField.text")); // NOI18N numEntriesField.setBorder(null); - numEntriesField.addActionListener(new java.awt.event.ActionListener() { + + org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(SQLiteViewer.class, "SQLiteViewer.jLabel2.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(currPageLabel, org.openide.util.NbBundle.getMessage(SQLiteViewer.class, "SQLiteViewer.currPageLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(SQLiteViewer.class, "SQLiteViewer.jLabel3.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(numPagesLabel, org.openide.util.NbBundle.getMessage(SQLiteViewer.class, "SQLiteViewer.numPagesLabel.text")); // NOI18N + + prevPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(prevPageButton, org.openide.util.NbBundle.getMessage(SQLiteViewer.class, "SQLiteViewer.prevPageButton.text")); // NOI18N + prevPageButton.setBorderPainted(false); + prevPageButton.setContentAreaFilled(false); + prevPageButton.setDisabledSelectedIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N + prevPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0)); + prevPageButton.setPreferredSize(new java.awt.Dimension(23, 23)); + prevPageButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - numEntriesFieldActionPerformed(evt); + prevPageButtonActionPerformed(evt); + } + }); + + nextPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(nextPageButton, org.openide.util.NbBundle.getMessage(SQLiteViewer.class, "SQLiteViewer.nextPageButton.text")); // NOI18N + nextPageButton.setBorderPainted(false); + nextPageButton.setContentAreaFilled(false); + nextPageButton.setDisabledSelectedIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N + nextPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0)); + nextPageButton.setPreferredSize(new java.awt.Dimension(23, 23)); + nextPageButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + nextPageButtonActionPerformed(evt); } }); @@ -102,11 +126,23 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { .addGroup(jHdrPanelLayout.createSequentialGroup() .addContainerGap() .addComponent(jLabel1) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(tablesDropdownList, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(23, 23, 23) + .addGap(18, 18, 18) .addComponent(numEntriesField, javax.swing.GroupLayout.PREFERRED_SIZE, 71, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(130, Short.MAX_VALUE)) + .addGap(15, 15, 15) + .addComponent(jLabel2) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(currPageLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel3) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(numPagesLabel) + .addGap(18, 18, 18) + .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, 0) + .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(133, Short.MAX_VALUE)) ); jHdrPanelLayout.setVerticalGroup( jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -115,40 +151,18 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { .addGroup(jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(tablesDropdownList, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jLabel1) - .addComponent(numEntriesField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap(16, Short.MAX_VALUE)) - ); - - jTable1.setModel(new javax.swing.table.DefaultTableModel( - new Object [][] { - {null, null, null, null}, - {null, null, null, null}, - {null, null, null, null}, - {null, null, null, null} - }, - new String [] { - "Title 1", "Title 2", "Title 3", "Title 4" - } - )); - jScrollPane1.setViewportView(jTable1); - - javax.swing.GroupLayout jTableDataPanelLayout = new javax.swing.GroupLayout(jTableDataPanel); - jTableDataPanel.setLayout(jTableDataPanelLayout); - jTableDataPanelLayout.setHorizontalGroup( - jTableDataPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jTableDataPanelLayout.createSequentialGroup() - .addContainerGap() - .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) - .addGap(15, 15, 15)) - ); - jTableDataPanelLayout.setVerticalGroup( - jTableDataPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jTableDataPanelLayout.createSequentialGroup() - .addContainerGap() - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 275, Short.MAX_VALUE) + .addComponent(numEntriesField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel2) + .addComponent(currPageLabel) + .addComponent(jLabel3) + .addComponent(numPagesLabel) + .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)) .addContainerGap()) ); + jTableDataPanel.setLayout(new javax.swing.OverlayLayout(jTableDataPanel)); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -159,15 +173,39 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addComponent(jHdrPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jTableDataPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(jHdrPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, 0) + .addComponent(jTableDataPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 317, Short.MAX_VALUE)) ); }// //GEN-END:initComponents - private void numEntriesFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_numEntriesFieldActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_numEntriesFieldActionPerformed + private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed + + currPage++; + if (currPage * ROWS_PER_PAGE > numRows) { + nextPageButton.setEnabled(false); + } + currPageLabel.setText(Integer.toString(currPage)); + prevPageButton.setEnabled(true); + + // read and display a page of rows + String tableName = (String) this.tablesDropdownList.getSelectedItem(); + readTable(tableName, (currPage - 1) * ROWS_PER_PAGE + 1, ROWS_PER_PAGE); + }//GEN-LAST:event_nextPageButtonActionPerformed + + private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed + + currPage--; + if (currPage == 1) { + prevPageButton.setEnabled(false); + } + currPageLabel.setText(Integer.toString(currPage)); + nextPageButton.setEnabled(true); + + // read and display a page of rows + String tableName = (String) this.tablesDropdownList.getSelectedItem(); + readTable(tableName, (currPage - 1) * ROWS_PER_PAGE + 1, ROWS_PER_PAGE); + }//GEN-LAST:event_prevPageButtonActionPerformed private void tablesDropdownListActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tablesDropdownListActionPerformed JComboBox cb = (JComboBox) evt.getSource(); @@ -176,17 +214,21 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { return; } - readTable(tableName); + selectTable(tableName); }//GEN-LAST:event_tablesDropdownListActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel currPageLabel; private javax.swing.JPanel jHdrPanel; private javax.swing.JLabel jLabel1; - private javax.swing.JScrollPane jScrollPane1; - private javax.swing.JTable jTable1; + private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; private javax.swing.JPanel jTableDataPanel; + private javax.swing.JButton nextPageButton; private javax.swing.JTextField numEntriesField; + private javax.swing.JLabel numPagesLabel; + private javax.swing.JButton prevPageButton; private javax.swing.JComboBox tablesDropdownList; // End of variables declaration//GEN-END:variables @@ -209,11 +251,11 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { public void resetComponent() { dbTablesMap.clear(); - + tablesDropdownList.setEnabled(true); tablesDropdownList.removeAllItems(); numEntriesField.setText(""); - + // close DB connection to file if (null != connection) { try { @@ -231,11 +273,6 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { } } - private void customizeComponents() { - - // add a actionListener to jTablesComboBox - } - /** * Process the given SQLite DB file * @@ -327,30 +364,26 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { // RAMAN TBD: parse and save the table schema } - } catch (Exception ex) { + } catch (SQLException ex) { LOGGER.log(Level.WARNING, "Error while trying to get columns from sqlite db." + connection, ex); //NON-NLS } } - } catch (Exception e) { + } catch (SQLException e) { LOGGER.log(Level.SEVERE, "Error getting table names from the DB", e); //NON-NLS } return true; } - private void readTable(String tableName) { - // TBD: need to handle cancelling if one is already in progress - - new SwingWorker() { + private void selectTable(String tableName) { + new SwingWorker() { @Override protected Integer doInBackground() throws Exception { try { Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery( - "SELECT COUNT(*) as count FROM " + tableName); //NON-NLS + "SELECT count (*) as count FROM " + tableName); //NON-NLS - // TBD: read the rows here and popluate the ExplorerManager. - return resultSet.getInt("count"); } catch (SQLException ex) { LOGGER.log(Level.SEVERE, "Failed to get data for table.", ex); //NON-NLS @@ -363,8 +396,70 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { protected void done() { super.done(); try { - int numRows = get(); + + numRows = get(); numEntriesField.setText(numRows + " entries"); + + currPage = 1; + currPageLabel.setText(Integer.toString(currPage)); + numPagesLabel.setText(Integer.toString((numRows/ROWS_PER_PAGE)+1)); + + prevPageButton.setEnabled(false); + + jTableDataPanel.removeAll(); + jTableDataPanel.repaint(); + + if (numRows > 0) { + nextPageButton.setEnabled(((numRows > ROWS_PER_PAGE))); + readTable(tableName, (currPage-1)*ROWS_PER_PAGE + 1, ROWS_PER_PAGE); + } + else { + nextPageButton.setEnabled(false); + } + + + } catch (InterruptedException | ExecutionException ex) { + LOGGER.log(Level.SEVERE, "Unexpected exception while reading table.", ex); //NON-NLS + } + } + }.execute(); + } + private void readTable(String tableName, int startRow, int numRowsToRead) { + + // TBD: need to handle cancelling if one is already in progress + + new SwingWorker>, Void>() { + @Override + protected ArrayList> doInBackground() throws Exception { + try { + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery( + "SELECT * FROM " + tableName + + " LIMIT " + Integer.toString(numRowsToRead) + + " OFFSET " + Integer.toString(startRow - 1) + ); //NON-NLS + + return resultSetToArrayList(resultSet); + } catch (SQLException ex) { + LOGGER.log(Level.SEVERE, "Failed to get data for table.", ex); //NON-NLS + } + //NON-NLS + return null; + } + + @Override + protected void done() { + super.done(); + try { + ArrayList> rows = get(); + if (Objects.nonNull(rows)) { + JPanel selectedTableView = new SQLiteTableView(rows); + + jTableDataPanel.removeAll(); + jTableDataPanel.setLayout(new javax.swing.OverlayLayout(jTableDataPanel)); + jTableDataPanel.add(selectedTableView); + jTableDataPanel.repaint(); + } } catch (InterruptedException | ExecutionException ex) { LOGGER.log(Level.SEVERE, "Unexpected exception while reading table.", ex); //NON-NLS } @@ -373,6 +468,29 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { } + private ArrayList> resultSetToArrayList(ResultSet rs) throws SQLException { + ResultSetMetaData md = rs.getMetaData(); + int columns = md.getColumnCount(); + ArrayList> arraylist = new ArrayList<>(); + while (rs.next()) { + Map row = new LinkedHashMap<>(columns); + for (int i = 1; i <= columns; ++i) { + if (rs.getObject(i) == null) { + row.put(md.getColumnName(i), ""); + } else { + if (md.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) { + row.put(md.getColumnName(i), "BLOB Data not shown..."); + } else { + row.put(md.getColumnName(i), rs.getObject(i)); + } + } + } + arraylist.add(row); + } + + return arraylist; + } + enum SQLStorageClass { NULL, INTEGER, @@ -390,6 +508,13 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { this.colName = colName; this.storageClass = sc; } - + + public String getColName() { + return colName; + } + + public SQLStorageClass getColStorageClass() { + return storageClass; + } } } From b8d9efb181ffb99b59eed5e0007afd6b88ae5ef2 Mon Sep 17 00:00:00 2001 From: Raman Date: Mon, 22 Jan 2018 10:19:06 -0500 Subject: [PATCH 2/7] Fixing scrollbar on the SQLiteTableView. - Removes the doubles vertical scroll bar. - A horizontal scroll bar appears but it is disabled and not functional --- .../contentviewers/SQLiteTableView.form | 25 +++++------------ .../contentviewers/SQLiteTableView.java | 28 ++++++++----------- 2 files changed, 18 insertions(+), 35 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.form b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.form index a34fd71d16..75cb61a443 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.form @@ -1,6 +1,11 @@
+ + + + + @@ -11,24 +16,8 @@ + - - - - - - - - - - - - - - - - - - + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java index b2fdc912d5..dc5d6c97af 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.contentviewers; +import java.awt.BorderLayout; import java.util.ArrayList; import java.util.Map; import java.util.Objects; @@ -48,9 +49,12 @@ public class SQLiteTableView extends javax.swing.JPanel implements ExplorerManag this.tableRows = rows; outlineView = new org.openide.explorer.view.OutlineView(); - + outlineView.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); + initComponents(); + add(outlineView,BorderLayout.CENTER); + outline = outlineView.getOutline(); outlineView.setPropertyColumns(); // column headers will be set later outline.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); @@ -60,11 +64,12 @@ public class SQLiteTableView extends javax.swing.JPanel implements ExplorerManag private void customize() { - tableScrollPane.setViewportView(outlineView); + //tableScrollPane.setViewportView(outlineView); + this.setVisible(true); outline.setRowSelectionAllowed(false); outline.setRootVisible(false); - + explorerManager = new ExplorerManager(); explorerManager.setRootContext(new AbstractNode(Children.create(new SQLiteTableRowFactory(tableRows), true))); @@ -95,7 +100,7 @@ public class SQLiteTableView extends javax.swing.JPanel implements ExplorerManag outlineView.setPropertyColumns(propStrings); // RAMAN TBD: Set width based on actual data in the top N rows?? - // TBD: Cant seem to geta horizontal scroll bar + // TBD: Can't seem to geta horizontal scroll bar for (int col = 0; col< outline.getModel().getColumnCount(); col++) { outline.getColumnModel().getColumn(col).setMinWidth(50); } @@ -116,18 +121,8 @@ public class SQLiteTableView extends javax.swing.JPanel implements ExplorerManag // //GEN-BEGIN:initComponents private void initComponents() { - tableScrollPane = new javax.swing.JScrollPane(); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) - ); + setPreferredSize(new java.awt.Dimension(600, 400)); + setLayout(new java.awt.BorderLayout()); }// //GEN-END:initComponents @@ -138,6 +133,5 @@ public class SQLiteTableView extends javax.swing.JPanel implements ExplorerManag } // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JScrollPane tableScrollPane; // End of variables declaration//GEN-END:variables } From 71c510c4916d0267a6515bacb2c7c0e410a2afb1 Mon Sep 17 00:00:00 2001 From: Raman Date: Mon, 22 Jan 2018 13:39:02 -0500 Subject: [PATCH 3/7] 3415: Show SQLite table contents. - Cleanup --- .../contentviewers/SQLiteTableView.java | 4 +- .../autopsy/contentviewers/SQLiteViewer.java | 80 ++++++++----------- 2 files changed, 35 insertions(+), 49 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java index dc5d6c97af..c795133f73 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java @@ -99,8 +99,8 @@ public class SQLiteTableView extends javax.swing.JPanel implements ExplorerManag outlineView.setPropertyColumns(propStrings); - // RAMAN TBD: Set width based on actual data in the top N rows?? - // TBD: Can't seem to geta horizontal scroll bar + // TBD: Set width based on actual data in the top N rows?? + // TBD: Can't seem to get the horizontal scrollbar working for (int col = 0; col< outline.getModel().getColumnCount(); col++) { outline.getColumnModel().getColumn(col).setMinWidth(50); } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index a60037fad1..5d90562834 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -1,3 +1,21 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.sleuthkit.autopsy.contentviewers; @@ -42,6 +60,7 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { private int numRows; // num of rows in the selected table private int currPage = 0; // curr page of rows being displayed + private SwingWorker>, Void> worker; /** * Creates new form SQLiteViewer */ @@ -352,21 +371,6 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { String tableSQL = resultSet.getString("sql"); //NON-NLS dbTablesMap.put(tableName, tableSQL); - String query = "PRAGMA table_info(" + tableName + ")"; //NON-NLS - ResultSet rs2; - try { - Statement statement2 = connection.createStatement(); - rs2 = statement2.executeQuery(query); - while (rs2.next()) { - - // System.out.println("RAMAN: Col Name = " + rs2.getString("name")); - // System.out.println("RAMAN: Col Type = " + rs2.getString("type")); - - // RAMAN TBD: parse and save the table schema - } - } catch (SQLException ex) { - LOGGER.log(Level.WARNING, "Error while trying to get columns from sqlite db." + connection, ex); //NON-NLS - } } } catch (SQLException e) { LOGGER.log(Level.SEVERE, "Error getting table names from the DB", e); //NON-NLS @@ -426,9 +430,12 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { } private void readTable(String tableName, int startRow, int numRowsToRead) { - // TBD: need to handle cancelling if one is already in progress - - new SwingWorker>, Void>() { + if (worker != null && !worker.isDone()) { + worker.cancel(false); + worker = null; + } + + worker = new SwingWorker>, Void>() { @Override protected ArrayList> doInBackground() throws Exception { try { @@ -449,6 +456,11 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { @Override protected void done() { + + if (isCancelled()) { + return; + } + super.done(); try { ArrayList> rows = get(); @@ -464,8 +476,9 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { LOGGER.log(Level.SEVERE, "Unexpected exception while reading table.", ex); //NON-NLS } } - }.execute(); - + }; + + worker.execute(); } private ArrayList> resultSetToArrayList(ResultSet rs) throws SQLException { @@ -490,31 +503,4 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { return arraylist; } - - enum SQLStorageClass { - NULL, - INTEGER, - REAL, - TEXT, - BLOB - }; - - private class SQLColDef { - - private final String colName; - private final SQLStorageClass storageClass; - - SQLColDef(String colName, SQLStorageClass sc) { - this.colName = colName; - this.storageClass = sc; - } - - public String getColName() { - return colName; - } - - public SQLStorageClass getColStorageClass() { - return storageClass; - } - } } From 75f6198190266102e433e589dcc9c2e2a42b6e5c Mon Sep 17 00:00:00 2001 From: Raman Date: Tue, 23 Jan 2018 13:33:03 -0500 Subject: [PATCH 4/7] Fixed a warning. --- Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index 5d90562834..6442b645d1 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -227,7 +227,7 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { }//GEN-LAST:event_prevPageButtonActionPerformed private void tablesDropdownListActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tablesDropdownListActionPerformed - JComboBox cb = (JComboBox) evt.getSource(); + JComboBox cb = (JComboBox) evt.getSource(); String tableName = (String) cb.getSelectedItem(); if (null == tableName) { return; From 6b02ab135864fff8ca223cab11743a95a849aafc Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 24 Jan 2018 11:44:57 +0100 Subject: [PATCH 5/7] refactor SQLiteViewer to fix scroll bar and painting issues. --- .../contentviewers/SQLiteTableRowFactory.java | 7 +- .../contentviewers/SQLiteTableView.form | 5 - .../contentviewers/SQLiteTableView.java | 99 +++++++--------- .../autopsy/contentviewers/SQLiteViewer.form | 47 ++------ .../autopsy/contentviewers/SQLiteViewer.java | 110 +++++++++--------- 5 files changed, 118 insertions(+), 150 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableRowFactory.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableRowFactory.java index 98b21149ce..633f40260c 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableRowFactory.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableRowFactory.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.contentviewers; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -31,9 +30,9 @@ import org.sleuthkit.autopsy.datamodel.NodeProperty; public class SQLiteTableRowFactory extends ChildFactory { - private final ArrayList> rows; + private final List> rows; - public SQLiteTableRowFactory(ArrayList> rows) { + public SQLiteTableRowFactory(List> rows) { this.rows = rows; } @@ -62,7 +61,7 @@ class SQLiteTableRowNode extends AbstractNode { private final Map row; - public SQLiteTableRowNode(Map row) { + SQLiteTableRowNode(Map row) { super(Children.LEAF); this.row = row; } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.form b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.form index 75cb61a443..2c7924e2a4 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.form @@ -1,11 +1,6 @@
- - - - - diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java index c795133f73..ad1117f0a0 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java @@ -19,10 +19,13 @@ package org.sleuthkit.autopsy.contentviewers; import java.awt.BorderLayout; -import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Objects; +import javax.swing.JPanel; +import javax.swing.JTable; import javax.swing.ListSelectionModel; +import javax.swing.ScrollPaneConstants; import javax.swing.table.TableColumnModel; import org.netbeans.swing.etable.ETableColumn; import org.netbeans.swing.etable.ETableColumnModel; @@ -31,73 +34,64 @@ import org.openide.explorer.ExplorerManager; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; +class SQLiteTableView extends JPanel implements ExplorerManager.Provider { -public class SQLiteTableView extends javax.swing.JPanel implements ExplorerManager.Provider { - - private final Outline outline; private final org.openide.explorer.view.OutlineView outlineView; - private ExplorerManager explorerManager; + private final Outline outline; + private final ExplorerManager explorerManager; - private final ArrayList> tableRows; - /** * Creates new form SQLiteTableView - * @param rows + * */ - public SQLiteTableView(ArrayList> rows) { - - this.tableRows = rows; - - outlineView = new org.openide.explorer.view.OutlineView(); - outlineView.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); - + SQLiteTableView() { + initComponents(); - - add(outlineView,BorderLayout.CENTER); - - outline = outlineView.getOutline(); + outlineView = new org.openide.explorer.view.OutlineView(); + add(outlineView, BorderLayout.CENTER); outlineView.setPropertyColumns(); // column headers will be set later + outlineView.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); + outlineView.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + + outline = outlineView.getOutline(); + outline.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - - customize(); + outline.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + outline.setRowSelectionAllowed(false); + outline.setRootVisible(false); + + explorerManager = new ExplorerManager(); } - private void customize() { - - //tableScrollPane.setViewportView(outlineView); - - this.setVisible(true); - outline.setRowSelectionAllowed(false); - outline.setRootVisible(false); - - explorerManager = new ExplorerManager(); - explorerManager.setRootContext(new AbstractNode(Children.create(new SQLiteTableRowFactory(tableRows), true))); - - setupColumns(); - } - /** * Sets up the columns in the display table + * + * @param tableRows */ - private void setupColumns() { - if (Objects.isNull(tableRows) || tableRows.isEmpty()) - return; - - Map row = tableRows.get(0); - - // Get the columns setup with respect to names and sortability - String[] propStrings = new String[row.size() * 2]; - - int i = 0; - for (Map.Entry col: row.entrySet()) { + void setupTable(List> tableRows) { + + explorerManager.setRootContext(new AbstractNode(Children.create(new SQLiteTableRowFactory(tableRows), true))); + + if (Objects.isNull(tableRows) || tableRows.isEmpty()) { + outlineView.setPropertyColumns(); +// return; + } else { + + Map row = tableRows.get(0); + + // Get the columns setup with respect to names and sortability + String[] propStrings = new String[row.size() * 2]; + + int i = 0; + for (Map.Entry col : row.entrySet()) { String colName = col.getKey(); propStrings[2 * i] = colName; propStrings[2 * i + 1] = colName; i++; + } + + outlineView.setPropertyColumns(propStrings); } - - - outlineView.setPropertyColumns(propStrings); // TBD: Set width based on actual data in the top N rows?? // TBD: Can't seem to get the horizontal scrollbar working @@ -109,9 +103,9 @@ public class SQLiteTableView extends javax.swing.JPanel implements ExplorerManag TableColumnModel columnModel = outline.getColumnModel(); ETableColumn column = (ETableColumn) columnModel.getColumn(0); ((ETableColumnModel) columnModel).setColumnHidden(column, true); - + } - + /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always @@ -121,12 +115,9 @@ public class SQLiteTableView extends javax.swing.JPanel implements ExplorerManag // //GEN-BEGIN:initComponents private void initComponents() { - setPreferredSize(new java.awt.Dimension(600, 400)); setLayout(new java.awt.BorderLayout()); }// //GEN-END:initComponents - - @Override public ExplorerManager getExplorerManager() { return explorerManager; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.form index 121a96c823..0469da7b73 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.form @@ -68,16 +68,18 @@ - - - - - - - - + + + + + + + + + + @@ -200,33 +202,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index 6442b645d1..d5b5afdb68 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -16,29 +16,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.sleuthkit.autopsy.contentviewers; +import java.awt.BorderLayout; import java.awt.Component; import java.io.File; import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.logging.Level; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.ResultSetMetaData; -import java.sql.Statement; import java.sql.SQLException; +import java.sql.Statement; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.TreeMap; import java.util.concurrent.ExecutionException; +import java.util.logging.Level; import javax.swing.JComboBox; -import javax.swing.JPanel; import javax.swing.SwingWorker; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; @@ -50,22 +50,26 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { public static final String[] SUPPORTED_MIMETYPES = new String[]{"application/x-sqlite3"}; private static final Logger LOGGER = Logger.getLogger(FileViewer.class.getName()); private Connection connection = null; - + private String tmpDBPathName = null; private File tmpDBFile = null; - Map dbTablesMap = new TreeMap<>(); + private final Map dbTablesMap = new TreeMap<>(); private static final int ROWS_PER_PAGE = 100; private int numRows; // num of rows in the selected table private int currPage = 0; // curr page of rows being displayed - - private SwingWorker>, Void> worker; + + SQLiteTableView selectedTableView = new SQLiteTableView(); + + private SwingWorker worker; + /** * Creates new form SQLiteViewer */ public SQLiteViewer() { initComponents(); + jTableDataPanel.add(selectedTableView, BorderLayout.CENTER); } /** @@ -167,20 +171,21 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jHdrPanelLayout.createSequentialGroup() .addContainerGap() - .addGroup(jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(tablesDropdownList, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jLabel1) - .addComponent(numEntriesField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jLabel2) - .addComponent(currPageLabel) - .addComponent(jLabel3) - .addComponent(numPagesLabel) + .addGroup(jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(tablesDropdownList, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel1) + .addComponent(numEntriesField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel2) + .addComponent(currPageLabel) + .addComponent(jLabel3) + .addComponent(numPagesLabel))) .addContainerGap()) ); - jTableDataPanel.setLayout(new javax.swing.OverlayLayout(jTableDataPanel)); + jTableDataPanel.setLayout(new java.awt.BorderLayout()); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); @@ -270,11 +275,11 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { public void resetComponent() { dbTablesMap.clear(); - + tablesDropdownList.setEnabled(true); tablesDropdownList.removeAllItems(); numEntriesField.setText(""); - + // close DB connection to file if (null != connection) { try { @@ -312,7 +317,7 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { tmpDBPathName = Case.getCurrentCase().getTempDirectory() + File.separator + sqliteFile.getName() + "-" + sqliteFile.getId(); tmpDBFile = new File(tmpDBPathName); ContentUtils.writeToFile(sqliteFile, tmpDBFile); - + // Open copy using JDBC Class.forName("org.sqlite.JDBC"); //NON-NLS //load JDBC driver connection = DriverManager.getConnection("jdbc:sqlite:" + tmpDBPathName); //NON-NLS @@ -379,7 +384,12 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { } private void selectTable(String tableName) { - new SwingWorker() { + if (worker != null && !worker.isDone()) { + worker.cancel(false); + worker = null; + } + + worker = new SwingWorker() { @Override protected Integer doInBackground() throws Exception { @@ -400,48 +410,47 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { protected void done() { super.done(); try { - + numRows = get(); numEntriesField.setText(numRows + " entries"); - + currPage = 1; currPageLabel.setText(Integer.toString(currPage)); - numPagesLabel.setText(Integer.toString((numRows/ROWS_PER_PAGE)+1)); - + numPagesLabel.setText(Integer.toString((numRows / ROWS_PER_PAGE) + 1)); + prevPageButton.setEnabled(false); - - jTableDataPanel.removeAll(); - jTableDataPanel.repaint(); - + + if (numRows > 0) { nextPageButton.setEnabled(((numRows > ROWS_PER_PAGE))); - readTable(tableName, (currPage-1)*ROWS_PER_PAGE + 1, ROWS_PER_PAGE); - } - else { + readTable(tableName, (currPage - 1) * ROWS_PER_PAGE + 1, ROWS_PER_PAGE); + } else { nextPageButton.setEnabled(false); + selectedTableView.setupTable(Collections.emptyList()); } - - + } catch (InterruptedException | ExecutionException ex) { LOGGER.log(Level.SEVERE, "Unexpected exception while reading table.", ex); //NON-NLS } } - }.execute(); + }; + worker.execute(); } + private void readTable(String tableName, int startRow, int numRowsToRead) { - + if (worker != null && !worker.isDone()) { worker.cancel(false); worker = null; } - + worker = new SwingWorker>, Void>() { @Override protected ArrayList> doInBackground() throws Exception { try { Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery( - "SELECT * FROM " + tableName + "SELECT * FROM " + tableName + " LIMIT " + Integer.toString(numRowsToRead) + " OFFSET " + Integer.toString(startRow - 1) ); //NON-NLS @@ -456,32 +465,29 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { @Override protected void done() { - + if (isCancelled()) { return; } - + super.done(); try { ArrayList> rows = get(); if (Objects.nonNull(rows)) { - JPanel selectedTableView = new SQLiteTableView(rows); - - jTableDataPanel.removeAll(); - jTableDataPanel.setLayout(new javax.swing.OverlayLayout(jTableDataPanel)); - jTableDataPanel.add(selectedTableView); - jTableDataPanel.repaint(); + selectedTableView.setupTable(rows); + }else{ + selectedTableView.setupTable(Collections.emptyList()); } } catch (InterruptedException | ExecutionException ex) { LOGGER.log(Level.SEVERE, "Unexpected exception while reading table.", ex); //NON-NLS } } }; - + worker.execute(); } - private ArrayList> resultSetToArrayList(ResultSet rs) throws SQLException { + private ArrayList> resultSetToArrayList(ResultSet rs) throws SQLException { ResultSetMetaData md = rs.getMetaData(); int columns = md.getColumnCount(); ArrayList> arraylist = new ArrayList<>(); @@ -502,5 +508,5 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { } return arraylist; - } + } } From 338334f62936c2d3ea30979d7076f5b39388d2cc Mon Sep 17 00:00:00 2001 From: Raman Date: Tue, 30 Jan 2018 09:50:25 -0500 Subject: [PATCH 6/7] 3415: Show SQLite table contents Resize columns based on data from top 20 rows --- .../contentviewers/SQLiteTableView.java | 59 +++++++++++++++---- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java index ad1117f0a0..7ca873e13c 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteTableView.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.contentviewers; import java.awt.BorderLayout; +import java.awt.Component; import java.util.List; import java.util.Map; import java.util.Objects; @@ -26,6 +27,8 @@ import javax.swing.JPanel; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.ScrollPaneConstants; +import javax.swing.SwingWorker; +import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumnModel; import org.netbeans.swing.etable.ETableColumn; import org.netbeans.swing.etable.ETableColumnModel; @@ -70,18 +73,14 @@ class SQLiteTableView extends JPanel implements ExplorerManager.Provider { */ void setupTable(List> tableRows) { - explorerManager.setRootContext(new AbstractNode(Children.create(new SQLiteTableRowFactory(tableRows), true))); - + if (Objects.isNull(tableRows) || tableRows.isEmpty()) { outlineView.setPropertyColumns(); -// return; } else { + // Set up the column names Map row = tableRows.get(0); - - // Get the columns setup with respect to names and sortability String[] propStrings = new String[row.size() * 2]; - int i = 0; for (Map.Entry col : row.entrySet()) { String colName = col.getKey(); @@ -93,19 +92,55 @@ class SQLiteTableView extends JPanel implements ExplorerManager.Provider { outlineView.setPropertyColumns(propStrings); } - // TBD: Set width based on actual data in the top N rows?? - // TBD: Can't seem to get the horizontal scrollbar working - for (int col = 0; col< outline.getModel().getColumnCount(); col++) { - outline.getColumnModel().getColumn(col).setMinWidth(50); - } - // Hide the 'Nodes' column TableColumnModel columnModel = outline.getColumnModel(); ETableColumn column = (ETableColumn) columnModel.getColumn(0); ((ETableColumnModel) columnModel).setColumnHidden(column, true); + // Set the Nodes for the ExplorerManager. + // The Swingworker ensures that setColumnWidths() is called after all nodes have been created. + new SwingWorker() { + @Override + protected Boolean doInBackground() throws Exception { + + explorerManager.setRootContext(new AbstractNode(Children.create(new SQLiteTableRowFactory(tableRows), true))); + return false; + } + + @Override + protected void done() { + super.done(); + + setColumnWidths(); + } + }.execute(); + } + private void setColumnWidths() { + int margin = 4; + int padding = 8; + + // find the maximum width needed to fit the values for the first N rows, at most + final int rows = Math.min(20, outline.getRowCount()); + for (int col = 1; col < outline.getColumnCount(); col++) { + int columnWidthLimit = 500; + int columnWidth = 50; + + for (int row = 0; row < rows; row++) { + TableCellRenderer renderer = outline.getCellRenderer(row, col); + Component comp = outline.prepareRenderer(renderer, row, col); + + columnWidth = Math.max(comp.getPreferredSize().width, columnWidth); + } + + columnWidth += 2 * margin + padding; // add margin and regular padding + columnWidth = Math.min(columnWidth, columnWidthLimit); + outline.getColumnModel().getColumn(col).setPreferredWidth(columnWidth); + } + } + + /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always From 481bec66c11f780db1417fee5fb094e0f09b3387 Mon Sep 17 00:00:00 2001 From: Raman Date: Thu, 1 Feb 2018 17:08:09 -0500 Subject: [PATCH 7/7] Address review comments --- .../autopsy/contentviewers/SQLiteViewer.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index d5b5afdb68..627d30e87c 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -40,6 +40,7 @@ import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.JComboBox; import javax.swing.SwingWorker; +import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.ContentUtils; @@ -457,7 +458,7 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { return resultSetToArrayList(resultSet); } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "Failed to get data for table.", ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Failed to get data for table " + tableName, ex); //NON-NLS } //NON-NLS return null; @@ -479,7 +480,7 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { selectedTableView.setupTable(Collections.emptyList()); } } catch (InterruptedException | ExecutionException ex) { - LOGGER.log(Level.SEVERE, "Unexpected exception while reading table.", ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Unexpected exception while reading table " + tableName, ex); //NON-NLS } } }; @@ -487,26 +488,27 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { worker.execute(); } + @NbBundle.Messages("SQLiteViewer.BlobNotShown.message=BLOB Data not shown") private ArrayList> resultSetToArrayList(ResultSet rs) throws SQLException { - ResultSetMetaData md = rs.getMetaData(); - int columns = md.getColumnCount(); - ArrayList> arraylist = new ArrayList<>(); + ResultSetMetaData metaData = rs.getMetaData(); + int columns = metaData.getColumnCount(); + ArrayList> rowlist = new ArrayList<>(); while (rs.next()) { Map row = new LinkedHashMap<>(columns); for (int i = 1; i <= columns; ++i) { if (rs.getObject(i) == null) { - row.put(md.getColumnName(i), ""); + row.put(metaData.getColumnName(i), ""); } else { - if (md.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) { - row.put(md.getColumnName(i), "BLOB Data not shown..."); + if (metaData.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) { + row.put(metaData.getColumnName(i), Bundle.SQLiteViewer_BlobNotShown_message()); } else { - row.put(md.getColumnName(i), rs.getObject(i)); + row.put(metaData.getColumnName(i), rs.getObject(i)); } } } - arraylist.add(row); + rowlist.add(row); } - return arraylist; + return rowlist; } }