diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java index 5cfddc7f7a..8f0802007e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -51,7 +51,9 @@ import org.sleuthkit.datamodel.TskData; /** * Filters database results by file extension. */ - public final class FileTypesByExtension implements AutopsyVisitableItem { +public final class FileTypesByExtension implements AutopsyVisitableItem { + + private static final Logger LOGGER = Logger.getLogger(FileTypesByExtension.class.getName()); private final SleuthkitCase skCase; @@ -72,13 +74,63 @@ import org.sleuthkit.datamodel.TskData; * Listens for case and ingest invest. Updates observers when events are * fired. FileType and FileTypes nodes are all listening to this. */ - private static class FileTypesByExtObservable extends Observable { + static private class FileTypesByExtObservable extends Observable { - private FileTypesByExtObservable() { + private boolean showCounts = true; + private final PropertyChangeListener pcl; + + private FileTypesByExtObservable(SleuthkitCase skCase) { 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())) { + /** + * Checking for a current case is a stop gap measure until a + * different way of handling the closing of cases is worked + * out. Currently, remote events may be received for a case + * that is already closed. + */ + try { + Case.getCurrentCase(); + shouldShowCounts(skCase); + update(); + } catch (IllegalStateException notUsed) { + /** + * Case is closed, do nothing. + */ + } + } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { + // case was closed. Remove listeners so that we don't get called with a stale case handle + if (evt.getNewValue() == null) { + removeListeners(); + } + } + }; + IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); Case.addPropertyChangeListener(pcl); + + } + + /** + * 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() { @@ -88,31 +140,6 @@ import org.sleuthkit.datamodel.TskData; Case.removePropertyChangeListener(pcl); } - private final PropertyChangeListener 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())) { - /** - * Checking for a current case is a stop gap measure until a - * different way of handling the closing of cases is worked out. - * Currently, remote events may be received for a case that is - * already closed. - */ - try { - Case.getCurrentCase(); - update(); - } catch (IllegalStateException notUsed) { - /** - * Case is closed, do nothing. - */ - } - } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { - // case was closed. Remove listeners so that we don't get called with a stale case handle - if (evt.getNewValue() == null) { - removeListeners(); - } - } - }; - private void update() { setChanged(); notifyObservers(); @@ -146,7 +173,7 @@ import org.sleuthkit.datamodel.TskData; * @param o Observable that was created by a higher-level node that * provides updates on events */ - private FileTypesByExtNode(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter, Observable o) { + 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())); this.filter = filter; init(); @@ -206,7 +233,7 @@ import org.sleuthkit.datamodel.TskData; private final SleuthkitCase skCase; private final FileTypesByExtension.RootFilter filter; - private final Observable notifier; + private final FileTypesByExtObservable notifier; /** * @@ -215,12 +242,12 @@ import org.sleuthkit.datamodel.TskData; * @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, Observable o) { + private FileTypesByExtNodeChildren(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter, FileTypesByExtObservable o) { super(); this.skCase = skCase; this.filter = filter; if (o == null) { - this.notifier = new FileTypesByExtObservable(); + this.notifier = new FileTypesByExtObservable(skCase); } else { this.notifier = o; } @@ -263,6 +290,7 @@ import org.sleuthkit.datamodel.TskData; FileTypesByExtension.SearchFilterInterface filter; SleuthkitCase skCase; + private final FileTypesByExtObservable notifier; /** * @@ -271,10 +299,11 @@ import org.sleuthkit.datamodel.TskData; * @param o Observable that sends updates when the child factories * should refresh */ - FileExtensionNode(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { + FileExtensionNode(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, FileTypesByExtObservable o) { super(Children.create(new FileExtensionNodeChildren(filter, skCase, o), true), Lookups.singleton(filter.getDisplayName())); this.filter = filter; this.skCase = skCase; + this.notifier = o; init(); o.addObserver(new ByExtNodeObserver()); } @@ -295,8 +324,10 @@ import org.sleuthkit.datamodel.TskData; } private void updateDisplayName() { - final long count = FileExtensionNodeChildren.calculateItems(skCase, filter); - super.setDisplayName(filter.getDisplayName() + " (" + count + ")"); + final String count = notifier.shouldShowCounts(skCase) + ? " (" + Long.toString(FileExtensionNodeChildren.calculateItems(skCase, filter)) + ")" + : ""; + super.setDisplayName(filter.getDisplayName() + count); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index 1549d6bdc1..2ccc1dc1e8 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -71,41 +71,18 @@ 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 void removeListeners() { deleteObservers(); IngestManager.getInstance().removeIngestJobEventListener(pcl); Case.removePropertyChangeListener(pcl); } - /* * The pcl is in the class because it has the easiest mechanisms to add and * remove itself during its life cycles. */ - private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { - String eventType = evt.getPropertyName(); - if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) - || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { - - /** - * Checking for a current case is a stop gap measure until a - * different way of handling the closing of cases is worked out. - * Currently, remote events may be received for a case that is - * already closed. - */ - try { - Case.getCurrentCase(); - populateHashMap(); - } catch (IllegalStateException notUsed) { - /** - * Case is closed, do nothing. - */ - } - } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { - if (evt.getNewValue() == null) { - removeListeners(); - } - } - }; + private final PropertyChangeListener pcl; /** * Retrieve the media types by retrieving the keyset from the hashmap. @@ -140,7 +117,6 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi existingMimeTypes.clear(); if (skCase == null) { - return; } try (SleuthkitCase.CaseDbQuery dbQuery = skCase.executeQuery(allDistinctMimeTypesQuery.toString())) { @@ -170,12 +146,59 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi } FileTypesByMimeType(SleuthkitCase skCase) { + this.pcl = (PropertyChangeEvent evt) -> { + String eventType = evt.getPropertyName(); + if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) + || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { + + /** + * Checking for a current case is a stop gap measure until a + * different way of handling the closing of cases is worked out. + * Currently, remote events may be received for a case that is + * already closed. + */ + try { + Case.getCurrentCase(); + shouldShowCounts(skCase); + + populateHashMap(); + } catch (IllegalStateException notUsed) { + /** + * Case is closed, do nothing. + */ + } + } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { + if (evt.getNewValue() == null) { + removeListeners(); + } + } + }; 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); @@ -358,10 +381,12 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi * results */ private void updateDisplayName(String mimeType) { - final long count = new MediaSubTypeNodeChildren(mimeType).calculateItems(skCase, mimeType); + final String count = shouldShowCounts(skCase) + ? " (" + Long.toString(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 + ")"); + super.setDisplayName(StringUtils.join(ArrayUtils.subarray(mimeTypeParts, 1, mimeTypeParts.length), "/") + count); } /**