From 78282bb2ec9aef1716d1d5c732e971618ccb0bd6 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 30 Jun 2017 13:41:56 +0200 Subject: [PATCH 01/16] cleanup and formatting --- .../sleuthkit/autopsy/casemodule/Case.java | 33 ++++------ .../datamodel/RootContentChildren.java | 9 +-- .../DirectoryTreeTopComponent.form | 6 +- .../DirectoryTreeTopComponent.java | 63 +++++++++---------- 4 files changed, 45 insertions(+), 66 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index ce69e7a017..340d847343 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -63,6 +63,8 @@ import org.openide.util.NbBundle.Messages; import org.openide.util.actions.CallableSystemAction; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.actions.OpenOutputFolderAction; +import org.sleuthkit.autopsy.appservices.AutopsyService; +import org.sleuthkit.autopsy.appservices.AutopsyService.CaseContext; import org.sleuthkit.autopsy.casemodule.CaseMetadata.CaseMetadataException; import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceEvent; import org.sleuthkit.autopsy.casemodule.events.AddingDataSourceFailedEvent; @@ -92,15 +94,13 @@ import org.sleuthkit.autopsy.coreutils.Version; import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.events.AutopsyEventException; import org.sleuthkit.autopsy.events.AutopsyEventPublisher; -import org.sleuthkit.autopsy.appservices.AutopsyService; -import org.sleuthkit.autopsy.appservices.AutopsyService.CaseContext; -import org.sleuthkit.autopsy.progress.LoggingProgressIndicator; -import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator; -import org.sleuthkit.autopsy.progress.ProgressIndicator; import org.sleuthkit.autopsy.ingest.IngestJob; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException; +import org.sleuthkit.autopsy.progress.LoggingProgressIndicator; +import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator; +import org.sleuthkit.autopsy.progress.ProgressIndicator; import org.sleuthkit.autopsy.timeline.OpenTimelineAction; import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.CaseDbConnectionInfo; @@ -964,23 +964,12 @@ public class Case { /* * Enable the case-specific actions. */ - CallableSystemAction.get(AddImageAction.class - ).setEnabled(true); - CallableSystemAction - .get(CaseCloseAction.class - ).setEnabled(true); - CallableSystemAction - .get(CasePropertiesAction.class - ).setEnabled(true); - CallableSystemAction - .get(CaseDeleteAction.class - ).setEnabled(true); - CallableSystemAction - .get(OpenTimelineAction.class - ).setEnabled(true); - CallableSystemAction - .get(OpenOutputFolderAction.class - ).setEnabled(false); + CallableSystemAction.get(AddImageAction.class).setEnabled(true); + CallableSystemAction.get(CaseCloseAction.class).setEnabled(true); + CallableSystemAction.get(CasePropertiesAction.class).setEnabled(true); + CallableSystemAction.get(CaseDeleteAction.class).setEnabled(true); + CallableSystemAction.get(OpenTimelineAction.class).setEnabled(true); + CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false); /* * Add the case to the recent cases tracker that supplies a list diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java index 7221aab9a6..f04495a706 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.datamodel; import java.util.Collection; import java.util.Collections; -import org.sleuthkit.datamodel.BlackboardArtifact; /** * Children implementation for the root node of a ContentNode tree. Accepts a @@ -28,7 +27,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact; */ public class RootContentChildren extends AbstractContentChildren { - private Collection contentKeys; + private final Collection contentKeys; /** * @param contentKeys root Content objects for the Node tree @@ -55,8 +54,6 @@ public class RootContentChildren extends AbstractContentChildren { * but we are not ready for this. */ public void refreshContentKeys() { - for (Object key : contentKeys) { - refreshKey(key); - } + contentKeys.forEach(this::refreshKey); } } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.form b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.form index d77daea41e..90c6ac00a6 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.form +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.form @@ -16,7 +16,7 @@ - + @@ -38,14 +38,14 @@ - + - + diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index 8b7c3e1a46..b8ff5615b6 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -114,7 +114,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat initComponents(); // only allow one item to be selected at a time - ((BeanTreeView) jScrollPane1).setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + getTree().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); // remove the close button putClientProperty(TopComponent.PROP_CLOSING_DISABLED, Boolean.TRUE); setName(NbBundle.getMessage(DirectoryTreeTopComponent.class, "CTL_DirectoryTreeTopComponent")); @@ -175,12 +175,12 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat // //GEN-BEGIN:initComponents private void initComponents() { - jScrollPane1 = new BeanTreeView(); + treeView = new BeanTreeView(); backButton = new javax.swing.JButton(); forwardButton = new javax.swing.JButton(); showRejectedCheckBox = new javax.swing.JCheckBox(); - jScrollPane1.setBorder(null); + treeView.setBorder(null); backButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/directorytree/btn_step_back.png"))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(backButton, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.backButton.text")); // NOI18N @@ -220,7 +220,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 262, Short.MAX_VALUE) + .addComponent(treeView, javax.swing.GroupLayout.DEFAULT_SIZE, 262, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() .addGap(5, 5, 5) .addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -239,7 +239,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat .addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(showRejectedCheckBox)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 854, Short.MAX_VALUE) + .addComponent(treeView, javax.swing.GroupLayout.DEFAULT_SIZE, 854, Short.MAX_VALUE) .addGap(0, 0, 0)) ); }// //GEN-END:initComponents @@ -296,8 +296,8 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton backButton; private javax.swing.JButton forwardButton; - private javax.swing.JScrollPane jScrollPane1; private javax.swing.JCheckBox showRejectedCheckBox; + private javax.swing.JScrollPane treeView; // End of variables declaration//GEN-END:variables /** @@ -372,19 +372,18 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat // close the top component if there's no image in this case if (null == currentCase || currentCase.hasData() == false) { - ((TreeView) this.jScrollPane1).setRootVisible(false); // hide the root + getTree().setRootVisible(false); // hide the root } else { // if there's at least one image, load the image and open the top component - List items = new ArrayList<>(); final SleuthkitCase tskCase = currentCase.getSleuthkitCase(); - items.add(new DataSources()); - items.add(new Views(tskCase)); - items.add(new Results(tskCase)); - items.add(new Tags()); - items.add(new Reports()); - contentChildren = new RootContentChildren(items); - + contentChildren = new RootContentChildren(Arrays.asList( + new DataSources(), + new Views(tskCase), + new Results(tskCase), + new Tags(), + new Reports())); Node root = new AbstractNode(contentChildren) { + //JMTODO: What is the point of these overrides? /** * to override the right click action in the white blank * space area on the directory tree window @@ -412,32 +411,26 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat em.setRootContext(root); em.getRootContext().setName(currentCase.getName()); em.getRootContext().setDisplayName(currentCase.getName()); - ((TreeView) this.jScrollPane1).setRootVisible(false); // hide the root + getTree().setRootVisible(false); // hide the root // Reset the forward and back lists because we're resetting the root context resetHistory(); - Children childNodes = em.getRootContext().getChildren(); + Children rootChildren = em.getRootContext().getChildren(); TreeView tree = getTree(); - Node results = childNodes.findChild(ResultsNode.NAME); + Node results = rootChildren.findChild(ResultsNode.NAME); tree.expandNode(results); + Children resultsChildren = results.getChildren(); + Arrays.stream(resultsChildren.getNodes()).forEach(tree::expandNode); - Children resultsChilds = results.getChildren(); - for (Node n : resultsChilds.getNodes()) { - tree.expandNode(n); - } - Accounts accounts = resultsChilds.findChild(Accounts.NAME).getLookup().lookup(Accounts.class); + Accounts accounts = resultsChildren.findChild(Accounts.NAME).getLookup().lookup(Accounts.class); showRejectedCheckBox.setAction(accounts.newToggleShowRejectedAction()); showRejectedCheckBox.setSelected(false); - Node views = childNodes.findChild(ViewsNode.NAME); - Children viewsChilds = views.getChildren(); - for (Node n : viewsChilds.getNodes()) { - tree.expandNode(n); - } - + Node views = rootChildren.findChild(ViewsNode.NAME); + Arrays.stream(views.getChildren().getNodes()).forEach(tree::expandNode); tree.collapseNode(views); // if the dataResult is not opened @@ -445,13 +438,15 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat dataResult.open(); // open the data result top component as well when the directory tree is opened } + /*JMTODO: What is this supposed to do? */ + // select the first image node, if there is one // (this has to happen after dataResult is opened, because the event // of changing the selected node fires a handler that tries to make // dataResult active) - if (childNodes.getNodesCount() > 0) { + if (rootChildren.getNodesCount() > 0) { try { - em.setSelectedNodes(new Node[]{childNodes.getNodeAt(0)}); + em.setSelectedNodes(new Node[]{rootChildren.getNodeAt(0)}); } catch (PropertyVetoException ex) { LOGGER.log(Level.SEVERE, "Error setting default selected node.", ex); //NON-NLS } @@ -605,9 +600,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat // We only need to trigger openCoreWindows() when the // first data source is added. if (currentCase.getDataSources().size() == 1) { - SwingUtilities.invokeLater(() -> { - CoreComponentControl.openCoreWindows(); - }); + SwingUtilities.invokeLater(CoreComponentControl::openCoreWindows); } } catch (IllegalStateException | TskCoreException notUsed) { /** @@ -749,7 +742,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat * @return tree the BeanTreeView */ public BeanTreeView getTree() { - return (BeanTreeView) this.jScrollPane1; + return (BeanTreeView) this.treeView; } /** From 548481c16673b1701a46bf766f1ed20d42b13ada Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 30 Jun 2017 14:48:40 +0200 Subject: [PATCH 02/16] expand tree in a swingworker --- .../DirectoryTreeTopComponent.java | 190 ++++++++++-------- 1 file changed, 104 insertions(+), 86 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index b8ff5615b6..c6dde4f581 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -30,11 +30,13 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.prefs.PreferenceChangeEvent; import java.util.prefs.PreferenceChangeListener; import javax.swing.Action; import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; import javax.swing.tree.TreeSelectionModel; import org.apache.commons.lang3.StringUtils; import org.openide.explorer.ExplorerManager; @@ -360,101 +362,117 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat public void componentOpened() { // change the cursor to "waiting cursor" for this operation this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + Case currentCase = null; try { - Case currentCase = null; - try { - currentCase = Case.getCurrentCase(); - } catch (IllegalStateException ex) { - /* - * No open case. + currentCase = Case.getCurrentCase(); + } catch (IllegalStateException ex) { + // No open case. + } + + // close the top component if there's no image in this case + if (null == currentCase || currentCase.hasData() == false) { + getTree().setRootVisible(false); // hide the root + } else { + // if there's at least one image, load the image and open the top component + final SleuthkitCase tskCase = currentCase.getSleuthkitCase(); + contentChildren = new RootContentChildren(Arrays.asList( + new DataSources(), + new Views(tskCase), + new Results(tskCase), + new Tags(), + new Reports())); + Node root = new AbstractNode(contentChildren) { + //JMTODO: What is the point of these overrides? + /** + * to override the right click action in the white blank space + * area on the directory tree window */ - } - - // close the top component if there's no image in this case - if (null == currentCase || currentCase.hasData() == false) { - getTree().setRootVisible(false); // hide the root - } else { - // if there's at least one image, load the image and open the top component - final SleuthkitCase tskCase = currentCase.getSleuthkitCase(); - contentChildren = new RootContentChildren(Arrays.asList( - new DataSources(), - new Views(tskCase), - new Results(tskCase), - new Tags(), - new Reports())); - Node root = new AbstractNode(contentChildren) { - //JMTODO: What is the point of these overrides? - /** - * to override the right click action in the white blank - * space area on the directory tree window - */ - @Override - public Action[] getActions(boolean popup) { - return new Action[]{}; - } - - // Overide the AbstractNode use of DefaultHandle to return - // a handle which can be serialized without a parent - @Override - public Node.Handle getHandle() { - return new Node.Handle() { - @Override - public Node getNode() throws IOException { - return em.getRootContext(); - } - }; - } - }; - - root = new DirectoryTreeFilterNode(root, true); - - em.setRootContext(root); - em.getRootContext().setName(currentCase.getName()); - em.getRootContext().setDisplayName(currentCase.getName()); - getTree().setRootVisible(false); // hide the root - - // Reset the forward and back lists because we're resetting the root context - resetHistory(); - - Children rootChildren = em.getRootContext().getChildren(); - TreeView tree = getTree(); - - Node results = rootChildren.findChild(ResultsNode.NAME); - tree.expandNode(results); - Children resultsChildren = results.getChildren(); - Arrays.stream(resultsChildren.getNodes()).forEach(tree::expandNode); - - - Accounts accounts = resultsChildren.findChild(Accounts.NAME).getLookup().lookup(Accounts.class); - showRejectedCheckBox.setAction(accounts.newToggleShowRejectedAction()); - showRejectedCheckBox.setSelected(false); - - Node views = rootChildren.findChild(ViewsNode.NAME); - Arrays.stream(views.getChildren().getNodes()).forEach(tree::expandNode); - tree.collapseNode(views); - - // if the dataResult is not opened - if (!dataResult.isOpened()) { - dataResult.open(); // open the data result top component as well when the directory tree is opened + @Override + public Action[] getActions(boolean popup) { + return new Action[]{}; } - /*JMTODO: What is this supposed to do? */ - - // select the first image node, if there is one - // (this has to happen after dataResult is opened, because the event - // of changing the selected node fires a handler that tries to make - // dataResult active) - if (rootChildren.getNodesCount() > 0) { + // Overide the AbstractNode use of DefaultHandle to return + // a handle which can be serialized without a parent + @Override + public Node.Handle getHandle() { + return new Node.Handle() { + @Override + public Node getNode() throws IOException { + return em.getRootContext(); + } + }; + } + }; + + root = new DirectoryTreeFilterNode(root, true); + + em.setRootContext(root); + em.getRootContext().setName(currentCase.getName()); + em.getRootContext().setDisplayName(currentCase.getName()); + getTree().setRootVisible(false); // hide the root + + // Reset the forward and back lists because we're resetting the root context + resetHistory(); + new SwingWorker() { + @Override + protected Node[] doInBackground() throws Exception { + Children rootChildren = em.getRootContext().getChildren(); + TreeView tree = getTree(); + + Node results = rootChildren.findChild(ResultsNode.NAME); + tree.expandNode(results); + Children resultsChildren = results.getChildren(); + Arrays.stream(resultsChildren.getNodes()).forEach(tree::expandNode); + + Accounts accounts = resultsChildren.findChild(Accounts.NAME).getLookup().lookup(Accounts.class); + showRejectedCheckBox.setAction(accounts.newToggleShowRejectedAction()); + showRejectedCheckBox.setSelected(false); + + Node views = rootChildren.findChild(ViewsNode.NAME); + Arrays.stream(views.getChildren().getNodes()).forEach(tree::expandNode); + tree.collapseNode(views); + /* + * JMTODO: What is this supposed to do? Right now it selects + * the data sources node, but the comment seems to indicate + * it is supposed to select the first datasource. + */ + // select the first image node, if there is one + // (this has to happen after dataResult is opened, because the event + // of changing the selected node fires a handler that tries to make + // dataResult active) + if (rootChildren.getNodesCount() > 0) { + return new Node[]{rootChildren.getNodeAt(0)}; + } + return new Node[]{}; + } + + @Override + protected void done() { + super.done(); + + // if the dataResult is not opened + if (!dataResult.isOpened()) { + dataResult.open(); // open the data result top component as well when the directory tree is opened + } + /* + * JMTODO: What is this supposed to do? + */ + // select the first image node, if there is one + // (this has to happen after dataResult is opened, because the event + // of changing the selected node fires a handler that tries to make + // dataResult active) try { - em.setSelectedNodes(new Node[]{rootChildren.getNodeAt(0)}); + em.setSelectedNodes(get()); } catch (PropertyVetoException ex) { LOGGER.log(Level.SEVERE, "Error setting default selected node.", ex); //NON-NLS + } catch (InterruptedException | ExecutionException ex) { + LOGGER.log(Level.SEVERE, "Error expanding tree to initial state.", ex); //NON-NLS + } finally { + setCursor(null); } } - - } - } finally { - this.setCursor(null); + }.execute(); } } From 88bed0b60a69278801acd379ec56d9df70d0a438 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 30 Jun 2017 15:40:54 +0200 Subject: [PATCH 03/16] some more minor cleanup --- .../directorytree/DirectoryTreeTopComponent.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index c6dde4f581..ff869c7772 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -767,12 +767,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat * Refresh the content node part of the dir tree safely in the EDT thread */ public void refreshContentTreeSafe() { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - refreshDataSourceTree(); - } - }); + SwingUtilities.invokeLater(this::refreshDataSourceTree); } /** @@ -812,7 +807,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat ArrayList selectedNodePath = new ArrayList<>(Arrays.asList(previouslySelectedNodePath)); while (null == selectedNode && !selectedNodePath.isEmpty()) { try { - selectedNode = NodeOp.findPath(em.getRootContext(), selectedNodePath.toArray(new String[0])); + selectedNode = NodeOp.findPath(em.getRootContext(), selectedNodePath.toArray(new String[selectedNodePath.size()])); } catch (NodeNotFoundException ex) { // The selected node may have been deleted (e.g., a deleted tag), so truncate the path and try again. if (selectedNodePath.size() > 1) { @@ -1039,7 +1034,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat } treeNode = binChildren.findChild(binName); } else { //default account type - treeNode = accountRootChilds.findChild(accountType);; + treeNode = accountRootChilds.findChild(accountType); } } catch (TskCoreException ex) { LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS From abb8079b83da34c25bec694ca16fc4c5ed85e456 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 3 Jul 2017 16:18:56 +0200 Subject: [PATCH 04/16] move duplicated node creation visitors into FileTypes.java; make methods static so we don't need to instantiate throw away object. --- .../autopsy/datamodel/FileTypes.java | 58 +++++++- .../datamodel/FileTypesByExtension.java | 41 +----- .../datamodel/FileTypesByMimeType.java | 138 ++++++------------ 3 files changed, 103 insertions(+), 134 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java index 6998b2ef71..f3941d563e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java @@ -1,15 +1,15 @@ /* * Autopsy Forensic Browser - * + * * Copyright 2011-2016 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. @@ -19,9 +19,18 @@ package org.sleuthkit.autopsy.datamodel; import java.util.Arrays; +import org.openide.nodes.AbstractNode; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ContentVisitor; +import org.sleuthkit.datamodel.DerivedFile; +import org.sleuthkit.datamodel.Directory; +import org.sleuthkit.datamodel.File; +import org.sleuthkit.datamodel.LayoutFile; +import org.sleuthkit.datamodel.LocalFile; +import org.sleuthkit.datamodel.SlackFile; import org.sleuthkit.datamodel.SleuthkitCase; /** @@ -99,4 +108,45 @@ public final class FileTypes implements AutopsyVisitableItem { } } + + static class FileNodeCreationVisitor extends ContentVisitor.Default { + + FileNodeCreationVisitor() { + } + + @Override + public FileNode visit(File f) { + return new FileNode(f, false); + } + + @Override + public DirectoryNode visit(Directory d) { + return new DirectoryNode(d); + } + + @Override + public LayoutFileNode visit(LayoutFile lf) { + return new LayoutFileNode(lf); + } + + @Override + public LocalFileNode visit(DerivedFile df) { + return new LocalFileNode(df); + } + + @Override + public LocalFileNode visit(LocalFile lf) { + return new LocalFileNode(lf); + } + + @Override + public SlackFileNode visit(SlackFile sf) { + return new SlackFileNode(sf, false); + } + + @Override + protected AbstractNode defaultVisit(Content di) { + throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(), "FileTypeChildren.exception.notSupported.msg", di.toString())); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java index 5cfddc7f7a..25af161f57 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -25,7 +25,6 @@ import java.util.List; import java.util.Observable; import java.util.Observer; import java.util.logging.Level; -import org.openide.nodes.AbstractNode; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; @@ -38,12 +37,6 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.ContentVisitor; -import org.sleuthkit.datamodel.DerivedFile; -import org.sleuthkit.datamodel.Directory; -import org.sleuthkit.datamodel.File; -import org.sleuthkit.datamodel.LayoutFile; -import org.sleuthkit.datamodel.LocalFile; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -51,7 +44,7 @@ import org.sleuthkit.datamodel.TskData; /** * Filters database results by file extension. */ - public final class FileTypesByExtension implements AutopsyVisitableItem { +public final class FileTypesByExtension implements AutopsyVisitableItem { private final SleuthkitCase skCase; @@ -427,37 +420,7 @@ import org.sleuthkit.datamodel.TskData; @Override protected Node createNodeForKey(Content key) { - return key.accept(new ContentVisitor.Default() { - @Override - public FileNode visit(File f) { - return new FileNode(f, false); - } - - @Override - public DirectoryNode visit(Directory d) { - return new DirectoryNode(d); - } - - @Override - public LayoutFileNode visit(LayoutFile lf) { - return new LayoutFileNode(lf); - } - - @Override - public LocalFileNode visit(DerivedFile df) { - return new LocalFileNode(df); - } - - @Override - public LocalFileNode visit(LocalFile lf) { - return new LocalFileNode(lf); - } - - @Override - protected AbstractNode defaultVisit(Content di) { - throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(), "FileTypeChildren.exception.notSupported.msg", di.toString())); - } - }); + return key.accept(new FileTypes.FileNodeCreationVisitor()); } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index 1549d6bdc1..46b16d9af0 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -31,7 +31,6 @@ import java.util.Observer; import java.util.logging.Level; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; -import org.openide.nodes.AbstractNode; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; @@ -42,13 +41,6 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.ContentVisitor; -import org.sleuthkit.datamodel.DerivedFile; -import org.sleuthkit.datamodel.Directory; -import org.sleuthkit.datamodel.File; -import org.sleuthkit.datamodel.LayoutFile; -import org.sleuthkit.datamodel.LocalFile; -import org.sleuthkit.datamodel.SlackFile; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -358,10 +350,9 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi * results */ private void updateDisplayName(String mimeType) { - final long count = new MediaSubTypeNodeChildren(mimeType).calculateItems(skCase, mimeType); - String[] mimeTypeParts = mimeType.split("/"); - //joins up all remaining parts of the mimeType into one sub-type string - super.setDisplayName(StringUtils.join(ArrayUtils.subarray(mimeTypeParts, 1, mimeTypeParts.length), "/") + " (" + count + ")"); + final long count = calculateItems(skCase, mimeType); + //only the part of the mimeType after the media type + super.setDisplayName(StringUtils.substringAfter(mimeType, "/") + " (" + count + ")"); } /** @@ -391,6 +382,48 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi } } + /** + * Get children count without actually loading all nodes + * + * @return count(*) - the number of items that will be shown in this items + * Directory Listing + */ + static private long calculateItems(SleuthkitCase sleuthkitCase, String mime_type) { + try { + return sleuthkitCase.countFilesWhere(createQuery(mime_type)); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error getting file search view count", ex); //NON-NLS + return 0; + } + } + + /** + * Create the portion of the query following WHERE for a query of the + * database for each file which matches the complete MIME type represented + * by this node. Matches against the mime_type column in tsk_files. + * + * @param mimeType - the complete mimetype of the file mediatype/subtype + * + * @return query.toString - portion of SQL query which will follow a WHERE + * clause. + */ + static private String createQuery(String mimeType) { + StringBuilder query = new StringBuilder(); + query.append("(dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()).append(")"); //NON-NLS + query.append(" AND (type IN (").append(TskData.TSK_DB_FILES_TYPE_ENUM.FS.ordinal()).append(","); //NON-NLS + query.append(TskData.TSK_DB_FILES_TYPE_ENUM.CARVED.ordinal()).append(","); + query.append(TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.ordinal()).append(","); + if (!UserPreferences.hideSlackFilesInViewsTree()) { + query.append(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal()).append(","); + } + query.append(TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()).append("))"); + if (UserPreferences.hideKnownFilesInViewsTree()) { + query.append(" AND (known IS NULL OR known != ").append(TskData.FileKnown.KNOWN.getFileKnownValue()).append(")"); //NON-NLS + } + query.append(" AND mime_type = '").append(mimeType).append("'"); //NON-NLS + return query.toString(); + } + /** * Factory for populating the contents of the Media Sub Type Node with the * files that match MimeType which is represented by this position in the @@ -406,21 +439,6 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi this.mimeType = mimeType; } - /** - * Get children count without actually loading all nodes - * - * @return count(*) - the number of items that will be shown in this - * items Directory Listing - */ - private long calculateItems(SleuthkitCase sleuthkitCase, String mime_type) { - try { - return sleuthkitCase.countFilesWhere(createQuery(mime_type)); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error getting file search view count", ex); //NON-NLS - return 0; - } - } - /** * Uses the createQuery method to complete the query, Select * from * tsk_files WHERE. The results from the database will contain the files @@ -442,34 +460,6 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi return true; } - /** - * Create the portion of the query following WHERE for a query of the - * database for each file which matches the complete MIME type - * represented by this node. Matches against the mime_type column in - * tsk_files. - * - * @param mimeType - the complete mimetype of the file mediatype/subtype - * - * @return query.toString - portion of SQL query which will follow a - * WHERE clause. - */ - private String createQuery(String mimeType) { - StringBuilder query = new StringBuilder(); - query.append("(dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()).append(")"); //NON-NLS - query.append(" AND (type IN (").append(TskData.TSK_DB_FILES_TYPE_ENUM.FS.ordinal()).append(","); //NON-NLS - query.append(TskData.TSK_DB_FILES_TYPE_ENUM.CARVED.ordinal()).append(","); - query.append(TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.ordinal()).append(","); - if (!UserPreferences.hideSlackFilesInViewsTree()) { - query.append(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal()).append(","); - } - query.append(TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()).append("))"); - if (UserPreferences.hideKnownFilesInViewsTree()) { - query.append(" AND (known IS NULL OR known != ").append(TskData.FileKnown.KNOWN.getFileKnownValue()).append(")"); //NON-NLS - } - query.append(" AND mime_type = '").append(mimeType).append("'"); //NON-NLS - return query.toString(); - } - @Override public void update(Observable o, Object arg) { refresh(true); @@ -485,43 +475,9 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi */ @Override protected Node createNodeForKey(Content key) { - return key.accept(new ContentVisitor.Default() { - @Override - public FileNode visit(File f) { - return new FileNode(f, false); - } - - @Override - public DirectoryNode visit(Directory d) { - return new DirectoryNode(d); - } - - @Override - public LayoutFileNode visit(LayoutFile lf) { - return new LayoutFileNode(lf); - } - - @Override - public LocalFileNode visit(DerivedFile df) { - return new LocalFileNode(df); - } - - @Override - public LocalFileNode visit(LocalFile lf) { - return new LocalFileNode(lf); - } - - @Override - public SlackFileNode visit(SlackFile sf) { - return new SlackFileNode(sf, false); - } - - @Override - protected AbstractNode defaultVisit(Content di) { - throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(), "FileTypeChildren.exception.notSupported.msg", di.toString())); - } - }); + return key.accept(new FileTypes.FileNodeCreationVisitor()); } + } } From ef0bb45774e3c70a993ef26127b40e0130e7697c Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 3 Jul 2017 14:53:22 -0400 Subject: [PATCH 05/16] 2778 list of thumbnails and long thumbnails now wrap text in HTML Report --- Core/src/org/sleuthkit/autopsy/report/ReportHTML.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java index 0ca661ab38..d7a4ee18bf 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java @@ -670,6 +670,7 @@ class ReportHTML implements TableReportModule { } StringBuilder linkToThumbnail = new StringBuilder(); + linkToThumbnail.append(""); currentRow.add(linkToThumbnail.toString()); totalCount++; @@ -844,7 +845,9 @@ class ReportHTML implements TableReportModule { + //NON-NLS "table td {white-space:nowrap; display: table-cell; padding: 2px 3px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align:left; }\n" + //NON-NLS - "table tr:nth-child(even) td {background: #f3f3f3;}"; //NON-NLS + "table tr:nth-child(even) td {background: #f3f3f3;}\n" + + //NON-NLS + "div#thumbnail_link {max-width: 200px; white-space: pre-wrap; white-space: -moz-pre-wrap; white-space: -pre-wrap; white-space: -o-pre-wrap; word-wrap: break-word;}"; cssOut.write(css); } catch (FileNotFoundException ex) { logger.log(Level.SEVERE, "Could not find index.css file to write to.", ex); //NON-NLS From f2724e746cbae424766cf37e3973249705b6f286 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 3 Jul 2017 16:23:10 -0400 Subject: [PATCH 06/16] 2778 contents of thumbnail cells now allign with top of cell --- Core/src/org/sleuthkit/autopsy/report/ReportHTML.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java index d7a4ee18bf..539930ac3d 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java @@ -843,7 +843,7 @@ class ReportHTML implements TableReportModule { + //NON-NLS "table .right_align_cell{display: table-cell; padding: 2px 4px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align: right; }\n" + //NON-NLS - "table td {white-space:nowrap; display: table-cell; padding: 2px 3px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align:left; }\n" + "table td {white-space:nowrap; display: table-cell; padding: 2px 3px; font: 13px/20px Arial, Helvetica, sans-serif; min-width: 125px; overflow: auto; text-align:left; vertical-align: text-top;}\n" + //NON-NLS "table tr:nth-child(even) td {background: #f3f3f3;}\n" + //NON-NLS From 2c1bfe292754b06340731859aeddb17794ee2101 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 6 Jul 2017 11:20:04 +0200 Subject: [PATCH 07/16] replace todos with jira references --- .../autopsy/directorytree/DirectoryTreeTopComponent.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index ff869c7772..bfb98e5220 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -382,7 +382,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat new Tags(), new Reports())); Node root = new AbstractNode(contentChildren) { - //JMTODO: What is the point of these overrides? + //JIRA-2807: What is the point of these overrides? /** * to override the right click action in the white blank space * area on the directory tree window @@ -433,7 +433,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat Arrays.stream(views.getChildren().getNodes()).forEach(tree::expandNode); tree.collapseNode(views); /* - * JMTODO: What is this supposed to do? Right now it selects + * JIRA-2806: What is this supposed to do? Right now it selects * the data sources node, but the comment seems to indicate * it is supposed to select the first datasource. */ @@ -456,7 +456,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat dataResult.open(); // open the data result top component as well when the directory tree is opened } /* - * JMTODO: What is this supposed to do? + * JIRA-2806: What is this supposed to do? */ // select the first image node, if there is one // (this has to happen after dataResult is opened, because the event From 5c38efbfc4d5d6936bde873fc9461e061c834deb Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dgrove" Date: Fri, 7 Jul 2017 15:01:20 -0400 Subject: [PATCH 08/16] Tweaked the regex to also remove the '$' at the end. --- .../sleuthkit/autopsy/ingest/FactoryClassNameNormalizer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/ingest/FactoryClassNameNormalizer.java b/Core/src/org/sleuthkit/autopsy/ingest/FactoryClassNameNormalizer.java index 8fbeb1d19c..6ac324e31f 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/FactoryClassNameNormalizer.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/FactoryClassNameNormalizer.java @@ -34,7 +34,7 @@ class FactoryClassNameNormalizer { // of their file name. This block of code gets rid of that variable // instance number and helps maitains constant module name over // multiple runs. - String moduleClassName = canonicalClassName.replaceAll("\\d*$", ""); //NON-NLS NON-NLS + String moduleClassName = canonicalClassName.replaceAll("\\$\\d*$", ""); //NON-NLS NON-NLS return moduleClassName; } return canonicalClassName; From 08340e69fd764fc1766cc7e546337e2e4e008ebe Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 3 Jul 2017 17:16:33 +0200 Subject: [PATCH 09/16] calculate child counts background thread and add to displayname when done. --- .../datamodel/FileTypesByExtension.java | 43 +++++++++++--- .../datamodel/FileTypesByMimeType.java | 57 +++++++++++++------ 2 files changed, 75 insertions(+), 25 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java index 8da58193bc..69ccf2ce50 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-17 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,7 +24,9 @@ import java.util.Arrays; import java.util.List; import java.util.Observable; import java.util.Observer; +import java.util.concurrent.ExecutionException; import java.util.logging.Level; +import javax.swing.SwingWorker; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; @@ -46,7 +48,7 @@ import org.sleuthkit.datamodel.TskData; */ public final class FileTypesByExtension implements AutopsyVisitableItem { - private static final Logger logger = Logger.getLogger(FileTypesByExtension.class.getName()); + private final static Logger logger = Logger.getLogger(FileTypesByExtension.class.getName()); private final SleuthkitCase skCase; public FileTypesByExtension(SleuthkitCase skCase) { @@ -280,9 +282,10 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { */ static class FileExtensionNode extends DisplayableItemNode { - FileTypesByExtension.SearchFilterInterface filter; - SleuthkitCase skCase; private final FileTypesByExtObservable notifier; + private FileTypesByExtension.SearchFilterInterface filter; + private SleuthkitCase skCase; + private long childCount = -1; /** * @@ -298,10 +301,11 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { this.notifier = o; init(); o.addObserver(new ByExtNodeObserver()); + } private void init() { - super.setName(filter.getName()); + setName(filter.getName()); updateDisplayName(); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS } @@ -316,10 +320,30 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { } private void updateDisplayName() { - final String count = notifier.shouldShowCounts(skCase) - ? " (" + Long.toString(FileExtensionNodeChildren.calculateItems(skCase, filter)) + ")" - : ""; - super.setDisplayName(filter.getDisplayName() + count); + if (notifier.shouldShowCounts(skCase)) { + setDisplayName(filter.getDisplayName() + ((childCount < 0) ? "(counting...)" + : ("(" + childCount + ")"))); + new SwingWorker() { + @Override + protected Long doInBackground() throws Exception { + return FileExtensionNodeChildren.calculateItems(skCase, filter); + } + + @Override + protected void done() { + try { + childCount = get(); + setDisplayName(filter.getDisplayName() + " (" + childCount + ")"); + } catch (InterruptedException | ExecutionException ex) { + setDisplayName(filter.getDisplayName()); + logger.log(Level.WARNING, "Failed to get count of files for filter " + filter.toString(), ex); + } + } + }.execute(); + } else { + setDisplayName(filter.getDisplayName() + ((childCount < 0) ? "" + : ("(" + childCount + "+)"))); + } } @Override @@ -628,5 +652,6 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { public String getDisplayName(); public List getFilter(); + } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index 2ab11bca97..a98df81967 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -28,7 +28,9 @@ import java.util.HashMap; import java.util.List; import java.util.Observable; import java.util.Observer; +import java.util.concurrent.ExecutionException; import java.util.logging.Level; +import javax.swing.SwingWorker; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.openide.nodes.ChildFactory; @@ -54,6 +56,7 @@ import org.sleuthkit.datamodel.TskData; */ public final class FileTypesByMimeType extends Observable implements AutopsyVisitableItem { + private final static Logger logger = Logger.getLogger(FileTypesByMimeType.class.getName()); private final SleuthkitCase skCase; /** * The nodes of this tree will be determined dynamically by the mimetypes @@ -347,22 +350,21 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi } - /** - * Node which represents the media sub type in the By MIME type tree, the - * media subtype is the portion of the MIME type following the /. - */ class MediaSubTypeNode extends DisplayableItemNode implements Observer { + private long childCount = -1; + private final String mimeType; + private final String subType; + private MediaSubTypeNode(String mimeType) { super(Children.create(new MediaSubTypeNodeChildren(mimeType), true)); - addObserver(this); - init(mimeType); - } - - private void init(String mimeType) { + this.mimeType = mimeType; + this.subType = StringUtils.substringAfter(mimeType, "/"); super.setName(mimeType); - updateDisplayName(mimeType); + updateDisplayName(); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS + + addObserver(this); } /** @@ -372,10 +374,33 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi * @param mimeType - the complete MimeType, needed for accurate query * results */ - private void updateDisplayName(String mimeType) { - final long count = calculateItems(skCase, mimeType); - //only the part of the mimeType after the media type - super.setDisplayName(StringUtils.substringAfter(mimeType, "/") + " (" + count + ")"); + private void updateDisplayName() { + if (shouldShowCounts(skCase)) { + setDisplayName(subType + ((childCount < 0) ? "(counting...)" + : ("(" + childCount + ")"))); + new SwingWorker() { + @Override + protected Long doInBackground() throws Exception { + return calculateItems(skCase, mimeType); + } + + @Override + protected void done() { + try { + childCount = get(); + setDisplayName(subType + " (" + childCount + ")"); + } catch (InterruptedException | ExecutionException ex) { + setDisplayName(subType); + logger.log(Level.WARNING, "Failed to get count of files for mimetype " + mimeType, ex); + } + } + + }. + execute(); + } else { + setDisplayName(subType + ((childCount < 0) ? "" + : ("(" + childCount + "+)"))); + } } /** @@ -390,7 +415,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi } @Override - public T accept(DisplayableItemNodeVisitor v) { + public T accept(DisplayableItemNodeVisitor< T> v) { return v.visit(this); } @@ -401,7 +426,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi @Override public void update(Observable o, Object arg) { - updateDisplayName(getName()); + updateDisplayName(); } } From 408ee20ae0b3004bb9be480047dd99cf541f3513 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 7 Jul 2017 16:13:59 +0200 Subject: [PATCH 10/16] clean up the merge --- .../datamodel/FileTypesByExtension.java | 55 ++++++++----------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java index 69ccf2ce50..bc05816c1c 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -37,7 +37,6 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; -import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; @@ -280,12 +279,12 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { * Node for a specific file type / extension. Children of it will be the * files of that type. */ - static class FileExtensionNode extends DisplayableItemNode { + static class FileExtensionNode extends DisplayableItemNode implements Observer { private final FileTypesByExtObservable notifier; - private FileTypesByExtension.SearchFilterInterface filter; private SleuthkitCase skCase; private long childCount = -1; + private final FileTypesByExtension.SearchFilterInterface filter; /** * @@ -299,24 +298,17 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { this.filter = filter; this.skCase = skCase; this.notifier = o; - init(); - o.addObserver(new ByExtNodeObserver()); - } - - private void init() { setName(filter.getName()); updateDisplayName(); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS + + o.addObserver(this); } - // update the display name when new events are fired - private class ByExtNodeObserver implements Observer { - - @Override - public void update(Observable o, Object arg) { - updateDisplayName(); - } + @Override + public void update(Observable o, Object arg) { + updateDisplayName(); } private void updateDisplayName() { @@ -359,13 +351,19 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { ss = Sheet.createPropertiesSet(); s.put(ss); } - ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.filterType.name"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.filterType.displayName"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.filterType.desc"), filter.getDisplayName())); + ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.filterType.name"), + NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.filterType.displayName"), + NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.filterType.desc"), + filter.getDisplayName())); String extensions = ""; for (String ext : filter.getFilter()) { extensions += "'" + ext + "', "; } extensions = extensions.substring(0, extensions.lastIndexOf(',')); - ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.name"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.displayName"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.desc"), extensions)); + ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.name"), + NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.displayName"), + NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.desc"), + extensions)); return s; } @@ -388,11 +386,10 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { * Child node factory for a specific file type - does the database * query. */ - private static class FileExtensionNodeChildren extends ChildFactory.Detachable { + private static class FileExtensionNodeChildren extends ChildFactory.Detachable implements Observer { private final SleuthkitCase skCase; private final FileTypesByExtension.SearchFilterInterface filter; - private static final Logger LOGGER = Logger.getLogger(FileExtensionNodeChildren.class.getName()); private final Observable notifier; /** @@ -412,25 +409,20 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { @Override protected void addNotify() { if (notifier != null) { - notifier.addObserver(observer); + notifier.addObserver(this); } } @Override protected void removeNotify() { if (notifier != null) { - notifier.deleteObserver(observer); + notifier.deleteObserver(this); } } - private final Observer observer = new FileTypeChildFactoryObserver(); - // Cause refresh of children if there are changes - private class FileTypeChildFactoryObserver implements Observer { - - @Override - public void update(Observable o, Object arg) { - refresh(true); - } + @Override + public void update(Observable o, Object arg) { + refresh(true); } /** @@ -450,10 +442,9 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { @Override protected boolean createKeys(List list) { try { - List files = skCase.findAllFilesWhere(createQuery(filter)); - list.addAll(files); + list.addAll(skCase.findAllFilesWhere(createQuery(filter))); } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS + logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS } return true; } From f4ef33a0d416c189cc39e8691ed04c54295671f6 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 10 Jul 2017 15:22:19 +0200 Subject: [PATCH 11/16] use common base class that does bg lookup of counts and has threshold. --- .../datamodel/AbstractContentChildren.java | 10 +- .../autopsy/datamodel/FileTypes.java | 106 +++++- .../datamodel/FileTypesByExtension.java | 341 +++++++----------- .../datamodel/FileTypesByMimeType.java | 85 +---- 4 files changed, 259 insertions(+), 283 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java index eb736069a7..03066faa55 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java @@ -51,8 +51,10 @@ abstract class AbstractContentChildren extends Keys { * Uses lazy Content.Keys */ AbstractContentChildren() { - /*This was turned off because we were getting out of memory errors when the - filter nodes were hiding nodes. Turning this off seemed to help */ + /* + * This was turned off because we were getting out of memory errors when + * the filter nodes were hiding nodes. Turning this off seemed to help + */ super(false); //don't use lazy behavior } @@ -138,7 +140,7 @@ abstract class AbstractContentChildren extends Keys { @Override public AbstractNode visit(FileTypesByExtension sf) { - return new org.sleuthkit.autopsy.datamodel.FileTypesByExtension.FileTypesByExtNode(sf.getSleuthkitCase(), null); + return sf.new FileTypesByExtNode(sf.getSleuthkitCase(), null); } @Override @@ -198,7 +200,7 @@ abstract class AbstractContentChildren extends Keys { @Override public AbstractNode visit(FileTypes ft) { - return new FileTypesNode(ft.getSleuthkitCase()); + return ft.new FileTypesNode(); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java index f3941d563e..773934c1cd 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java @@ -19,10 +19,18 @@ package org.sleuthkit.autopsy.datamodel; import java.util.Arrays; +import java.util.Observable; +import java.util.Observer; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import javax.swing.SwingWorker; import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; import org.openide.nodes.Sheet; +import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentVisitor; import org.sleuthkit.datamodel.DerivedFile; @@ -32,13 +40,17 @@ import org.sleuthkit.datamodel.LayoutFile; import org.sleuthkit.datamodel.LocalFile; import org.sleuthkit.datamodel.SlackFile; import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; /** * File Types node support */ public final class FileTypes implements AutopsyVisitableItem { + private final static Logger logger = Logger.getLogger(FileTypes.class.getName()); + private final SleuthkitCase skCase; + private boolean showCounts = true; FileTypes(SleuthkitCase skCase) { this.skCase = skCase; @@ -53,19 +65,38 @@ public final class FileTypes implements AutopsyVisitableItem { return skCase; } + /** + * Should the nodes show counts? + * + * + * @return True, unless the DB has more than 200k rows. + */ + boolean shouldShowCounts() { + if (showCounts) { + try { + if (skCase.countFilesWhere("1=1") > 200000) { + showCounts = false; + } + } catch (TskCoreException tskCoreException) { + showCounts = false; + logger.log(Level.SEVERE, "Error counting files.", tskCoreException); + } + } + return showCounts; + } + @NbBundle.Messages("FileTypes.name.text=File Types") + static private final String NAME = Bundle.FileTypes_name_text(); + /** * Node which will contain By Mime Type and By Extension nodes. */ - public static final class FileTypesNode extends DisplayableItemNode { + public final class FileTypesNode extends DisplayableItemNode { - @NbBundle.Messages("FileTypes.name.text=File Types") - private static final String NAME = Bundle.FileTypes_name_text(); - - FileTypesNode(SleuthkitCase sleuthkitCase) { + FileTypesNode() { super(new RootContentChildren(Arrays.asList( - new FileTypesByExtension(sleuthkitCase), - new FileTypesByMimeType(sleuthkitCase) - )), Lookups.singleton(NAME)); + new FileTypesByExtension(FileTypes.this), + new FileTypesByMimeType(FileTypes.this))), + Lookups.singleton(NAME)); setName(NAME); setDisplayName(NAME); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); @@ -149,4 +180,63 @@ public final class FileTypes implements AutopsyVisitableItem { throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(), "FileTypeChildren.exception.notSupported.msg", di.toString())); } } + + static abstract class BGCountUpdatingNode extends DisplayableItemNode implements Observer { + + private long childCount = -1; + private final SleuthkitCase skCase; + private FileTypes typesRoot; + + BGCountUpdatingNode(FileTypes typesRoot, Children children) { + this(typesRoot, children, null); + + } + + BGCountUpdatingNode(FileTypes typesRoot, Children children, Lookup lookup) { + super(children, lookup); + this.typesRoot = typesRoot; + this.skCase = typesRoot.getSleuthkitCase(); + } + + @Override + public void update(Observable o, Object arg) { + updateDisplayName(); + } + + abstract String getDisplayNameBase(); + + abstract String geQuery(); + + /** + * Updates the display name of the mediaSubTypeNode to include the count + * of files which it represents. + */ + void updateDisplayName() { + if (typesRoot.shouldShowCounts()) { + //only show "(counting...)" the first time, otherwise it is distracting. + setDisplayName(getDisplayNameBase() + ((childCount < 0) ? "(counting...)" + : ("(" + childCount + ")"))); + new SwingWorker() { + @Override + protected Long doInBackground() throws Exception { + return skCase.countFilesWhere(geQuery()); + } + + @Override + protected void done() { + try { + childCount = get(); + setDisplayName(getDisplayNameBase() + " (" + childCount + ")"); + } catch (InterruptedException | ExecutionException ex) { + setDisplayName(getDisplayNameBase()); + logger.log(Level.WARNING, "Failed to get count of files for " + getDisplayNameBase(), ex); + } + } + }.execute(); + } else { + setDisplayName(getDisplayNameBase() + ((childCount < 0) ? "" + : ("(" + childCount + "+)"))); + } + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java index bc05816c1c..726a6fa67b 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -24,9 +24,7 @@ import java.util.Arrays; import java.util.List; import java.util.Observable; import java.util.Observer; -import java.util.concurrent.ExecutionException; import java.util.logging.Level; -import javax.swing.SwingWorker; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; @@ -49,9 +47,11 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { private final static Logger logger = Logger.getLogger(FileTypesByExtension.class.getName()); private final SleuthkitCase skCase; + private final FileTypes typesRoot; - public FileTypesByExtension(SleuthkitCase skCase) { - this.skCase = skCase; + public FileTypesByExtension(FileTypes typesRoot) { + this.skCase = typesRoot.getSleuthkitCase(); + this.typesRoot = typesRoot; } public SleuthkitCase getSleuthkitCase() { @@ -67,16 +67,18 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { * Listens for case and ingest invest. Updates observers when events are * fired. FileType and FileTypes nodes are all listening to this. */ - static private class FileTypesByExtObservable extends Observable { + private class FileTypesByExtObservable extends Observable { - private boolean showCounts = true; private final PropertyChangeListener pcl; - private FileTypesByExtObservable(SleuthkitCase skCase) { + private FileTypesByExtObservable() { super(); this.pcl = (PropertyChangeEvent evt) -> { String eventType = evt.getPropertyName(); - if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString()) || eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString()) || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { + if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString()) + || eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) + || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString()) + || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { /** * Checking for a current case is a stop gap measure until a * different way of handling the closing of cases is worked @@ -85,7 +87,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { */ try { Case.getCurrentCase(); - shouldShowCounts(skCase); + typesRoot.shouldShowCounts(); update(); } catch (IllegalStateException notUsed) { /** @@ -106,26 +108,6 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { } - /** - * Should the nodes show counts? - * - * - * @return True, unless the DB has more than 200k rows. - */ - private boolean shouldShowCounts(SleuthkitCase skCase) { - if (showCounts) { - try { - if (skCase.countFilesWhere("1=1") > 200000) { - showCounts = false; - } - } catch (TskCoreException tskCoreException) { - showCounts = false; - logger.log(Level.SEVERE, "Error counting files.", tskCoreException); - } - } - return showCounts; - } - private void removeListeners() { deleteObservers(); IngestManager.getInstance().removeIngestJobEventListener(pcl); @@ -138,13 +120,13 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { notifyObservers(); } } + private static final String FNAME = NbBundle.getMessage(FileTypesByExtNode.class, "FileTypesByExtNode.fname.text"); /** * Node for root of file types view. Children are nodes for specific types. */ - static class FileTypesByExtNode extends DisplayableItemNode { + class FileTypesByExtNode extends DisplayableItemNode { - private static final String FNAME = NbBundle.getMessage(FileTypesByExtNode.class, "FileTypesByExtNode.fname.text"); private final FileTypesByExtension.RootFilter filter; /** @@ -154,9 +136,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { * something to provide a sub-node. */ FileTypesByExtNode(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter) { - super(Children.create(new FileTypesByExtNodeChildren(skCase, filter, null), true), Lookups.singleton(filter == null ? FNAME : filter.getName())); - this.filter = filter; - init(); + this(skCase, filter, null); } /** @@ -167,12 +147,10 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { * provides updates on events */ private FileTypesByExtNode(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter, FileTypesByExtObservable o) { - super(Children.create(new FileTypesByExtNodeChildren(skCase, filter, o), true), Lookups.singleton(filter == null ? FNAME : filter.getName())); + super(Children.create(new FileTypesByExtNodeChildren(skCase, filter, o), true), + Lookups.singleton(filter == null ? FNAME : filter.getName())); this.filter = filter; - init(); - } - private void init() { // root node of tree if (filter == null) { super.setName(FNAME); @@ -222,68 +200,65 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { return getClass().getName(); } - private static class FileTypesByExtNodeChildren extends ChildFactory { + } - private final SleuthkitCase skCase; - private final FileTypesByExtension.RootFilter filter; - private final FileTypesByExtObservable notifier; + private class FileTypesByExtNodeChildren extends ChildFactory { - /** - * - * @param skCase - * @param filter Is null for root node - * @param o Observable that provides updates based on events - * being fired (or null if one needs to be created) - */ - private FileTypesByExtNodeChildren(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter, FileTypesByExtObservable o) { - super(); - this.skCase = skCase; - this.filter = filter; - if (o == null) { - this.notifier = new FileTypesByExtObservable(skCase); - } else { - this.notifier = o; - } - } + private final SleuthkitCase skCase; + private final FileTypesByExtension.RootFilter filter; + private final FileTypesByExtObservable notifier; - @Override - protected boolean createKeys(List list) { - // root node - if (filter == null) { - list.addAll(Arrays.asList(FileTypesByExtension.RootFilter.values())); - } // document and executable has another level of nodes - else if (filter.equals(FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER)) { - list.addAll(Arrays.asList(FileTypesByExtension.DocumentFilter.values())); - } else if (filter.equals(FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER)) { - list.addAll(Arrays.asList(FileTypesByExtension.ExecutableFilter.values())); - } - return true; - } - - @Override - protected Node createNodeForKey(FileTypesByExtension.SearchFilterInterface key) { - // make new nodes for the sub-nodes - if (key.getName().equals(FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER.getName())) { - return new FileTypesByExtNode(skCase, FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER, notifier); - } else if (key.getName().equals(FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER.getName())) { - return new FileTypesByExtNode(skCase, FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER, notifier); - } else { - return new FileExtensionNode(key, skCase, notifier); - } + /** + * + * @param skCase + * @param filter Is null for root node + * @param o Observable that provides updates based on events being + * fired (or null if one needs to be created) + */ + private FileTypesByExtNodeChildren(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter, FileTypesByExtObservable o) { + super(); + this.skCase = skCase; + this.filter = filter; + if (o == null) { + this.notifier = new FileTypesByExtObservable(); + } else { + this.notifier = o; } } + @Override + protected boolean createKeys(List list) { + // root node + if (filter == null) { + list.addAll(Arrays.asList(FileTypesByExtension.RootFilter.values())); + } // document and executable has another level of nodes + else if (filter.equals(FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER)) { + list.addAll(Arrays.asList(FileTypesByExtension.DocumentFilter.values())); + } else if (filter.equals(FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER)) { + list.addAll(Arrays.asList(FileTypesByExtension.ExecutableFilter.values())); + } + return true; + } + + @Override + protected Node createNodeForKey(FileTypesByExtension.SearchFilterInterface key) { + // make new nodes for the sub-nodes + if (key.getName().equals(FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER.getName())) { + return new FileTypesByExtNode(skCase, FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER, notifier); + } else if (key.getName().equals(FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER.getName())) { + return new FileTypesByExtNode(skCase, FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER, notifier); + } else { + return new FileExtensionNode(key, skCase, notifier); + } + } } /** * Node for a specific file type / extension. Children of it will be the * files of that type. */ - static class FileExtensionNode extends DisplayableItemNode implements Observer { + class FileExtensionNode extends FileTypes.BGCountUpdatingNode { - private final FileTypesByExtObservable notifier; - private SleuthkitCase skCase; - private long childCount = -1; private final FileTypesByExtension.SearchFilterInterface filter; /** @@ -294,11 +269,9 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { * should refresh */ FileExtensionNode(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, FileTypesByExtObservable o) { - super(Children.create(new FileExtensionNodeChildren(filter, skCase, o), true), Lookups.singleton(filter.getDisplayName())); + super(typesRoot, Children.create(new FileExtensionNodeChildren(filter, skCase, o), true), + Lookups.singleton(filter.getDisplayName())); this.filter = filter; - this.skCase = skCase; - this.notifier = o; - setName(filter.getName()); updateDisplayName(); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS @@ -306,38 +279,6 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { o.addObserver(this); } - @Override - public void update(Observable o, Object arg) { - updateDisplayName(); - } - - private void updateDisplayName() { - if (notifier.shouldShowCounts(skCase)) { - setDisplayName(filter.getDisplayName() + ((childCount < 0) ? "(counting...)" - : ("(" + childCount + ")"))); - new SwingWorker() { - @Override - protected Long doInBackground() throws Exception { - return FileExtensionNodeChildren.calculateItems(skCase, filter); - } - - @Override - protected void done() { - try { - childCount = get(); - setDisplayName(filter.getDisplayName() + " (" + childCount + ")"); - } catch (InterruptedException | ExecutionException ex) { - setDisplayName(filter.getDisplayName()); - logger.log(Level.WARNING, "Failed to get count of files for filter " + filter.toString(), ex); - } - } - }.execute(); - } else { - setDisplayName(filter.getDisplayName() + ((childCount < 0) ? "" - : ("(" + childCount + "+)"))); - } - } - @Override public T accept(DisplayableItemNodeVisitor v) { return v.visit(this); @@ -355,15 +296,11 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.filterType.displayName"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.filterType.desc"), filter.getDisplayName())); - String extensions = ""; - for (String ext : filter.getFilter()) { - extensions += "'" + ext + "', "; - } - extensions = extensions.substring(0, extensions.lastIndexOf(',')); + ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.name"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.displayName"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.desc"), - extensions)); + String.join(", ", filter.getFilter()))); return s; } @@ -382,91 +319,87 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { return DisplayableItemNode.FILE_PARENT_NODE_KEY; } + @Override + String getDisplayNameBase() { + return filter.getDisplayName(); + } + + @Override + String geQuery() { + return createQuery(filter); + } + + } + + private static String createQuery(FileTypesByExtension.SearchFilterInterface filter) { + StringBuilder query = new StringBuilder(); + query.append("(dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()).append(")"); //NON-NLS + if (UserPreferences.hideKnownFilesInViewsTree()) { + query.append(" AND (known IS NULL OR known != ").append(TskData.FileKnown.KNOWN.getFileKnownValue()).append(")"); //NON-NLS + } + query.append(" AND (NULL"); //NON-NLS + for (String s : filter.getFilter()) { + query.append(" OR LOWER(name) LIKE LOWER('%").append(s).append("')"); //NON-NLS + } + query.append(')'); + return query.toString(); + } + + /** + * Child node factory for a specific file type - does the database query. + */ + private static class FileExtensionNodeChildren extends ChildFactory.Detachable implements Observer { + + private final SleuthkitCase skCase; + private final FileTypesByExtension.SearchFilterInterface filter; + private final Observable notifier; + /** - * Child node factory for a specific file type - does the database - * query. + * + * @param filter Extensions to display + * @param skCase + * @param o Observable that will notify when there could be new + * data to display */ - private static class FileExtensionNodeChildren extends ChildFactory.Detachable implements Observer { + private FileExtensionNodeChildren(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { + super(); + this.filter = filter; + this.skCase = skCase; + notifier = o; + } - private final SleuthkitCase skCase; - private final FileTypesByExtension.SearchFilterInterface filter; - private final Observable notifier; - - /** - * - * @param filter Extensions to display - * @param skCase - * @param o Observable that will notify when there could be new - * data to display - */ - private FileExtensionNodeChildren(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { - super(); - this.filter = filter; - this.skCase = skCase; - notifier = o; + @Override + protected void addNotify() { + if (notifier != null) { + notifier.addObserver(this); } + } - @Override - protected void addNotify() { - if (notifier != null) { - notifier.addObserver(this); - } + @Override + protected void removeNotify() { + if (notifier != null) { + notifier.deleteObserver(this); } + } - @Override - protected void removeNotify() { - if (notifier != null) { - notifier.deleteObserver(this); - } - } + @Override + public void update(Observable o, Object arg) { + refresh(true); + } - @Override - public void update(Observable o, Object arg) { - refresh(true); + @Override + protected boolean createKeys(List list) { + try { + list.addAll(skCase.findAllFilesWhere(createQuery(filter))); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS } + return true; + } - /** - * Get children count without actually loading all nodes - * - * @return - */ - private static long calculateItems(SleuthkitCase sleuthkitCase, FileTypesByExtension.SearchFilterInterface filter) { - try { - return sleuthkitCase.countFilesWhere(createQuery(filter)); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error getting file search view count", ex); //NON-NLS - return 0; - } - } - - @Override - protected boolean createKeys(List list) { - try { - list.addAll(skCase.findAllFilesWhere(createQuery(filter))); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS - } - return true; - } - - private static String createQuery(FileTypesByExtension.SearchFilterInterface filter) { - StringBuilder query = new StringBuilder(); - query.append("(dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()).append(")"); //NON-NLS - if (UserPreferences.hideKnownFilesInViewsTree()) { - query.append(" AND (known IS NULL OR known != ").append(TskData.FileKnown.KNOWN.getFileKnownValue()).append(")"); //NON-NLS - } - query.append(" AND (NULL"); //NON-NLS - for (String s : filter.getFilter()) { - query.append(" OR LOWER(name) LIKE LOWER('%").append(s).append("')"); //NON-NLS - } - query.append(')'); - return query.toString(); - } - - @Override - protected Node createNodeForKey(Content key) { - return key.accept(new FileTypes.FileNodeCreationVisitor()); - } + @Override + protected Node createNodeForKey(Content key) { + return key.accept(new FileTypes.FileNodeCreationVisitor()); } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index a98df81967..50c0813d05 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -28,9 +28,7 @@ import java.util.HashMap; import java.util.List; import java.util.Observable; import java.util.Observer; -import java.util.concurrent.ExecutionException; import java.util.logging.Level; -import javax.swing.SwingWorker; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.openide.nodes.ChildFactory; @@ -65,8 +63,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi */ private final HashMap> existingMimeTypes = new HashMap<>(); private static final Logger LOGGER = Logger.getLogger(FileTypesByMimeType.class.getName()); - - private boolean showCounts = true; + private final FileTypes typesRoot; private void removeListeners() { deleteObservers(); @@ -140,7 +137,9 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi notifyObservers(); } - FileTypesByMimeType(SleuthkitCase skCase) { + FileTypesByMimeType( FileTypes typesRoot) { + this.skCase = typesRoot.getSleuthkitCase(); + this.typesRoot = typesRoot; this.pcl = (PropertyChangeEvent evt) -> { String eventType = evt.getPropertyName(); if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) @@ -154,8 +153,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi */ try { Case.getCurrentCase(); - shouldShowCounts(skCase); - + typesRoot.shouldShowCounts(); populateHashMap(); } catch (IllegalStateException notUsed) { /** @@ -170,30 +168,9 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi }; IngestManager.getInstance().addIngestJobEventListener(pcl); Case.addPropertyChangeListener(pcl); - this.skCase = skCase; populateHashMap(); } - /** - * Should the nodes show counts? - * - * - * @return True, unless the DB has more than 200k rows. - */ - private boolean shouldShowCounts(final SleuthkitCase skCase) { - if (showCounts) { - try { - if (skCase.countFilesWhere("1=1") > 200000) { - showCounts = false; - } - } catch (TskCoreException tskCoreException) { - showCounts = false; - LOGGER.log(Level.SEVERE, "Error counting files.", tskCoreException); - } - } - return showCounts; - } - @Override public T accept(AutopsyItemVisitor v) { return v.visit(this); @@ -350,14 +327,13 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi } - class MediaSubTypeNode extends DisplayableItemNode implements Observer { + class MediaSubTypeNode extends FileTypes.BGCountUpdatingNode { - private long childCount = -1; private final String mimeType; private final String subType; private MediaSubTypeNode(String mimeType) { - super(Children.create(new MediaSubTypeNodeChildren(mimeType), true)); + super(typesRoot,Children.create(new MediaSubTypeNodeChildren(mimeType), true)); this.mimeType = mimeType; this.subType = StringUtils.substringAfter(mimeType, "/"); super.setName(mimeType); @@ -367,42 +343,6 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi addObserver(this); } - /** - * Updates the display name of the mediaSubTypeNode to include the count - * of files which it represents. - * - * @param mimeType - the complete MimeType, needed for accurate query - * results - */ - private void updateDisplayName() { - if (shouldShowCounts(skCase)) { - setDisplayName(subType + ((childCount < 0) ? "(counting...)" - : ("(" + childCount + ")"))); - new SwingWorker() { - @Override - protected Long doInBackground() throws Exception { - return calculateItems(skCase, mimeType); - } - - @Override - protected void done() { - try { - childCount = get(); - setDisplayName(subType + " (" + childCount + ")"); - } catch (InterruptedException | ExecutionException ex) { - setDisplayName(subType); - logger.log(Level.WARNING, "Failed to get count of files for mimetype " + mimeType, ex); - } - } - - }. - execute(); - } else { - setDisplayName(subType + ((childCount < 0) ? "" - : ("(" + childCount + "+)"))); - } - } - /** * This returns true because any MediaSubTypeNode that exists is going * to be a bottom level node in the Tree view on the left of Autopsy. @@ -428,6 +368,17 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi public void update(Observable o, Object arg) { updateDisplayName(); } + + @Override + String getDisplayNameBase() { + return subType; + } + + @Override + String geQuery() { + return createQuery(mimeType); + } + } /** From 214fe440c8956f876595aa2e3f4fd639021ece8a Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 12 Jul 2017 12:19:59 +0200 Subject: [PATCH 12/16] fix typo --- Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java | 4 ++-- .../org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java | 2 +- .../org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java index 773934c1cd..956708b66b 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java @@ -205,7 +205,7 @@ public final class FileTypes implements AutopsyVisitableItem { abstract String getDisplayNameBase(); - abstract String geQuery(); + abstract String getQuery(); /** * Updates the display name of the mediaSubTypeNode to include the count @@ -219,7 +219,7 @@ public final class FileTypes implements AutopsyVisitableItem { new SwingWorker() { @Override protected Long doInBackground() throws Exception { - return skCase.countFilesWhere(geQuery()); + return skCase.countFilesWhere(getQuery()); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java index 726a6fa67b..b3d2020322 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -325,7 +325,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { } @Override - String geQuery() { + String getQuery() { return createQuery(filter); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index 50c0813d05..01ccc24267 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -375,7 +375,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi } @Override - String geQuery() { + String getQuery() { return createQuery(mimeType); } From 2afd45d861823bd8ca2bf0857c8eda32e2ea882a Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 11 Jul 2017 22:32:24 +0200 Subject: [PATCH 13/16] use a single query to get all mime type counts. --- .../autopsy/datamodel/FileTypes.java | 7 +- .../datamodel/FileTypesByExtension.java | 5 +- .../datamodel/FileTypesByMimeType.java | 77 ++++++++----------- 3 files changed, 40 insertions(+), 49 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java index 956708b66b..6b8e502efc 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java @@ -96,7 +96,7 @@ public final class FileTypes implements AutopsyVisitableItem { super(new RootContentChildren(Arrays.asList( new FileTypesByExtension(FileTypes.this), new FileTypesByMimeType(FileTypes.this))), - Lookups.singleton(NAME)); + Lookups.singleton(NAME)); setName(NAME); setDisplayName(NAME); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); @@ -205,7 +205,7 @@ public final class FileTypes implements AutopsyVisitableItem { abstract String getDisplayNameBase(); - abstract String getQuery(); + abstract long calculateItems() throws Exception; /** * Updates the display name of the mediaSubTypeNode to include the count @@ -219,7 +219,7 @@ public final class FileTypes implements AutopsyVisitableItem { new SwingWorker() { @Override protected Long doInBackground() throws Exception { - return skCase.countFilesWhere(getQuery()); + return calculateItems(); } @Override @@ -232,6 +232,7 @@ public final class FileTypes implements AutopsyVisitableItem { logger.log(Level.WARNING, "Failed to get count of files for " + getDisplayNameBase(), ex); } } + }.execute(); } else { setDisplayName(getDisplayNameBase() + ((childCount < 0) ? "" diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java index b3d2020322..57c0663e4a 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -325,10 +325,9 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { } @Override - String getQuery() { - return createQuery(filter); + long calculateItems() throws Exception { + return skCase.countFilesWhere(createQuery(filter)); } - } private static String createQuery(FileTypesByExtension.SearchFilterInterface filter) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index 01ccc24267..7731606da4 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -26,10 +26,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Observable; import java.util.Observer; import java.util.logging.Level; -import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; @@ -61,7 +61,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi * which exist in the database. This hashmap will store them with the media * type as the key and a list of media subtypes as the value. */ - private final HashMap> existingMimeTypes = new HashMap<>(); + private final HashMap> existingMimeTypes = new HashMap<>(); private static final Logger LOGGER = Logger.getLogger(FileTypesByMimeType.class.getName()); private final FileTypes typesRoot; @@ -95,35 +95,35 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi * files in it, and populate the hashmap with those results. */ private void populateHashMap() { - StringBuilder allDistinctMimeTypesQuery = new StringBuilder(); - allDistinctMimeTypesQuery.append("SELECT DISTINCT mime_type from tsk_files where mime_type IS NOT null"); //NON-NLS - allDistinctMimeTypesQuery.append(" AND dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()); //NON-NLS - allDistinctMimeTypesQuery.append(" AND (type IN (").append(TskData.TSK_DB_FILES_TYPE_ENUM.FS.ordinal()).append(","); //NON-NLS - allDistinctMimeTypesQuery.append(TskData.TSK_DB_FILES_TYPE_ENUM.CARVED.ordinal()).append(","); - allDistinctMimeTypesQuery.append(TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.ordinal()).append(","); - if (!UserPreferences.hideSlackFilesInViewsTree()) { - allDistinctMimeTypesQuery.append(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal()).append(","); - } - allDistinctMimeTypesQuery.append(TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()).append("))"); + String query = "SELECT mime_type,count(*) as count from tsk_files " + + " where mime_type IS NOT null " + + " AND dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + + " AND (type IN (" + + TskData.TSK_DB_FILES_TYPE_ENUM.FS.ordinal() + "," + + TskData.TSK_DB_FILES_TYPE_ENUM.CARVED.ordinal() + "," + + TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.ordinal() + "," + + TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal() + + ((UserPreferences.hideSlackFilesInViewsTree()) ? "" + : ("," + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal())) + + "))" + " GROUP BY mime_type"; synchronized (existingMimeTypes) { existingMimeTypes.clear(); if (skCase == null) { return; } - try (SleuthkitCase.CaseDbQuery dbQuery = skCase.executeQuery(allDistinctMimeTypesQuery.toString())) { + try (SleuthkitCase.CaseDbQuery dbQuery = skCase.executeQuery(query)) { ResultSet resultSet = dbQuery.getResultSet(); while (resultSet.next()) { final String mime_type = resultSet.getString("mime_type"); //NON-NLS if (!mime_type.isEmpty()) { - String mimeType[] = mime_type.split("/"); //if the mime_type contained multiple slashes then everything after the first slash will become the subtype - final String mimeMediaSubType = StringUtils.join(ArrayUtils.subarray(mimeType, 1, mimeType.length), "/"); - if (mimeType.length > 1 && !mimeType[0].isEmpty() && !mimeMediaSubType.isEmpty()) { - if (!existingMimeTypes.containsKey(mimeType[0])) { - existingMimeTypes.put(mimeType[0], new ArrayList<>()); - } - existingMimeTypes.get(mimeType[0]).add(mimeMediaSubType); + final String mediaType = StringUtils.substringBefore(mime_type, "/"); + final String subType = StringUtils.removeStart(mime_type, mediaType + "/"); + if (!mediaType.isEmpty() && !subType.isEmpty()) { + final long count = resultSet.getLong("count"); + existingMimeTypes.computeIfAbsent(mediaType, t -> new HashMap<>()) + .put(subType, count); } } } @@ -133,11 +133,10 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi } setChanged(); - notifyObservers(); } - FileTypesByMimeType( FileTypes typesRoot) { + FileTypesByMimeType(FileTypes typesRoot) { this.skCase = typesRoot.getSleuthkitCase(); this.typesRoot = typesRoot; this.pcl = (PropertyChangeEvent evt) -> { @@ -228,7 +227,9 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi } boolean isEmpty() { - return existingMimeTypes.isEmpty(); + synchronized (existingMimeTypes) { + return existingMimeTypes.isEmpty(); + } } } @@ -310,7 +311,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi @Override protected boolean createKeys(List mediaTypeNodes) { - mediaTypeNodes.addAll(existingMimeTypes.get(mediaType)); + mediaTypeNodes.addAll(existingMimeTypes.get(mediaType).keySet()); return true; } @@ -333,7 +334,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi private final String subType; private MediaSubTypeNode(String mimeType) { - super(typesRoot,Children.create(new MediaSubTypeNodeChildren(mimeType), true)); + super(typesRoot, Children.create(new MediaSubTypeNodeChildren(mimeType), true)); this.mimeType = mimeType; this.subType = StringUtils.substringAfter(mimeType, "/"); super.setName(mimeType); @@ -374,25 +375,15 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi return subType; } + /** + * Get children count without actually loading all nodes + * + * @return count(*) - the number of items that will be shown in this + * items Directory Listing + */ @Override - String getQuery() { - return createQuery(mimeType); - } - - } - - /** - * Get children count without actually loading all nodes - * - * @return count(*) - the number of items that will be shown in this items - * Directory Listing - */ - static private long calculateItems(SleuthkitCase sleuthkitCase, String mime_type) { - try { - return sleuthkitCase.countFilesWhere(createQuery(mime_type)); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error getting file search view count", ex); //NON-NLS - return 0; + long calculateItems() { + return existingMimeTypes.get(StringUtils.substringBefore(mimeType, "/")).get(subType); } } From 28966563747e9503ea37cb8448df4f31dd0c10d8 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 12 Jul 2017 12:36:31 +0200 Subject: [PATCH 14/16] remove unused field --- Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java index 6b8e502efc..5b1d1597af 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java @@ -184,18 +184,15 @@ public final class FileTypes implements AutopsyVisitableItem { static abstract class BGCountUpdatingNode extends DisplayableItemNode implements Observer { private long childCount = -1; - private final SleuthkitCase skCase; private FileTypes typesRoot; BGCountUpdatingNode(FileTypes typesRoot, Children children) { this(typesRoot, children, null); - } BGCountUpdatingNode(FileTypes typesRoot, Children children, Lookup lookup) { super(children, lookup); this.typesRoot = typesRoot; - this.skCase = typesRoot.getSleuthkitCase(); } @Override @@ -232,7 +229,6 @@ public final class FileTypes implements AutopsyVisitableItem { logger.log(Level.WARNING, "Failed to get count of files for " + getDisplayNameBase(), ex); } } - }.execute(); } else { setDisplayName(getDisplayNameBase() + ((childCount < 0) ? "" From 95083475010371596fa5dc0a4504dfae2a628e51 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 12 Jul 2017 12:39:13 +0200 Subject: [PATCH 15/16] remove outdated comment --- .../sleuthkit/autopsy/datamodel/FileTypesByMimeType.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index 7731606da4..047e105658 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -375,12 +375,6 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi return subType; } - /** - * Get children count without actually loading all nodes - * - * @return count(*) - the number of items that will be shown in this - * items Directory Listing - */ @Override long calculateItems() { return existingMimeTypes.get(StringUtils.substringBefore(mimeType, "/")).get(subType); From cb8acc0cfbb79aec268679ba9f2342226ddf2ff2 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 12 Jul 2017 16:44:09 +0200 Subject: [PATCH 16/16] limit exception type and other cleanup --- .../autopsy/datamodel/FileTypes.java | 29 +++++++----- .../datamodel/FileTypesByExtension.java | 2 +- .../datamodel/FileTypesByMimeType.java | 46 +++++++------------ 3 files changed, 36 insertions(+), 41 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java index 5b1d1597af..0878a5a451 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java @@ -74,12 +74,12 @@ public final class FileTypes implements AutopsyVisitableItem { boolean shouldShowCounts() { if (showCounts) { try { - if (skCase.countFilesWhere("1=1") > 200000) { + if (skCase.countFilesWhere("1=1") > 200000) { //NON-NLS showCounts = false; } } catch (TskCoreException tskCoreException) { showCounts = false; - logger.log(Level.SEVERE, "Error counting files.", tskCoreException); + logger.log(Level.SEVERE, "Error counting files.", tskCoreException); //NON-NLS } } return showCounts; @@ -99,7 +99,7 @@ public final class FileTypes implements AutopsyVisitableItem { Lookups.singleton(NAME)); setName(NAME); setDisplayName(NAME); - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); //NON-NLS } @Override @@ -202,37 +202,44 @@ public final class FileTypes implements AutopsyVisitableItem { abstract String getDisplayNameBase(); - abstract long calculateItems() throws Exception; + /** + * Calculate the number of children of this node, possibly by querying + * the DB. + * + * @return @throws TskCoreException if there was an error querying the + * DB to calculate the number of children. + */ + abstract long calculateChildCount() throws TskCoreException; /** * Updates the display name of the mediaSubTypeNode to include the count * of files which it represents. */ + @NbBundle.Messages("FileTypes.bgCounting.placeholder=(counting...)") void updateDisplayName() { if (typesRoot.shouldShowCounts()) { //only show "(counting...)" the first time, otherwise it is distracting. - setDisplayName(getDisplayNameBase() + ((childCount < 0) ? "(counting...)" - : ("(" + childCount + ")"))); + setDisplayName(getDisplayNameBase() + ((childCount < 0) ? Bundle.FileTypes_bgCounting_placeholder() + : ("(" + childCount + ")"))); //NON-NLS new SwingWorker() { @Override protected Long doInBackground() throws Exception { - return calculateItems(); + return calculateChildCount(); } @Override protected void done() { try { childCount = get(); - setDisplayName(getDisplayNameBase() + " (" + childCount + ")"); + setDisplayName(getDisplayNameBase() + " (" + childCount + ")"); //NON-NLS } catch (InterruptedException | ExecutionException ex) { setDisplayName(getDisplayNameBase()); - logger.log(Level.WARNING, "Failed to get count of files for " + getDisplayNameBase(), ex); + logger.log(Level.WARNING, "Failed to get count of files for " + getDisplayNameBase(), ex); //NON-NLS } } }.execute(); } else { - setDisplayName(getDisplayNameBase() + ((childCount < 0) ? "" - : ("(" + childCount + "+)"))); + setDisplayName(getDisplayNameBase() + ((childCount < 0) ? "" : ("(" + childCount + "+)"))); //NON-NLS } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java index 57c0663e4a..a447b9bad9 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -325,7 +325,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { } @Override - long calculateItems() throws Exception { + long calculateChildCount() throws TskCoreException { return skCase.countFilesWhere(createQuery(filter)); } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index 047e105658..7f3de1cd21 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -50,7 +50,7 @@ import org.sleuthkit.datamodel.TskData; * File Types view, shows all files with a mime type. Will initially be empty * until file type identification has been performed. Contains a Property Change * Listener which is checking for changes in IngestJobEvent Completed or - * Cancelled and IngestModuleEvent Content Changed. + * Canceled and IngestModuleEvent Content Changed. */ public final class FileTypesByMimeType extends Observable implements AutopsyVisitableItem { @@ -59,9 +59,9 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi /** * The nodes of this tree will be determined dynamically by the mimetypes * which exist in the database. This hashmap will store them with the media - * type as the key and a list of media subtypes as the value. + * type as the key and a Map, from media subtype to count, as the value. */ - private final HashMap> existingMimeTypes = new HashMap<>(); + private final HashMap> existingMimeTypeCounts = new HashMap<>(); private static final Logger LOGGER = Logger.getLogger(FileTypesByMimeType.class.getName()); private final FileTypes typesRoot; @@ -76,20 +76,6 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi */ private final PropertyChangeListener pcl; - /** - * Retrieve the media types by retrieving the keyset from the hashmap. - * - * @return mediaTypes - a list of strings representing all distinct media - * types of files for this case - */ - private List getMediaTypeList() { - synchronized (existingMimeTypes) { - List mediaTypes = new ArrayList<>(existingMimeTypes.keySet()); - Collections.sort(mediaTypes); - return mediaTypes; - } - } - /** * Performs the query on the database to get all distinct MIME types of * files in it, and populate the hashmap with those results. @@ -106,8 +92,8 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi + ((UserPreferences.hideSlackFilesInViewsTree()) ? "" : ("," + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal())) + "))" + " GROUP BY mime_type"; - synchronized (existingMimeTypes) { - existingMimeTypes.clear(); + synchronized (existingMimeTypeCounts) { + existingMimeTypeCounts.clear(); if (skCase == null) { return; @@ -122,7 +108,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi final String subType = StringUtils.removeStart(mime_type, mediaType + "/"); if (!mediaType.isEmpty() && !subType.isEmpty()) { final long count = resultSet.getLong("count"); - existingMimeTypes.computeIfAbsent(mediaType, t -> new HashMap<>()) + existingMimeTypeCounts.computeIfAbsent(mediaType, t -> new HashMap<>()) .put(subType, count); } } @@ -227,11 +213,10 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi } boolean isEmpty() { - synchronized (existingMimeTypes) { - return existingMimeTypes.isEmpty(); + synchronized (existingMimeTypeCounts) { + return existingMimeTypeCounts.isEmpty(); } } - } /** @@ -247,9 +232,13 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi @Override protected boolean createKeys(List mediaTypeNodes) { - if (!existingMimeTypes.isEmpty()) { - mediaTypeNodes.addAll(getMediaTypeList()); + final List keylist; + synchronized (existingMimeTypeCounts) { + keylist = new ArrayList<>(existingMimeTypeCounts.keySet()); } + Collections.sort(keylist); + mediaTypeNodes.addAll(keylist); + return true; } @@ -262,7 +251,6 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi public void update(Observable o, Object arg) { refresh(true); } - } /** @@ -311,7 +299,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi @Override protected boolean createKeys(List mediaTypeNodes) { - mediaTypeNodes.addAll(existingMimeTypes.get(mediaType).keySet()); + mediaTypeNodes.addAll(existingMimeTypeCounts.get(mediaType).keySet()); return true; } @@ -376,8 +364,8 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi } @Override - long calculateItems() { - return existingMimeTypes.get(StringUtils.substringBefore(mimeType, "/")).get(subType); + long calculateChildCount() { + return existingMimeTypeCounts.get(StringUtils.substringBefore(mimeType, "/")).get(subType); } }