From 214fe440c8956f876595aa2e3f4fd639021ece8a Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 12 Jul 2017 12:19:59 +0200 Subject: [PATCH 1/5] 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 2/5] 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 3/5] 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 4/5] 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 5/5] 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); } }