diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/EmailsDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/EmailsDAO.java index 899db7ef7e..7478f17d8e 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/EmailsDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/EmailsDAO.java @@ -232,27 +232,37 @@ public class EmailsDAO extends AbstractDAO { return safePath; } - public static class EmailTreeItem extends TreeItemDTO { - - private final Optional hasChildren; - - EmailTreeItem(String fullFolder, Long dataSourceId, TreeDisplayCount count, - Boolean hasChildren) { - super( - EmailSearchParams.getTypeId(), - new EmailSearchParams(dataSourceId, fullFolder), - fullFolder == null ? 0 : fullFolder, - getFolderDisplayName(fullFolder), - count - ); - this.hasChildren = Optional.ofNullable(hasChildren); - } - - public Optional getHasChildren() { - return hasChildren; - } + public TreeItemDTO createEmailTreeItem(String fullFolder, Long dataSourceId, TreeDisplayCount count) { + return new TreeItemDTO<>( + EmailSearchParams.getTypeId(), + new EmailSearchParams(dataSourceId, fullFolder), + fullFolder == null ? 0 : fullFolder, + getFolderDisplayName(fullFolder), + count + ); } + + public Optional getNextSubFolder(String folderParent, String folder) { + String normalizedParent = folderParent == null ? null : getNormalizedPath(folderParent); + String normalizedFolder = folder == null ? null : getNormalizedPath(folder); + + if (normalizedParent == null || normalizedFolder.startsWith(normalizedParent)) { + if (normalizedFolder == null) { + return Optional.of(null); + } else { + int nextDelim = normalizedFolder.indexOf(PATH_DELIMITER, normalizedParent.length()); + if (nextDelim >= 0) { + return Optional.of(normalizedFolder.substring(normalizedParent.length(), nextDelim)); + } else { + return Optional.of(normalizedFolder.substring(normalizedParent.length())); + } + } + } else { + return Optional.empty(); + } + } + /** * Returns sql to query for email counts. * @@ -383,8 +393,7 @@ public class EmailsDAO extends AbstractDAO { ? TreeDisplayCount.INDETERMINATE : TreeResultsDTO.TreeDisplayCount.getDeterminate(resultSet.getLong("count")); - accumulatedData.add( - new EmailTreeItem(getNormalizedPath(rsPath), dataSourceId, treeDisplayCount, hasChildren)); + accumulatedData.add(createEmailTreeItem(getNormalizedPath(rsPath), dataSourceId, treeDisplayCount)); } } catch (SQLException ex) { throw new IllegalStateException("A sql exception occurred.", ex); @@ -414,11 +423,10 @@ public class EmailsDAO extends AbstractDAO { Set handleIngestComplete() { return SubDAOUtils.getIngestCompleteEvents( this.emailCounts, - (daoEvt, count) -> new EmailTreeItem( + (daoEvt, count) -> createEmailTreeItem( getNormalizedPath(daoEvt.getFolder()), daoEvt.getDataSourceId(), - count, - daoEvt.getHasChildren().orElse(null) + count )); } @@ -426,11 +434,10 @@ public class EmailsDAO extends AbstractDAO { Set shouldRefreshTree() { return SubDAOUtils.getRefreshEvents( this.emailCounts, - (daoEvt, count) -> new EmailTreeItem( + (daoEvt, count) -> createEmailTreeItem( getNormalizedPath(daoEvt.getFolder()), daoEvt.getDataSourceId(), - count, - daoEvt.getHasChildren().orElse(null) + count )); } @@ -469,18 +476,17 @@ public class EmailsDAO extends AbstractDAO { for (Entry> folderEntry : emailMap.entrySet()) { String folder = folderEntry.getKey(); for (Long dsObjId : folderEntry.getValue()) { - emailEvents.add(new EmailEvent(dsObjId, folder, null)); + emailEvents.add(new EmailEvent(dsObjId, folder)); } } Stream treeEvents = this.emailCounts.enqueueAll(emailEvents).stream() .map(daoEvt -> { return new TreeEvent( - new EmailTreeItem( + createEmailTreeItem( daoEvt.getFolder(), daoEvt.getDataSourceId(), - TreeResultsDTO.TreeDisplayCount.INDETERMINATE, - null), + TreeResultsDTO.TreeDisplayCount.INDETERMINATE), false); }); @@ -509,6 +515,7 @@ public class EmailsDAO extends AbstractDAO { } } + /** * Handles fetching and paging of data for communication accounts. */ diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/events/EmailEvent.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/events/EmailEvent.java index 7e88935a71..0a10913d1b 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/events/EmailEvent.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/events/EmailEvent.java @@ -19,7 +19,6 @@ package org.sleuthkit.autopsy.mainui.datamodel.events; import java.util.Objects; -import java.util.Optional; import org.sleuthkit.datamodel.BlackboardArtifact; /** @@ -28,7 +27,6 @@ import org.sleuthkit.datamodel.BlackboardArtifact; public class EmailEvent extends DataArtifactEvent { private final String folder; - private final Optional hasChildren; /** * Main constructor. @@ -36,22 +34,16 @@ public class EmailEvent extends DataArtifactEvent { * @param dataSourceId The data source id that the email message belongs to. * @param account The email message account. * @param folder The folder within that account of the email message. - * @param hasChildren True if this folder has further tree folders. Null if unknown. */ - public EmailEvent(long dataSourceId, String folder, Boolean hasChildren) { + public EmailEvent(long dataSourceId, String folder) { super(BlackboardArtifact.Type.TSK_EMAIL_MSG, dataSourceId); this.folder = folder; - this.hasChildren = Optional.ofNullable(hasChildren); } public String getFolder() { return folder; } - public Optional getHasChildren() { - return hasChildren; - } - @Override public int hashCode() { int hash = 7; diff --git a/Core/src/org/sleuthkit/autopsy/mainui/nodes/DataArtifactTypeFactory.java b/Core/src/org/sleuthkit/autopsy/mainui/nodes/DataArtifactTypeFactory.java index 0593bb916c..385c3762f9 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/nodes/DataArtifactTypeFactory.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/nodes/DataArtifactTypeFactory.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.mainui.nodes; import java.util.Objects; +import java.util.Optional; import java.util.concurrent.ExecutionException; import org.apache.commons.lang3.StringUtils; import org.openide.nodes.Children; @@ -135,7 +136,7 @@ public class DataArtifactTypeFactory extends TreeChildFactory { + static class EmailFolderFactory extends TreeChildFactory { + private final String folderParent; private final Long dataSourceId; /** * Main constructor. * + * @param folderParent The email parent folder for the factory. * @param dataSourceId The data source object id for which the results * should be filtered or null if no data source * filtering. */ - public EmailAccountTypeFactory(Long dataSourceId) { + public EmailFolderFactory(String folderParent, Long dataSourceId) { this.dataSourceId = dataSourceId; + this.folderParent = folderParent; } private EmailsDAO getDAO() { @@ -289,12 +293,12 @@ public class DataArtifactTypeFactory extends TreeChildFactory getChildResults() throws IllegalArgumentException, ExecutionException { - return getDAO().getEmailCounts(dataSourceId, null); + return getDAO().getEmailCounts(dataSourceId, this.folderParent); } @Override protected TreeNode createNewNode(TreeResultsDTO.TreeItemDTO rowData) { - return new EmailAccountTypeNode(rowData); + return new EmailNode(rowData); } @Override @@ -303,103 +307,20 @@ public class DataArtifactTypeFactory extends TreeChildFactory originalTreeItem = getTypedTreeItem(treeEvt, EmailSearchParams.class); if (originalTreeItem != null + // ensure data source id for factory is null or data sources are equal && (this.dataSourceId == null || Objects.equals(this.dataSourceId, originalTreeItem.getSearchParams().getDataSourceId()))) { + EmailSearchParams originalSearchParam = originalTreeItem.getSearchParams(); - return getDAO().createEmailTreeItem( - originalSearchParam.getAccount(), - null, - getDAO().getAccountDisplayName(originalSearchParam.getAccount(), null), - dataSourceId, - originalTreeItem.getDisplayCount()); - } - return null; - } + Optional thisSubFolderOpt = getDAO().getNextSubFolder(this.folderParent, originalSearchParam.getFolder()); + if (thisSubFolderOpt.isPresent()) { + String thisSubFolder = thisSubFolderOpt.get(); - @Override - public int compare(TreeItemDTO o1, TreeItemDTO o2) { - boolean firstDown = o1.getSearchParams().getAccount() == null; - boolean secondDown = o2.getSearchParams().getAccount() == null; - - if (firstDown == secondDown) { - return o1.getDisplayName().compareToIgnoreCase(o2.getDisplayName()); - } else { - return Boolean.compare(firstDown, secondDown); - } - } - } - - /** - * A node representing a single account type in the tree. - */ - static class EmailAccountTypeNode extends TreeNode { - - /** - * Main constructor. - * - * @param itemData The data to display. - */ - public EmailAccountTypeNode(TreeResultsDTO.TreeItemDTO itemData) { - super(itemData.getSearchParams().getAccount(), - "org/sleuthkit/autopsy/images/account-icon-16.png", - itemData, - Children.create(new EmailFolderTypeFactory(itemData.getSearchParams().getAccount(), itemData.getSearchParams().getDataSourceId()), true), - getDefaultLookup(itemData) - ); - - } - } - - /** - * Factory for displaying account types. - */ - static class EmailFolderTypeFactory extends TreeChildFactory { - - private final String account; - private final Long dataSourceId; - - /** - * Main constructor. - * - * @param account The email account for the factory. - * @param dataSourceId The data source object id for which the results - * should be filtered or null if no data source - * filtering. - */ - public EmailFolderTypeFactory(String account, Long dataSourceId) { - this.dataSourceId = dataSourceId; - this.account = account; - } - - private EmailsDAO getDAO() { - return MainDAO.getInstance().getEmailsDAO(); - } - - @Override - protected TreeResultsDTO getChildResults() throws IllegalArgumentException, ExecutionException { - return getDAO().getEmailCounts(dataSourceId, account); - } - - @Override - protected TreeNode createNewNode(TreeResultsDTO.TreeItemDTO rowData) { - return new EmailFolderTypeNode(rowData); - } - - @Override - protected TreeItemDTO getOrCreateRelevantChild(TreeEvent treeEvt) { - - TreeItemDTO originalTreeItem = getTypedTreeItem(treeEvt, EmailSearchParams.class); - - if (originalTreeItem != null - && Objects.equals(this.account, originalTreeItem.getSearchParams().getAccount()) - && (this.dataSourceId == null || Objects.equals(this.dataSourceId, originalTreeItem.getSearchParams().getDataSourceId()))) { - EmailSearchParams originalSearchParam = originalTreeItem.getSearchParams(); - return getDAO().createEmailTreeItem( - originalSearchParam.getAccount(), - originalSearchParam.getFolder(), - getDAO().getFolderDisplayName(originalSearchParam.getFolder()), - dataSourceId, - originalTreeItem.getDisplayCount()); + return getDAO().createEmailTreeItem( + thisSubFolder, + dataSourceId, + originalTreeItem.getDisplayCount()); + } } return null; @@ -421,22 +342,40 @@ public class DataArtifactTypeFactory extends TreeChildFactory { + static class EmailNode extends TreeNode { + + private final Children children; /** * Main constructor. * * @param itemData The data to display. */ - public EmailFolderTypeNode(TreeResultsDTO.TreeItemDTO itemData) { + public EmailNode(TreeResultsDTO.TreeItemDTO itemData) { + this(itemData, Children.create(new EmailFolderFactory(itemData.getSearchParams().getFolder(), itemData.getSearchParams().getDataSourceId()), true)); + } + + private EmailNode(TreeResultsDTO.TreeItemDTO itemData, Children children) { super(itemData.getSearchParams().getFolder(), "org/sleuthkit/autopsy/images/folder-icon-16.png", - itemData); + itemData, + children, + getDefaultLookup(itemData)); + + this.children = children; + } + + private boolean hasChildren() { + return this.children.getNodesCount(true) > 0; } @Override public void respondSelection(DataResultTopComponent dataResultPanel) { - dataResultPanel.displayEmailMessages(super.getItemData().getSearchParams()); + if (hasChildren()) { + super.respondSelection(dataResultPanel); + } else { + dataResultPanel.displayEmailMessages(super.getItemData().getSearchParams()); + } } }