diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java similarity index 81% rename from Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java rename to Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java index ac9435cea0..0becc89c10 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java @@ -24,6 +24,7 @@ import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.logging.Level; import java.util.stream.Collectors; @@ -61,40 +62,71 @@ import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_INTER import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT; /** - * Parent of the "extracted content" artifacts to be displayed in the tree. - * Other artifacts are displayed under other more specific parents. + * Classes for creating nodes for BlackboardArtifacts. */ -public class ExtractedContent { +public class Artifacts { - private static final Set INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED); + private static final Set INGEST_JOB_EVENTS_OF_INTEREST + = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED); + /** + * Parent node of all analysis results. + */ @Messages({ "AnalysisResultsNode_name=Analysis Results",}) - static class AnalysisResultsNode extends RootNode { + static class AnalysisResultsNode extends BaseArtifactNode { + /** + * Main constructor. + * + * @param filteringDSObjId The data source object id for which results + * should be filtered. If no filtering should + * occur, this number should be <= 0. + */ AnalysisResultsNode(long filteringDSObjId) { - super(Children.create(new AnalysisResultsTypeFactory(filteringDSObjId), true), + super(Children.create(new TypeFactory(Category.ANALYSIS_RESULT, filteringDSObjId), true), "org/sleuthkit/autopsy/images/analysis_result.png", Bundle.AnalysisResultsNode_name(), Bundle.AnalysisResultsNode_name()); } } + /** + * Parent node of all data artifacts. + */ @Messages({ "DataArtifactsNode_name=Data Artifacts",}) - static class DataArtifactsNode extends RootNode { + static class DataArtifactsNode extends BaseArtifactNode { + /** + * Main constructor. + * + * @param filteringDSObjId The data source object id for which results + * should be filtered. If no filtering should + * occur, this number should be <= 0. + */ DataArtifactsNode(long filteringDSObjId) { - super(Children.create(new DataArtifactsTypeFactory(filteringDSObjId), true), + super(Children.create(new TypeFactory(Category.DATA_ARTIFACT, filteringDSObjId), true), "org/sleuthkit/autopsy/images/extracted_content.png", Bundle.DataArtifactsNode_name(), Bundle.DataArtifactsNode_name()); } } - static class RootNode extends DisplayableItemNode { + /** + * Base class for a parent node of artifacts. + */ + private static class BaseArtifactNode extends DisplayableItemNode { - RootNode(Children children, String icon, String name, String displayName) { + /** + * Main constructor. + * + * @param children The children of the node. + * @param icon The icon for the node. + * @param name The name identifier of the node. + * @param displayName The display name for the node. + */ + BaseArtifactNode(Children children, String icon, String name, String displayName) { super(children, Lookups.singleton(name)); super.setName(name); super.setDisplayName(displayName); @@ -133,64 +165,87 @@ public class ExtractedContent { } } - private static class AnalysisResultsTypeFactory extends TypeFactory { + /** + * A key to be used with the type factory. + */ + private static class TypeNodeKey { - AnalysisResultsTypeFactory(long filteringDSObjId) { - super(Category.ANALYSIS_RESULT, filteringDSObjId); - } - } - - private static class DataArtifactsTypeFactory extends TypeFactory { - - DataArtifactsTypeFactory(long filteringDSObjId) { - super(Category.DATA_ARTIFACT, filteringDSObjId); - } - } - - private static class TypeNodeRecord { - - private final Node node; - private final Runnable onUpdate; + private final UpdatableCountTypeNode node; private final Set applicableTypes; - TypeNodeRecord(BlackboardArtifact.Type type, long dsObjId) { + /** + * Constructor generating a generic TypeNode for a given artifact type. + * + * @param type The type for the key. + * @param dsObjId The data source object id if filtering should occur. + * If no filtering should occur, this number should be <= + * 0. + */ + TypeNodeKey(BlackboardArtifact.Type type, long dsObjId) { this(new TypeNode(type, dsObjId), type); } - private TypeNodeRecord(UpdatableTypeCountNode typeNode, BlackboardArtifact.Type... types) { - this(typeNode, typeNode::updateDisplayName, types); - } - - TypeNodeRecord(Node node, Runnable onUpdate, BlackboardArtifact.Type... types) { - this.node = node; - this.onUpdate = onUpdate; + /** + * Constructor for any UpdatableCountTypeNode. + * + * @param typeNode The UpdatableCountTypeNode. + * @param types The blackboard artifact types corresponding to this + * node. + */ + TypeNodeKey(UpdatableCountTypeNode typeNode, BlackboardArtifact.Type... types) { + this.node = typeNode; this.applicableTypes = Stream.of(types) .filter(t -> t != null) .collect(Collectors.toSet()); } - Node getNode() { + /** + * Returns the node associated with this key. + * @return The node associated with this key. + */ + UpdatableCountTypeNode getNode() { return node; } - void update() { - if (onUpdate != null) { - onUpdate.run(); - } - } - + /** + * Returns the blackboard artifact types associated with this key. + * @return The blackboard artifact types associated with this key. + */ Set getApplicableTypes() { return applicableTypes; } + @Override + public int hashCode() { + int hash = 3; + hash = 61 * hash + Objects.hashCode(this.applicableTypes); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final TypeNodeKey other = (TypeNodeKey) obj; + if (!Objects.equals(this.applicableTypes, other.applicableTypes)) { + return false; + } + return true; + } + } /** - * Creates the children for the ExtractedContent area of the results tree. - * This area has all of the blackboard artifacts that are not displayed in a - * more specific form elsewhere in the tree. + * */ - private static class TypeFactory extends ChildFactory.Detachable implements RefreshThrottler.Refresher { + private static class TypeFactory extends ChildFactory.Detachable implements RefreshThrottler.Refresher { private static final Logger logger = Logger.getLogger(TypeNode.class.getName()); @@ -205,35 +260,36 @@ public class ExtractedContent { new BlackboardArtifact.Type(TSK_ASSOCIATED_OBJECT) ); - private static TypeNodeRecord getRecord(BlackboardArtifact.Type type, SleuthkitCase skCase, long dsObjId) { + + private static TypeNodeKey getRecord(BlackboardArtifact.Type type, SleuthkitCase skCase, long dsObjId) { int typeId = type.getTypeID(); if (TSK_EMAIL_MSG.getTypeID() == typeId) { EmailExtracted.RootNode emailNode = new EmailExtracted(skCase, dsObjId).new RootNode(); - return new TypeNodeRecord(emailNode, new BlackboardArtifact.Type(TSK_EMAIL_MSG)); + return new TypeNodeKey(emailNode, new BlackboardArtifact.Type(TSK_EMAIL_MSG)); } else if (TSK_ACCOUNT.getTypeID() == typeId) { Accounts.AccountsRootNode accountsNode = new Accounts(skCase, dsObjId).new AccountsRootNode(); - return new TypeNodeRecord(accountsNode, new BlackboardArtifact.Type(TSK_ACCOUNT)); + return new TypeNodeKey(accountsNode, new BlackboardArtifact.Type(TSK_ACCOUNT)); } else if (TSK_KEYWORD_HIT.getTypeID() == typeId) { KeywordHits.RootNode keywordsNode = new KeywordHits(skCase, dsObjId).new RootNode(); - return new TypeNodeRecord(keywordsNode, new BlackboardArtifact.Type(TSK_KEYWORD_HIT)); + return new TypeNodeKey(keywordsNode, new BlackboardArtifact.Type(TSK_KEYWORD_HIT)); } else if (TSK_INTERESTING_ARTIFACT_HIT.getTypeID() == typeId || TSK_INTERESTING_FILE_HIT.getTypeID() == typeId) { InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, dsObjId).new RootNode(); - return new TypeNodeRecord(interestingHitsNode, + return new TypeNodeKey(interestingHitsNode, new BlackboardArtifact.Type(TSK_INTERESTING_ARTIFACT_HIT), new BlackboardArtifact.Type(TSK_INTERESTING_FILE_HIT)); } else { - return new TypeNodeRecord(type, dsObjId); + return new TypeNodeKey(type, dsObjId); } } // maps the artifact type to its child node - private final HashMap typeNodeMap = new HashMap<>(); + private final HashMap typeNodeMap = new HashMap<>(); private final long filteringDSObjId; /** @@ -292,7 +348,7 @@ public class ExtractedContent { } @Override - protected boolean createKeys(List list) { + protected boolean createKeys(List list) { try { SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); @@ -300,15 +356,15 @@ public class ExtractedContent { ? skCase.getBlackboard().getArtifactTypesInUse(this.filteringDSObjId) : skCase.getArtifactTypesInUse(); - List allKeysSorted = types.stream() + List allKeysSorted = types.stream() .filter(tp -> category.equals(tp.getCategory()) && !IGNORED_TYPES.contains(tp)) .map(tp -> { if (typeNodeMap.containsKey(tp)) { - TypeNodeRecord record = typeNodeMap.get(tp); - record.update(); + TypeNodeKey record = typeNodeMap.get(tp); + record.getNode().updateDisplayName(); return record; } else { - TypeNodeRecord newRecord = getRecord(tp, skCase, filteringDSObjId); + TypeNodeKey newRecord = getRecord(tp, skCase, filteringDSObjId); for (BlackboardArtifact.Type recordType : newRecord.getApplicableTypes()) { typeNodeMap.put(recordType, newRecord); } @@ -324,8 +380,6 @@ public class ExtractedContent { }) .collect(Collectors.toList()); - allKeysSorted.forEach(record -> record.update()); - list.addAll(allKeysSorted); } catch (NoCurrentCaseException ex) { @@ -337,7 +391,7 @@ public class ExtractedContent { } @Override - protected Node createNodeForKey(TypeNodeRecord key) { + protected Node createNodeForKey(TypeNodeKey key) { return key.getNode(); } @@ -377,9 +431,9 @@ public class ExtractedContent { } } - public static abstract class UpdatableTypeCountNode extends DisplayableItemNode { + public static abstract class UpdatableCountTypeNode extends DisplayableItemNode { - private static final Logger logger = Logger.getLogger(UpdatableTypeCountNode.class.getName()); + private static final Logger logger = Logger.getLogger(UpdatableCountTypeNode.class.getName()); private final Set types; private final long filteringDSObjId; @@ -397,7 +451,7 @@ public class ExtractedContent { * @param children The Children object for the node. * @param lookup The Lookup object for the node. */ - public UpdatableTypeCountNode(Children children, Lookup lookup, String baseName, long filteringDSObjId, BlackboardArtifact.Type... types) { + public UpdatableCountTypeNode(Children children, Lookup lookup, String baseName, long filteringDSObjId, BlackboardArtifact.Type... types) { super(children, lookup); this.types = Stream.of(types).collect(Collectors.toSet()); this.filteringDSObjId = filteringDSObjId; @@ -438,7 +492,7 @@ public class ExtractedContent { * the artifacts of a given type. Its children will be * BlackboardArtifactNode objects. */ - public static class TypeNode extends UpdatableTypeCountNode { + public static class TypeNode extends UpdatableCountTypeNode { private static final Logger logger = Logger.getLogger(TypeNode.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index 4cdb32ff2c..715f9aecd9 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -90,9 +90,9 @@ public interface DisplayableItemNodeVisitor { T visit(BlackboardArtifactNode ban); - T visit(ExtractedContent.TypeNode atn); + T visit(Artifacts.TypeNode atn); - T visit(ExtractedContent.RootNode ecn); + T visit(Artifacts.BaseArtifactNode ecn); T visit(KeywordHits.RootNode khrn); @@ -296,12 +296,12 @@ public interface DisplayableItemNodeVisitor { } @Override - public T visit(ExtractedContent.TypeNode atn) { + public T visit(Artifacts.TypeNode atn) { return defaultVisit(atn); } @Override - public T visit(ExtractedContent.RootNode ecn) { + public T visit(Artifacts.BaseArtifactNode ecn) { return defaultVisit(ecn); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java index d042573e53..06ed408976 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java @@ -49,7 +49,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.autopsy.datamodel.ExtractedContent.UpdatableTypeCountNode; +import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode; /** * Support for TSK_EMAIL_MSG nodes and displaying emails in the directory tree. @@ -203,7 +203,7 @@ public class EmailExtracted implements AutopsyVisitableItem { * Mail root node grouping all mail accounts, supports account-> folder * structure */ - public class RootNode extends UpdatableTypeCountNode { + public class RootNode extends UpdatableCountTypeNode { public RootNode() { super(Children.create(new AccountFactory(), true), diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java index 7a04d3cf33..150a52e3da 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java @@ -52,7 +52,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.autopsy.datamodel.ExtractedContent.UpdatableTypeCountNode; +import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode; /** * Hash set hits node support. Inner classes have all of the nodes in the tree. @@ -170,7 +170,7 @@ public class HashsetHits implements AutopsyVisitableItem { /** * Top-level node for all hash sets */ - public class RootNode extends UpdatableTypeCountNode { + public class RootNode extends UpdatableCountTypeNode { public RootNode() { super(Children.create(new HashsetNameFactory(), true), diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java index 27d08f3781..28bfb3e8dc 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java @@ -50,7 +50,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.autopsy.datamodel.ExtractedContent.UpdatableTypeCountNode; +import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode; public class InterestingHits implements AutopsyVisitableItem { @@ -166,7 +166,7 @@ public class InterestingHits implements AutopsyVisitableItem { /** * Node for the interesting items */ - public class RootNode extends UpdatableTypeCountNode { + public class RootNode extends UpdatableCountTypeNode { public RootNode() { super(Children.create(new SetNameFactory(), true), diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java index 6713a6722b..845c87d5ea 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java @@ -56,7 +56,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT; -import org.sleuthkit.autopsy.datamodel.ExtractedContent.UpdatableTypeCountNode; +import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode; /** * Keyword hits node support @@ -377,7 +377,7 @@ public class KeywordHits implements AutopsyVisitableItem { } // Created by CreateAutopsyNodeVisitor - public class RootNode extends UpdatableTypeCountNode { + public class RootNode extends UpdatableCountTypeNode { public RootNode() { super(Children.create(new ListFactory(), true), diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java index ed1af75078..acb33129dd 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java @@ -196,13 +196,13 @@ public class RootContentChildren extends Children.Keys { @Override public AbstractNode visit(AnalysisResults analysisResults) { - return new ExtractedContent.AnalysisResultsNode( + return new Artifacts.AnalysisResultsNode( analysisResults.getFilteringDataSourceObjId()); } @Override public AbstractNode visit(DataArtifacts dataArtifacts) { - return new ExtractedContent.DataArtifactsNode( + return new Artifacts.DataArtifactsNode( dataArtifacts.getFilteringDataSourceObjId()); } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java index e8ec4f42bc..aa6a53f8ec 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java @@ -69,7 +69,7 @@ import org.sleuthkit.autopsy.datamodel.CreditCards; import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; -import org.sleuthkit.autopsy.datamodel.ExtractedContent.UpdatableTypeCountNode; +import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -235,7 +235,7 @@ final public class Accounts implements AutopsyVisitableItem { * Top-level node for the accounts tree */ @NbBundle.Messages({"Accounts.RootNode.displayName=Accounts"}) - final public class AccountsRootNode extends UpdatableTypeCountNode { + final public class AccountsRootNode extends UpdatableCountTypeNode { public AccountsRootNode() { super(Children.create(new AccountTypeFactory(), true), diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index d2cd291de0..90245dfe4e 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -78,7 +78,7 @@ import org.sleuthkit.autopsy.datamodel.CreditCards; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.EmailExtracted; import org.sleuthkit.autopsy.datamodel.EmptyNode; -import org.sleuthkit.autopsy.datamodel.ExtractedContent; +import org.sleuthkit.autopsy.datamodel.Artifacts; import org.sleuthkit.autopsy.datamodel.FileTypesByMimeType; import org.sleuthkit.autopsy.datamodel.InterestingHits; import org.sleuthkit.autopsy.datamodel.KeywordHits; @@ -1391,7 +1391,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS } } else { - Node extractedContent = resultsChilds.findChild(ExtractedContent.NAME); + Node extractedContent = resultsChilds.findChild(Artifacts.NAME); Children extractedChilds = extractedContent.getChildren(); if (extractedChilds == null) { return;