diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java index 773934c1cd..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; @@ -96,10 +96,10 @@ 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"); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); //NON-NLS } @Override @@ -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 @@ -205,37 +202,44 @@ public final class FileTypes implements AutopsyVisitableItem { abstract String getDisplayNameBase(); - abstract String geQuery(); + /** + * 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 skCase.countFilesWhere(geQuery()); + 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 726a6fa67b..a447b9bad9 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 geQuery() { - return createQuery(filter); + long calculateChildCount() throws TskCoreException { + 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 50c0813d05..7f3de1cd21 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; @@ -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,54 +76,40 @@ 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. */ 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("))"); - synchronized (existingMimeTypes) { - existingMimeTypes.clear(); + 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 (existingMimeTypeCounts) { + existingMimeTypeCounts.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"); + existingMimeTypeCounts.computeIfAbsent(mediaType, t -> new HashMap<>()) + .put(subType, count); } } } @@ -133,11 +119,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,9 +213,10 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi } boolean isEmpty() { - return existingMimeTypes.isEmpty(); + synchronized (existingMimeTypeCounts) { + return existingMimeTypeCounts.isEmpty(); + } } - } /** @@ -246,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; } @@ -261,7 +251,6 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi public void update(Observable o, Object arg) { refresh(true); } - } /** @@ -310,7 +299,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi @Override protected boolean createKeys(List mediaTypeNodes) { - mediaTypeNodes.addAll(existingMimeTypes.get(mediaType)); + mediaTypeNodes.addAll(existingMimeTypeCounts.get(mediaType).keySet()); return true; } @@ -333,7 +322,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); @@ -375,24 +364,8 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi } @Override - String geQuery() { - 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 calculateChildCount() { + return existingMimeTypeCounts.get(StringUtils.substringBefore(mimeType, "/")).get(subType); } }