diff --git a/Core/src/org/sleuthkit/autopsy/actions/TagAction.java b/Core/src/org/sleuthkit/autopsy/actions/TagAction.java index 376e52ce62..0b454bd4a5 100755 --- a/Core/src/org/sleuthkit/autopsy/actions/TagAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/TagAction.java @@ -50,13 +50,13 @@ import org.sleuthkit.datamodel.BlackboardArtifact; protected void refreshDirectoryTree() { // The way the "directory tree" currently works, a new tags sub-tree // needs to be made to reflect the results of invoking tag Actions. The - // way to do this is to call DirectoryTreeTopComponent.refreshTree(), + // way to do this is to call DirectoryTreeTopComponent.refreshResultsTree(), // which calls RootContentChildren.refreshKeys(BlackboardArtifact.ARTIFACT_TYPE... types) // for the RootContentChildren object that is the child factory for the // ResultsNode that is the root of the tags sub-tree. There is a switch // statement in RootContentChildren.refreshKeys() that maps both // BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE and BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT // to making a call to refreshKey(TagsNodeKey). - DirectoryTreeTopComponent.findInstance().refreshTree(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE); + DirectoryTreeTopComponent.findInstance().refreshResultsTree(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE); } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java index 539fb1e1fc..cd066a0a64 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java @@ -22,7 +22,6 @@ import org.openide.nodes.AbstractNode; import org.openide.nodes.Children.Keys; import org.openide.nodes.Node; import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsRootNode; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.DerivedFile; import org.sleuthkit.datamodel.Directory; @@ -119,8 +118,8 @@ abstract class AbstractContentChildren extends Keys { static class CreateAutopsyNodeVisitor extends AutopsyItemVisitor.Default { @Override - public ExtractedContentNode visit(ExtractedContent ec) { - return new ExtractedContentNode(ec.getSleuthkitCase()); + public ExtractedContent.RootNode visit(ExtractedContent ec) { + return ec.new RootNode(ec.getSleuthkitCase()); } @Override @@ -145,22 +144,22 @@ abstract class AbstractContentChildren extends Keys { @Override public AbstractNode visit(KeywordHits kh) { - return kh.new KeywordHitsRootNode(); + return kh.new RootNode(); } @Override public AbstractNode visit(HashsetHits hh) { - return hh.new HashsetHitsRootNode(); + return hh.new RootNode(); } @Override public AbstractNode visit(InterestingHits ih) { - return ih.new InterestingHitsRootNode(); + return ih.new RootNode(); } @Override public AbstractNode visit(EmailExtracted ee) { - return ee.new EmailExtractedRootNode(); + return ee.new RootNode(); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java index 3a53552739..1f4ab0a94e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java @@ -19,8 +19,6 @@ package org.sleuthkit.autopsy.datamodel; /** - * - * @author dfickling */ interface AutopsyItemVisitor { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 9cebc3a51c..69af7dccaf 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -41,8 +41,8 @@ import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; /** - * Node wrapping a blackboard artifact object. This represents a single artifact. - * Its parent is typically an ArtifactTypeNode. + * Node wrapping a blackboard artifact object. This is generated from several + * places in the tree. */ public class BlackboardArtifactNode extends DisplayableItemNode { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index 88921891fd..60b2a50e48 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -20,18 +20,8 @@ package org.sleuthkit.autopsy.datamodel; import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsChildren.DeletedContentNode; import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsNode; -import org.sleuthkit.autopsy.datamodel.EmailExtracted.EmailExtractedAccountNode; -import org.sleuthkit.autopsy.datamodel.EmailExtracted.EmailExtractedFolderNode; -import org.sleuthkit.autopsy.datamodel.EmailExtracted.EmailExtractedRootNode; import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootChildren.FileSizeNode; import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootNode; -import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsRootNode; -import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsSetNode; -import org.sleuthkit.autopsy.datamodel.InterestingHits.InterestingHitsRootNode; -import org.sleuthkit.autopsy.datamodel.InterestingHits.InterestingHitsSetNode; -import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsKeywordNode; -import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsListNode; -import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsRootNode; import org.sleuthkit.autopsy.directorytree.BlackboardArtifactTagTypeNode; /** @@ -49,9 +39,9 @@ public interface DisplayableItemNodeVisitor { T visit(BlackboardArtifactNode ban); - T visit(ArtifactTypeNode atn); + T visit(ExtractedContent.TypeNode atn); - T visit(ExtractedContentNode ecn); + T visit(ExtractedContent.RootNode ecn); T visit(FileTypeNode fsfn); @@ -69,27 +59,27 @@ public interface DisplayableItemNodeVisitor { T visit(RecentFilesFilterNode rffn); - T visit(KeywordHitsRootNode khrn); + T visit(KeywordHits.RootNode khrn); - T visit(KeywordHitsListNode khsn); + T visit(KeywordHits.ListNode khsn); - T visit(KeywordHitsKeywordNode khmln); + T visit(KeywordHits.TermNode khmln); - T visit(HashsetHitsRootNode hhrn); + T visit(HashsetHits.RootNode hhrn); - T visit(HashsetHitsSetNode hhsn); + T visit(HashsetHits.HashsetNameNode hhsn); - T visit(EmailExtractedRootNode eern); + T visit(EmailExtracted.RootNode eern); - T visit(EmailExtractedAccountNode eean); + T visit(EmailExtracted.AccountNode eean); - T visit(EmailExtractedFolderNode eefn); + T visit(EmailExtracted.FolderNode eefn); T visit(TagsNode node); - T visit(InterestingHitsRootNode ihrn); + T visit(InterestingHits.RootNode ihrn); - T visit(InterestingHitsSetNode ihsn); + T visit(InterestingHits.SetNameNode ihsn); T visit(TagNameNode node); @@ -155,12 +145,12 @@ public interface DisplayableItemNodeVisitor { } @Override - public T visit(ArtifactTypeNode atn) { + public T visit(ExtractedContent.TypeNode atn) { return defaultVisit(atn); } @Override - public T visit(ExtractedContentNode ecn) { + public T visit(ExtractedContent.RootNode ecn) { return defaultVisit(ecn); } @@ -205,17 +195,17 @@ public interface DisplayableItemNodeVisitor { } @Override - public T visit(KeywordHitsRootNode khrn) { + public T visit(KeywordHits.RootNode khrn) { return defaultVisit(khrn); } @Override - public T visit(KeywordHitsListNode khsn) { + public T visit(KeywordHits.ListNode khsn) { return defaultVisit(khsn); } @Override - public T visit(KeywordHitsKeywordNode khmln) { + public T visit(KeywordHits.TermNode khmln) { return defaultVisit(khmln); } @@ -235,37 +225,37 @@ public interface DisplayableItemNodeVisitor { } @Override - public T visit(HashsetHitsRootNode hhrn) { + public T visit(HashsetHits.RootNode hhrn) { return defaultVisit(hhrn); } @Override - public T visit(HashsetHitsSetNode hhsn) { + public T visit(HashsetHits.HashsetNameNode hhsn) { return defaultVisit(hhsn); } @Override - public T visit(InterestingHitsRootNode ihrn) { + public T visit(InterestingHits.RootNode ihrn) { return defaultVisit(ihrn); } @Override - public T visit(InterestingHitsSetNode ihsn) { + public T visit(InterestingHits.SetNameNode ihsn) { return defaultVisit(ihsn); } @Override - public T visit(EmailExtractedRootNode eern) { + public T visit(EmailExtracted.RootNode eern) { return defaultVisit(eern); } @Override - public T visit(EmailExtractedAccountNode eean) { + public T visit(EmailExtracted.AccountNode eean) { return defaultVisit(eean); } @Override - public T visit(EmailExtractedFolderNode eefn) { + public T visit(EmailExtracted.FolderNode eefn) { return defaultVisit(eefn); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java index 7e0d8801f4..5d60ce4fa8 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.datamodel; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; @@ -25,6 +27,9 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Observable; +import java.util.Observer; +import java.util.Set; import java.util.logging.Level; import org.openide.util.NbBundle; @@ -34,11 +39,14 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskException; + /** * Support for TSK_EMAIL_MSG nodes and displaying emails in the directory tree * Email messages are grouped into parent folders, and the folders are grouped @@ -54,64 +62,85 @@ public class EmailExtracted implements AutopsyVisitableItem { private static final String MAIL_FOLDER = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailFolder.text"); private static final String MAIL_PATH_SEPARATOR = "/"; private SleuthkitCase skCase; - private Map>> accounts; + private EmailResults emailResults; + public EmailExtracted(SleuthkitCase skCase) { this.skCase = skCase; - accounts = new LinkedHashMap<>(); + emailResults = new EmailResults(); } - - @SuppressWarnings("deprecation") - private void initArtifacts() { - accounts.clear(); - try { - int artId = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(); - int pathAttrId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID(); - String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS - + "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS - + "attribute_type_id=" + pathAttrId //NON-NLS - + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS - + " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS - ResultSet rs = skCase.runQuery(query); - while (rs.next()) { - final String path = rs.getString("value_text"); //NON-NLS - final long artifactId = rs.getLong("artifact_id"); //NON-NLS - final Map parsedPath = parsePath(path); - final String account = parsedPath.get(MAIL_ACCOUNT); - final String folder = parsedPath.get(MAIL_FOLDER); - - Map> folders = accounts.get(account); - if (folders == null) { - folders = new LinkedHashMap<>(); - accounts.put(account, folders); - } - List messages = folders.get(folder); - if (messages == null) { - messages = new ArrayList<>(); - folders.put(folder, messages); - } - messages.add(artifactId); - } - skCase.closeRunQuery(rs); - - } catch (SQLException ex) { - logger.log(Level.WARNING, "Cannot initialize email extraction", ex); //NON-NLS + + private final class EmailResults extends Observable { + private Map>> accounts = new LinkedHashMap<>(); + + EmailResults() { + update(); } - } + + public Set getAccounts() { + return accounts.keySet(); + } + + public Set getFolders(String account) { + return accounts.get(account).keySet(); + } + + public List getArtifactIds(String account, String folder) { + return accounts.get(account).get(folder); + } + + public void update() { + accounts.clear(); + try { + int artId = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(); + int pathAttrId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID(); + String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS + + "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS + + "attribute_type_id=" + pathAttrId //NON-NLS + + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS + + " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS + ResultSet rs = skCase.runQuery(query); + while (rs.next()) { + final String path = rs.getString("value_text"); //NON-NLS + final long artifactId = rs.getLong("artifact_id"); //NON-NLS + final Map parsedPath = parsePath(path); + final String account = parsedPath.get(MAIL_ACCOUNT); + final String folder = parsedPath.get(MAIL_FOLDER); - private static Map parsePath(String path) { - Map parsed = new HashMap<>(); - String[] split = path.split(MAIL_PATH_SEPARATOR); - if (split.length < 4) { - logger.log(Level.WARNING, "Unexpected number of tokens when parsing email PATH: {0}, will use defaults", split.length); //NON-NLS - parsed.put(MAIL_ACCOUNT, NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultAcct.text")); - parsed.put(MAIL_FOLDER, NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultFolder.text")); + Map> folders = accounts.get(account); + if (folders == null) { + folders = new LinkedHashMap<>(); + accounts.put(account, folders); + } + List messages = folders.get(folder); + if (messages == null) { + messages = new ArrayList<>(); + folders.put(folder, messages); + } + messages.add(artifactId); + } + skCase.closeRunQuery(rs); + + } catch (SQLException ex) { + logger.log(Level.WARNING, "Cannot initialize email extraction", ex); //NON-NLS + } + } + + + private Map parsePath(String path) { + Map parsed = new HashMap<>(); + String[] split = path.split(MAIL_PATH_SEPARATOR); + if (split.length < 4) { + logger.log(Level.WARNING, "Unexpected number of tokens when parsing email PATH: {0}, will use defaults", split.length); //NON-NLS + parsed.put(MAIL_ACCOUNT, NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultAcct.text")); + parsed.put(MAIL_FOLDER, NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultFolder.text")); + return parsed; + } + + parsed.put(MAIL_ACCOUNT, split[2]); + parsed.put(MAIL_FOLDER, split[3]); return parsed; } - - parsed.put(MAIL_ACCOUNT, split[2]); - parsed.put(MAIL_FOLDER, split[3]); - return parsed; } @Override @@ -122,94 +151,94 @@ public class EmailExtracted implements AutopsyVisitableItem { /** * Mail root node showing all emails */ - public class EmailExtractedRootNodeFlat extends DisplayableItemNode { - - public EmailExtractedRootNodeFlat() { - super(Children.create(new EmailExtractedRootChildrenFlat(), true), Lookups.singleton(DISPLAY_NAME)); - super.setName(LABEL_NAME); - super.setDisplayName(DISPLAY_NAME); - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/mail-icon-16.png"); //NON-NLS - initArtifacts(); - } - - @Override - public boolean isLeafTypeNode() { - return false; - } - - @Override - public T accept(DisplayableItemNodeVisitor v) { - //return v.visit(this); - return null; - } - - @Override - protected Sheet createSheet() { - Sheet s = super.createSheet(); - Sheet.Set ss = s.get(Sheet.PROPERTIES); - if (ss == null) { - ss = Sheet.createPropertiesSet(); - s.put(ss); - } - - ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.name"), - NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.displayName"), - NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.desc"), - getName())); - return s; - } - } - - /** - * Mail root child node showing flattened emails - */ - private class EmailExtractedRootChildrenFlat extends ChildFactory { - - private EmailExtractedRootChildrenFlat() { - super(); - } - - @Override - protected boolean createKeys(List list) { - //flatten all emails - List tempList = new ArrayList<>(); - for (String account : accounts.keySet()) { - Map> folders = accounts.get(account); - for (String folder : folders.keySet()) { - List messages = folders.get(folder); - for (long l : messages) { - try { - //TODO: bulk artifact gettings - tempList.add(skCase.getBlackboardArtifact(l)); - } catch (TskException ex) { - logger.log(Level.WARNING, "Error creating mail messages nodes", ex); //NON-NLS - } - } - } - } - - list.addAll(tempList); - return true; - } - - @Override - protected Node createNodeForKey(BlackboardArtifact artifact) { - return new BlackboardArtifactNode(artifact); - } - } +// public class FlatRootNode extends DisplayableItemNode { +// +// public FlatRootNode() { +// super(Children.create(new FlatRootFactory(), true), Lookups.singleton(DISPLAY_NAME)); +// super.setName(LABEL_NAME); +// super.setDisplayName(DISPLAY_NAME); +// this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/mail-icon-16.png"); //NON-NLS +// initArtifacts(); +// } +// +// @Override +// public boolean isLeafTypeNode() { +// return false; +// } +// +// @Override +// public T accept(DisplayableItemNodeVisitor v) { +// //return v.visit(this); +// return null; +// } +// +// @Override +// protected Sheet createSheet() { +// Sheet s = super.createSheet(); +// Sheet.Set ss = s.get(Sheet.PROPERTIES); +// if (ss == null) { +// ss = Sheet.createPropertiesSet(); +// s.put(ss); +// } +// +// ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.name"), +// NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.displayName"), +// NbBundle.getMessage(this.getClass(), "EmailExtracted.createSheet.name.desc"), +// getName())); +// return s; +// } +// } +// +// /** +// * Mail root child node showing flattened emails +// */ +// private class FlatRootFactory extends ChildFactory { +// +// private FlatRootFactory() { +// super(); +// } +// +// @Override +// protected boolean createKeys(List list) { +// //flatten all emails +// List tempList = new ArrayList<>(); +// for (String account : accounts.keySet()) { +// Map> folders = accounts.get(account); +// for (String folder : folders.keySet()) { +// List messages = folders.get(folder); +// for (long l : messages) { +// try { +// //TODO: bulk artifact gettings +// tempList.add(skCase.getBlackboardArtifact(l)); +// } catch (TskException ex) { +// logger.log(Level.WARNING, "Error creating mail messages nodes", ex); //NON-NLS +// } +// } +// } +// } +// +// list.addAll(tempList); +// return true; +// } +// +// @Override +// protected Node createNodeForKey(BlackboardArtifact artifact) { +// return new BlackboardArtifactNode(artifact); +// } +// } /** * Mail root node grouping all mail accounts, supports account-> folder * structure */ - public class EmailExtractedRootNode extends DisplayableItemNode { + public class RootNode extends DisplayableItemNode { - public EmailExtractedRootNode() { - super(Children.create(new EmailExtractedRootChildren(), true), Lookups.singleton(DISPLAY_NAME)); + public RootNode() { + super(Children.create(new AccountFactory(), true), Lookups.singleton(DISPLAY_NAME)); super.setName(LABEL_NAME); super.setDisplayName(DISPLAY_NAME); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/mail-icon-16.png"); //NON-NLS - initArtifacts(); + emailResults.update(); } @Override @@ -220,7 +249,6 @@ public class EmailExtracted implements AutopsyVisitableItem { @Override public T accept(DisplayableItemNodeVisitor v) { return v.visit(this); - //return null; } @Override @@ -244,30 +272,74 @@ public class EmailExtracted implements AutopsyVisitableItem { /** * Mail root child node creating each account node */ - private class EmailExtractedRootChildren extends ChildFactory { + private class AccountFactory extends ChildFactory.Detachable implements Observer { + + /* 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 = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + String eventType = evt.getPropertyName(); + + if (eventType.equals(IngestManager.IngestEvent.DATA.toString())) { + if (((ModuleDataEvent) evt.getOldValue()).getArtifactType() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG) { + emailResults.update(); + } + } + else if (eventType.equals(IngestManager.IngestEvent.INGEST_JOB_COMPLETED.toString()) + || eventType.equals(IngestManager.IngestEvent.INGEST_JOB_CANCELLED.toString())) { + emailResults.update(); + } + } + }; + + @Override + protected void addNotify() { + IngestManager.addPropertyChangeListener(pcl); + emailResults.addObserver(this); + } + @Override + protected void removeNotify() { + IngestManager.removePropertyChangeListener(pcl); + emailResults.deleteObserver(this); + } + @Override protected boolean createKeys(List list) { - list.addAll(accounts.keySet()); + list.addAll(emailResults.getAccounts()); return true; } @Override protected Node createNodeForKey(String key) { - return new EmailExtractedAccountNode(key, accounts.get(key)); + return new AccountNode(key); + } + + @Override + public void update(Observable o, Object arg) { + refresh(true); } } /** * Account node representation */ - public class EmailExtractedAccountNode extends DisplayableItemNode { - - public EmailExtractedAccountNode(String name, Map> children) { - super(Children.create(new EmailExtractedAccountChildrenNode(children), true), Lookups.singleton(name)); - super.setName(name); - super.setDisplayName(name + " (" + children.size() + ")"); + public class AccountNode extends DisplayableItemNode implements Observer { + private String accountName; + + public AccountNode(String accountName) { + super(Children.create(new FolderFactory(accountName), true), Lookups.singleton(accountName)); + super.setName(accountName); + this.accountName = accountName; this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/account-icon-16.png"); //NON-NLS + updateDisplayName(); + emailResults.addObserver(this); + } + + private void updateDisplayName() { + super.setDisplayName(accountName + " (" + emailResults.getFolders(accountName) + ")"); } @Override @@ -296,43 +368,61 @@ public class EmailExtracted implements AutopsyVisitableItem { public T accept(DisplayableItemNodeVisitor v) { return v.visit(this); } + + @Override + public void update(Observable o, Object arg) { + updateDisplayName(); + } } /** * Account node child creating sub nodes for every folder */ - private class EmailExtractedAccountChildrenNode extends ChildFactory { + private class FolderFactory extends ChildFactory implements Observer { - private Map> folders; + private String accountName; - private EmailExtractedAccountChildrenNode(Map> folders) { + private FolderFactory(String accountName) { super(); - this.folders = folders; + this.accountName = accountName; + emailResults.addObserver(this); } @Override protected boolean createKeys(List list) { - list.addAll(folders.keySet()); - + list.addAll(emailResults.getFolders(accountName)); return true; } @Override - protected Node createNodeForKey(String key) { - return new EmailExtractedFolderNode(key, folders.get(key)); + protected Node createNodeForKey(String folderName) { + return new FolderNode(accountName, folderName); + } + + @Override + public void update(Observable o, Object arg) { + refresh(true); } } /** * Node representing mail folder */ - public class EmailExtractedFolderNode extends DisplayableItemNode { - - public EmailExtractedFolderNode(String name, List children) { - super(Children.create(new EmailExtractedFolderChildrenNode(children), true), Lookups.singleton(name)); - super.setName(name); - super.setDisplayName(name + " (" + children.size() + ")"); + public class FolderNode extends DisplayableItemNode implements Observer { + private String accountName; + private String folderName; + + public FolderNode(String accountName, String folderName) { + super(Children.create(new MessageFactory(accountName, folderName), true), Lookups.singleton(accountName)); + super.setName(folderName); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/folder-icon-16.png"); //NON-NLS + updateDisplayName(); + emailResults.addObserver(this); + } + + private void updateDisplayName() { + super.setDisplayName(folderName + " (" + emailResults.getArtifactIds(accountName, folderName).size() + ")"); + } @Override @@ -361,38 +451,48 @@ public class EmailExtracted implements AutopsyVisitableItem { public T accept(DisplayableItemNodeVisitor v) { return v.visit(this); } + + @Override + public void update(Observable o, Object arg) { + updateDisplayName(); + } } /** * Node representing mail folder content (mail messages) */ - private class EmailExtractedFolderChildrenNode extends ChildFactory { + private class MessageFactory extends ChildFactory implements Observer { - private List messages; + private String accountName; + private String folderName; - private EmailExtractedFolderChildrenNode(List messages) { + private MessageFactory(String accountName, String folderName) { super(); - this.messages = messages; + this.accountName = accountName; + this.folderName = folderName; + emailResults.addObserver(this); } @Override - protected boolean createKeys(List list) { - List tempList = new ArrayList<>(); - for (long l : messages) { - try { - //TODO: bulk artifact gettings - tempList.add(skCase.getBlackboardArtifact(l)); - } catch (TskException ex) { - logger.log(Level.WARNING, "Error creating mail messages nodes", ex); //NON-NLS - } - } - list.addAll(tempList); + protected boolean createKeys(List list) { + list.addAll(emailResults.getArtifactIds(accountName, folderName)); return true; } @Override - protected Node createNodeForKey(BlackboardArtifact artifact) { - return new BlackboardArtifactNode(artifact); + protected Node createNodeForKey(Long artifactId) { + try { + BlackboardArtifact artifact = skCase.getBlackboardArtifact(artifactId); + return new BlackboardArtifactNode(artifact); + } catch (TskException ex) { + logger.log(Level.WARNING, "Error creating mail messages nodes", ex); //NON-NLS + } + return null; + } + + @Override + public void update(Observable o, Object arg) { + refresh(true); } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java index 394eb2228e..2be34d8cae 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2014 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,17 +18,59 @@ */ package org.sleuthkit.autopsy.datamodel; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.logging.Level; +import org.openide.nodes.ChildFactory; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.openide.nodes.Sheet; +import org.openide.util.NbBundle; +import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.autopsy.ingest.ModuleDataEvent; +import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_CALENDAR_ENTRY; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DEVICE_ATTACHED; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_SEARCH; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_INSTALLED_PROG; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_RECENT_OBJECT; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_SERVICE_ACCOUNT; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_SPEED_DIAL_ENTRY; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY; import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskException; /** - * Parent of the "extracted content" artifacts to be displayed in the tree. Other - * artifacts are displayed under other more specific parents. + * Parent of the "extracted content" artifacts to be displayed in the tree. + * Other artifacts are displayed under other more specific parents. */ - class ExtractedContent implements AutopsyVisitableItem{ +public class ExtractedContent implements AutopsyVisitableItem { - SleuthkitCase skCase; + private SleuthkitCase skCase; + public static final String NAME = NbBundle.getMessage(RootNode.class, "ExtractedContentNode.name.text"); - public ExtractedContent(SleuthkitCase skCase){ + public ExtractedContent(SleuthkitCase skCase) { this.skCase = skCase; } @@ -37,7 +79,310 @@ import org.sleuthkit.datamodel.SleuthkitCase; return v.visit(this); } - public SleuthkitCase getSleuthkitCase(){ + public SleuthkitCase getSleuthkitCase() { return skCase; } + + public class RootNode extends DisplayableItemNode { + + public RootNode(SleuthkitCase skCase) { + super(Children.create(new TypeFactory(skCase), true), Lookups.singleton(NAME)); + super.setName(NAME); + super.setDisplayName(NAME); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/extracted_content.png"); //NON-NLS + } + + @Override + public boolean isLeafTypeNode() { + return false; + } + + @Override + public T accept(DisplayableItemNodeVisitor v) { + return v.visit(this); + } + + @Override + protected Sheet createSheet() { + Sheet s = super.createSheet(); + Sheet.Set ss = s.get(Sheet.PROPERTIES); + if (ss == null) { + ss = Sheet.createPropertiesSet(); + s.put(ss); + } + + ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.name"), + NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.displayName"), + NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.desc"), + NAME)); + return s; + } + } + + /** + * 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 class TypeFactory extends ChildFactory.Detachable { + + private SleuthkitCase skCase; + private final ArrayList doNotShow; + // maps the artifact type to its child node + private HashMap typeNodeList = new HashMap<>(); + + public TypeFactory(SleuthkitCase skCase) { + super(); + this.skCase = skCase; + + // these are shown in other parts of the UI tree + doNotShow = new ArrayList<>(); + doNotShow.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_GEN_INFO); + doNotShow.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG); + doNotShow.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT); + doNotShow.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT); + doNotShow.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT); + doNotShow.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE); + doNotShow.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT); + doNotShow.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT); + } + + private final PropertyChangeListener pcl = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + String eventType = evt.getPropertyName(); + + if (eventType.equals(IngestManager.IngestEvent.DATA.toString())) { + final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue(); + if (doNotShow.contains(event.getArtifactType()) == false) { + refresh(true); + } + } else if (eventType.equals(IngestManager.IngestEvent.INGEST_JOB_COMPLETED.toString()) + || eventType.equals(IngestManager.IngestEvent.INGEST_JOB_CANCELLED.toString())) { + refresh(true); + } + } + }; + + @Override + protected void addNotify() { + IngestManager.addPropertyChangeListener(pcl); + } + + @Override + protected void removeNotify() { + IngestManager.removePropertyChangeListener(pcl); + typeNodeList.clear(); + } + + @Override + protected boolean createKeys(List list) { + try { + List inUse = skCase.getBlackboardArtifactTypesInUse(); + inUse.removeAll(doNotShow); + Collections.sort(inUse, + new Comparator() { + @Override + public int compare(BlackboardArtifact.ARTIFACT_TYPE a, BlackboardArtifact.ARTIFACT_TYPE b) { + return a.getDisplayName().compareTo(b.getDisplayName()); + } + }); + list.addAll(inUse); + + // the create node method will get called only for new types + // refresh the counts if we already created them from a previous update + for (BlackboardArtifact.ARTIFACT_TYPE art : inUse) { + TypeNode node = typeNodeList.get(art); + if (node != null) { + node.updateDisplayName(); + } + } + } catch (TskCoreException ex) { + Logger.getLogger(TypeFactory.class.getName()).log(Level.SEVERE, "Error getting list of artifacts in use: " + ex.getLocalizedMessage()); //NON-NLS + return false; + } + return true; + } + + @Override + protected Node createNodeForKey(BlackboardArtifact.ARTIFACT_TYPE key) { + TypeNode node = new TypeNode(key, skCase); + typeNodeList.put(key, node); + return node; + } + } + + /** + * Node encapsulating blackboard artifact type. This is used on the + * left-hand navigation side of the Autopsy UI as the parent node for all of + * the artifacts of a given type. Its children will be + * BlackboardArtifactNode objects. + */ + public class TypeNode extends DisplayableItemNode { + + private BlackboardArtifact.ARTIFACT_TYPE type; + private long childCount = 0; + private SleuthkitCase skCase; + + TypeNode(BlackboardArtifact.ARTIFACT_TYPE type, SleuthkitCase skCase) { + super(Children.create(new ArtifactFactory(type, skCase), true), Lookups.singleton(type.getDisplayName())); + super.setName(type.getLabel()); + this.skCase = skCase; + this.type = type; + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/" + getIcon(type)); //NON-NLS + updateDisplayName(); + } + + final void updateDisplayName() { + // NOTE: This completely destroys our lazy-loading ideal + // a performance increase might be had by adding a + // "getBlackboardArtifactCount()" method to skCase + try { + this.childCount = skCase.getBlackboardArtifactsTypeCount(type.getTypeID()); + } catch (TskException ex) { + Logger.getLogger(TypeNode.class.getName()) + .log(Level.WARNING, "Error getting child count", ex); //NON-NLS + } + super.setDisplayName(type.getDisplayName() + " (" + childCount + ")"); + } + + @Override + protected Sheet createSheet() { + Sheet s = super.createSheet(); + Sheet.Set ss = s.get(Sheet.PROPERTIES); + if (ss == null) { + ss = Sheet.createPropertiesSet(); + s.put(ss); + } + + ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.name"), + NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.displayName"), + NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.desc"), + type.getDisplayName())); + + ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.name"), + NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.displayName"), + NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.desc"), + childCount)); + + return s; + } + + @Override + public T accept(DisplayableItemNodeVisitor v) { + return v.visit(this); + } + + // @@@ TODO: Merge with BlackboartArtifactNode.getIcon() + private String getIcon(BlackboardArtifact.ARTIFACT_TYPE type) { + switch (type) { + case TSK_WEB_BOOKMARK: + return "bookmarks.png"; //NON-NLS + case TSK_WEB_COOKIE: + return "cookies.png"; //NON-NLS + case TSK_WEB_HISTORY: + return "history.png"; //NON-NLS + case TSK_WEB_DOWNLOAD: + return "downloads.png"; //NON-NLS + case TSK_INSTALLED_PROG: + return "programs.png"; //NON-NLS + case TSK_RECENT_OBJECT: + return "recent_docs.png"; //NON-NLS + case TSK_DEVICE_ATTACHED: + return "usb_devices.png"; //NON-NLS + case TSK_WEB_SEARCH_QUERY: + return "searchquery.png"; //NON-NLS + case TSK_METADATA_EXIF: + return "camera-icon-16.png"; //NON-NLS + case TSK_CONTACT: + return "contact.png"; //NON-NLS + case TSK_MESSAGE: + return "message.png"; //NON-NLS + case TSK_CALLLOG: + return "calllog.png"; //NON-NLS + case TSK_CALENDAR_ENTRY: + return "calendar.png"; //NON-NLS + case TSK_SPEED_DIAL_ENTRY: + return "speeddialentry.png"; //NON-NLS + case TSK_BLUETOOTH_PAIRING: + return "bluetooth.png"; //NON-NLS + case TSK_GPS_BOOKMARK: + return "gpsfav.png"; //NON-NLS + case TSK_GPS_LAST_KNOWN_LOCATION: + return "gps-lastlocation.png"; //NON-NLS + case TSK_GPS_SEARCH: + return "gps-search.png"; //NON-NLS + case TSK_SERVICE_ACCOUNT: + return "account-icon-16.png"; //NON-NLS + case TSK_ENCRYPTION_DETECTED: + return "encrypted-file.png"; //NON-NLS + case TSK_EXT_MISMATCH_DETECTED: + return "mismatch-16.png"; //NON-NLS + } + return "artifact-icon.png"; //NON-NLS + } + + @Override + public boolean isLeafTypeNode() { + return true; + } + } + + /** + * Creates children for a given artifact type + */ + private static class ArtifactFactory extends ChildFactory.Detachable { + + private SleuthkitCase skCase; + private BlackboardArtifact.ARTIFACT_TYPE type; + + public ArtifactFactory(BlackboardArtifact.ARTIFACT_TYPE type, SleuthkitCase skCase) { + super(); + this.skCase = skCase; + this.type = type; + } + + private final PropertyChangeListener pcl = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + String eventType = evt.getPropertyName(); + + if (eventType.equals(IngestManager.IngestEvent.DATA.toString())) { + final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue(); + if (event.getArtifactType() == type) { + refresh(true); + } + } else if (eventType.equals(IngestManager.IngestEvent.INGEST_JOB_COMPLETED.toString()) + || eventType.equals(IngestManager.IngestEvent.INGEST_JOB_CANCELLED.toString())) { + refresh(true); + } + } + }; + + @Override + protected void addNotify() { + IngestManager.addPropertyChangeListener(pcl); + } + + @Override + protected void removeNotify() { + IngestManager.removePropertyChangeListener(pcl); + } + + @Override + protected boolean createKeys(List list) { + try { + List arts = skCase.getBlackboardArtifacts(type.getTypeID()); + list.addAll(arts); + } catch (TskException ex) { + Logger.getLogger(ArtifactFactory.class.getName()).log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS + } + return true; + } + + @Override + protected Node createNodeForKey(BlackboardArtifact key) { + return new BlackboardArtifactNode(key); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java index e2970c3993..b555f385d7 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java @@ -18,12 +18,16 @@ */ package org.sleuthkit.autopsy.datamodel; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Observable; +import java.util.Observer; import java.util.Set; import java.util.logging.Level; @@ -34,13 +38,16 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; +import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskException; /** - * Hash set hits node support + * Hash set hits node support. Inner classes have all of the nodes in the tree. */ public class HashsetHits implements AutopsyVisitableItem { @@ -48,65 +55,85 @@ public class HashsetHits implements AutopsyVisitableItem { private static final String DISPLAY_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getDisplayName(); private static final Logger logger = Logger.getLogger(HashsetHits.class.getName()); private SleuthkitCase skCase; - private Map> hashSetHitsMap; - + private HashsetResults hashsetResults; + public HashsetHits(SleuthkitCase skCase) { this.skCase = skCase; - hashSetHitsMap = new LinkedHashMap<>(); - } - - @SuppressWarnings("deprecation") - private void initArtifacts() { - hashSetHitsMap.clear(); - ResultSet rs = null; - try { - int setNameId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(); - int artId = BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(); - String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS - + "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS - + "attribute_type_id=" + setNameId //NON-NLS - + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS - + " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS - rs = skCase.runQuery(query); - while (rs.next()) { - String value = rs.getString("value_text"); //NON-NLS - long artifactId = rs.getLong("artifact_id"); //NON-NLS - if (!hashSetHitsMap.containsKey(value)) { - hashSetHitsMap.put(value, new HashSet()); - } - hashSetHitsMap.get(value).add(artifactId); - - } - - } catch (SQLException ex) { - logger.log(Level.WARNING, "SQL Exception occurred: ", ex); //NON-NLS - } finally { - if (rs != null) { - try { - skCase.closeRunQuery(rs); - } catch (SQLException ex) { - logger.log(Level.WARNING, "Error closing result set after getting hashset hits", ex); //NON-NLS - } - } - } + hashsetResults = new HashsetResults(); } + @Override public T accept(AutopsyItemVisitor v) { return v.visit(this); } + + /** + * Stores all of the hashset results in a single class that is observable for the + * child nodes + */ + private class HashsetResults extends Observable { + // maps hashset name to list of artifacts for that set + private Map> hashSetHitsMap = new LinkedHashMap<>(); + + HashsetResults() { + update(); + } + + Set getSetNames() { + return hashSetHitsMap.keySet(); + } + + Set getArtifactIds(String hashSetName) { + return hashSetHitsMap.get(hashSetName); + } + + final void update() { + hashSetHitsMap.clear(); + ResultSet rs = null; + try { + int setNameId = ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(); + int artId = ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(); + String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS + + "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS + + "attribute_type_id=" + setNameId //NON-NLS + + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS + + " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS + rs = skCase.runQuery(query); + while (rs.next()) { + String setName = rs.getString("value_text"); //NON-NLS + long artifactId = rs.getLong("artifact_id"); //NON-NLS + if (!hashSetHitsMap.containsKey(setName)) { + hashSetHitsMap.put(setName, new HashSet()); + } + hashSetHitsMap.get(setName).add(artifactId); + } + } catch (SQLException ex) { + logger.log(Level.WARNING, "SQL Exception occurred: ", ex); //NON-NLS + } finally { + if (rs != null) { + try { + skCase.closeRunQuery(rs); + } catch (SQLException ex) { + logger.log(Level.WARNING, "Error closing result set after getting hashset hits", ex); //NON-NLS + } + } + } + setChanged(); + notifyObservers(); + } + } /** - * Node for the hash set hits + * Top-level node for all hash sets */ - public class HashsetHitsRootNode extends DisplayableItemNode { + public class RootNode extends DisplayableItemNode { - public HashsetHitsRootNode() { - super(Children.create(new HashsetHitsRootChildren(), true), Lookups.singleton(DISPLAY_NAME)); + public RootNode() { + super(Children.create(new HashsetNameFactory(), true), Lookups.singleton(DISPLAY_NAME)); super.setName(HASHSET_HITS); super.setDisplayName(DISPLAY_NAME); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/hashset_hits.png"); //NON-NLS - initArtifacts(); } @Override @@ -137,27 +164,80 @@ public class HashsetHits implements AutopsyVisitableItem { } } - private class HashsetHitsRootChildren extends ChildFactory { + /** + * Creates child nodes for each hashset name + */ + private class HashsetNameFactory extends ChildFactory.Detachable implements Observer { + + /* This should probably be in the HashsetHits class, but the factory has nice methods + * for its startup and shutdown, so it seemed like a cleaner place to register the + * property change listener. + */ + private final PropertyChangeListener pcl = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + String eventType = evt.getPropertyName(); + + if (eventType.equals(IngestManager.IngestEvent.DATA.toString())) { + if (((ModuleDataEvent) evt.getOldValue()).getArtifactType() == ARTIFACT_TYPE.TSK_HASHSET_HIT) { + hashsetResults.update(); + } + } + else if (eventType.equals(IngestManager.IngestEvent.INGEST_JOB_COMPLETED.toString()) + || eventType.equals(IngestManager.IngestEvent.INGEST_JOB_CANCELLED.toString())) { + hashsetResults.update(); + } + } + }; @Override - protected boolean createKeys(List list) { - list.addAll(hashSetHitsMap.keySet()); + protected void addNotify() { + IngestManager.addPropertyChangeListener(pcl); + hashsetResults.addObserver(this); + } + + @Override + protected void removeNotify() { + IngestManager.removePropertyChangeListener(pcl); + hashsetResults.deleteObserver(this); + } + + @Override + protected boolean createKeys(List list) { + list.addAll(hashsetResults.getSetNames()); return true; } @Override protected Node createNodeForKey(String key) { - return new HashsetHitsSetNode(key, hashSetHitsMap.get(key)); + return new HashsetNameNode(key); + } + + @Override + public void update(Observable o, Object arg) { + refresh(true); } } - public class HashsetHitsSetNode extends DisplayableItemNode { - - public HashsetHitsSetNode(String name, Set children) { - super(Children.create(new HashsetHitsSetChildren(children), true), Lookups.singleton(name)); - super.setName(name); - super.setDisplayName(name + " (" + children.size() + ")"); + /** + * Node for a hash set name + */ + public class HashsetNameNode extends DisplayableItemNode implements Observer { + private String hashSetName; + public HashsetNameNode(String hashSetName) { + super(Children.create(new HitFactory(hashSetName), true), Lookups.singleton(hashSetName)); + super.setName(hashSetName); + super.setDisplayName(hashSetName + " (0)"); + this.hashSetName = hashSetName; this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/hashset_hits.png"); //NON-NLS + hashsetResults.addObserver(this); + } + + /** + * Update the count in the display name + */ + private void updateName() { + super.setDisplayName(hashSetName + " (" + hashsetResults.getArtifactIds(hashSetName).size() + ")"); } @Override @@ -186,33 +266,54 @@ public class HashsetHits implements AutopsyVisitableItem { public T accept(DisplayableItemNodeVisitor v) { return v.visit(this); } + + @Override + public void update(Observable o, Object arg) { + updateName(); + } } - private class HashsetHitsSetChildren extends ChildFactory { - - private Set children; - - private HashsetHitsSetChildren(Set children) { + /** + * Creates the nodes for the hits in a given set. + */ + private class HitFactory extends ChildFactory.Detachable implements Observer { + private String hashsetName; + + private HitFactory(String hashsetName) { super(); - this.children = children; + this.hashsetName = hashsetName; + } + + @Override + protected void addNotify() { + hashsetResults.addObserver(this); } @Override - protected boolean createKeys(List list) { - for (long l : children) { - try { - //TODO: bulk artifact gettings - list.add(skCase.getBlackboardArtifact(l)); - } catch (TskException ex) { - logger.log(Level.WARNING, "TSK Exception occurred", ex); //NON-NLS - } - } + protected void removeNotify() { + hashsetResults.deleteObserver(this); + } + + @Override + protected boolean createKeys(List list) { + list.addAll(hashsetResults.getArtifactIds(hashsetName)); return true; } @Override - protected Node createNodeForKey(BlackboardArtifact artifact) { - return new BlackboardArtifactNode(artifact); + protected Node createNodeForKey(Long id) { + try { + BlackboardArtifact art = skCase.getBlackboardArtifact(id); + return new BlackboardArtifactNode(art); + } catch (TskException ex) { + logger.log(Level.WARNING, "TSK Exception occurred", ex); //NON-NLS + } + return null; + } + + @Override + public void update(Observable o, Object arg) { + refresh(true); } } -} +} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java index f084188ef7..2abfaf1f9a 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java @@ -106,10 +106,10 @@ public class InterestingHits implements AutopsyVisitableItem { /** * Node for the interesting items */ - public class InterestingHitsRootNode extends DisplayableItemNode { + public class RootNode extends DisplayableItemNode { - public InterestingHitsRootNode() { - super(Children.create(new InterestingHitsRootChildren(), true), Lookups.singleton(DISPLAY_NAME)); + public RootNode() { + super(Children.create(new SetNameFactory(), true), Lookups.singleton(DISPLAY_NAME)); super.setName(INTERESTING_ITEMS); super.setDisplayName(DISPLAY_NAME); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS @@ -144,7 +144,7 @@ public class InterestingHits implements AutopsyVisitableItem { } } - private class InterestingHitsRootChildren extends ChildFactory { + private class SetNameFactory extends ChildFactory { @Override protected boolean createKeys(List list) { @@ -154,14 +154,14 @@ public class InterestingHits implements AutopsyVisitableItem { @Override protected Node createNodeForKey(String key) { - return new InterestingHitsSetNode(key, interestingItemsMap.get(key)); + return new SetNameNode(key, interestingItemsMap.get(key)); } } - public class InterestingHitsSetNode extends DisplayableItemNode { + public class SetNameNode extends DisplayableItemNode { - public InterestingHitsSetNode(String name, Set children) { - super(Children.create(new InterestingHitsSetChildren(children), true), Lookups.singleton(name)); + public SetNameNode(String name, Set children) { + super(Children.create(new HitFactory(children), true), Lookups.singleton(name)); super.setName(name); super.setDisplayName(name + " (" + children.size() + ")"); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS @@ -195,11 +195,11 @@ public class InterestingHits implements AutopsyVisitableItem { } } - private class InterestingHitsSetChildren extends ChildFactory { + private class HitFactory extends ChildFactory { private Set children; - private InterestingHitsSetChildren(Set children) { + private HitFactory(Set children) { super(); this.children = children; } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java index e031fe4fc7..69b1b246f3 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java @@ -18,13 +18,16 @@ */ package org.sleuthkit.autopsy.datamodel; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Observable; +import java.util.Observer; import java.util.Set; import java.util.logging.Level; import org.openide.util.NbBundle; @@ -34,6 +37,8 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.SleuthkitCase; @@ -54,101 +59,123 @@ public class KeywordHits implements AutopsyVisitableItem { .getMessage(KeywordHits.class, "KeywordHits.simpleLiteralSearch.text"); public static final String SIMPLE_REGEX_SEARCH = NbBundle .getMessage(KeywordHits.class, "KeywordHits.singleRegexSearch.text"); - // Map from String (list name) to Map from string (keyword) to set (artifact ids) - private Map>> topLevelMap; - private Map>> listsMap; - // Map from String (literal keyword) to set (artifact ids) - private Map> literalMap; - // Map from String (regex keyword) to set (artifact ids); - private Map> regexMap; - Map> artifacts; + private KeywordResults keywordResults; public KeywordHits(SleuthkitCase skCase) { this.skCase = skCase; - artifacts = new LinkedHashMap<>(); - listsMap = new LinkedHashMap<>(); - literalMap = new LinkedHashMap<>(); - regexMap = new LinkedHashMap<>(); - topLevelMap = new LinkedHashMap<>(); + keywordResults = new KeywordResults(); } - - private void initMaps() { - topLevelMap.clear(); - topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap); - topLevelMap.put(SIMPLE_REGEX_SEARCH, regexMap); - listsMap.clear(); - regexMap.clear(); - literalMap.clear(); - for (Map.Entry> art : artifacts.entrySet()) { - long id = art.getKey(); - Map attributes = art.getValue(); - // I think we can use attributes.remove(...) here? - String listName = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID())); - String word = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID())); - String reg = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID())); - if (listName != null) { - if (!listsMap.containsKey(listName)) { - listsMap.put(listName, new LinkedHashMap>()); - } - if (!listsMap.get(listName).containsKey(word)) { - listsMap.get(listName).put(word, new HashSet()); - } - listsMap.get(listName).get(word).add(id); - } else if (reg != null) { - if (!regexMap.containsKey(reg)) { - regexMap.put(reg, new HashSet()); - } - regexMap.get(reg).add(id); - } else { - if (!literalMap.containsKey(word)) { - literalMap.put(word, new HashSet()); - } - literalMap.get(word).add(id); - } - topLevelMap.putAll(listsMap); + + private final class KeywordResults extends Observable { + // Map from listName/Type to Map of keyword to set of artifact Ids + private Map>> topLevelMap; + + KeywordResults() { + topLevelMap = new LinkedHashMap<>(); + update(); + } + + Set getListNames() { + return topLevelMap.keySet(); + } + + Set getKeywords(String listName) { + return topLevelMap.get(listName).keySet(); + } + + Set getArtifactIds(String listName, String keyword) { + return topLevelMap.get(listName).get(keyword); } - } - @SuppressWarnings("deprecation") - private void initArtifacts() { - artifacts.clear(); - ResultSet rs = null; - try { - int setId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(); - int wordId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID(); - int regexId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID(); - int artId = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(); - String query = "SELECT blackboard_attributes.value_text,blackboard_attributes.artifact_id," //NON-NLS - + "blackboard_attributes.attribute_type_id FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS - + "(blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id AND " //NON-NLS - + "blackboard_artifacts.artifact_type_id=" + artId //NON-NLS - + ") AND (attribute_type_id=" + setId + " OR " //NON-NLS - + "attribute_type_id=" + wordId + " OR " //NON-NLS - + "attribute_type_id=" + regexId + ")"; //NON-NLS - rs = skCase.runQuery(query); - while (rs.next()) { - String value = rs.getString("value_text"); //NON-NLS - long artifactId = rs.getLong("artifact_id"); //NON-NLS - long typeId = rs.getLong("attribute_type_id"); //NON-NLS - if (!artifacts.containsKey(artifactId)) { - artifacts.put(artifactId, new LinkedHashMap()); + // populate maps based on artifactIds + void populateMaps(Map> artifactIds) { + topLevelMap.clear(); + + Map>> listsMap = new LinkedHashMap<>(); + // Map from String (literal keyword) to set (artifact ids) + Map> literalMap = new LinkedHashMap<>(); + // Map from String (regex keyword) to set (artifact ids); + Map> regexMap = new LinkedHashMap<>(); + + + // top-level nodes + topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap); + topLevelMap.put(SIMPLE_REGEX_SEARCH, regexMap); + + for (Map.Entry> art : artifactIds.entrySet()) { + long id = art.getKey(); + Map attributes = art.getValue(); + + // I think we can use attributes.remove(...) here? + String listName = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID())); + String word = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID())); + String reg = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID())); + if (listName != null) { + if (!listsMap.containsKey(listName)) { + listsMap.put(listName, new LinkedHashMap>()); + } + if (!listsMap.get(listName).containsKey(word)) { + listsMap.get(listName).put(word, new HashSet()); + } + listsMap.get(listName).get(word).add(id); + } else if (reg != null) { + if (!regexMap.containsKey(reg)) { + regexMap.put(reg, new HashSet()); + } + regexMap.get(reg).add(id); + } else { + if (!literalMap.containsKey(word)) { + literalMap.put(word, new HashSet()); + } + literalMap.get(word).add(id); } - if (!value.equals("")) { - artifacts.get(artifactId).put(typeId, value); - } - + topLevelMap.putAll(listsMap); } - - } catch (SQLException ex) { - logger.log(Level.WARNING, "SQL Exception occurred: ", ex); //NON-NLS - } finally { - if (rs != null) { - try { - skCase.closeRunQuery(rs); - } catch (SQLException ex) { - logger.log(Level.WARNING, "Error closing result set after getting keyword hits", ex); //NON-NLS + + setChanged(); + notifyObservers(); + } + + public void update() { + Map> artifactIds = new LinkedHashMap<>(); + + ResultSet rs = null; + try { + int setId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(); + int wordId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID(); + int regexId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID(); + int artId = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(); + String query = "SELECT blackboard_attributes.value_text,blackboard_attributes.artifact_id," //NON-NLS + + "blackboard_attributes.attribute_type_id FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS + + "(blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id AND " //NON-NLS + + "blackboard_artifacts.artifact_type_id=" + artId //NON-NLS + + ") AND (attribute_type_id=" + setId + " OR " //NON-NLS + + "attribute_type_id=" + wordId + " OR " //NON-NLS + + "attribute_type_id=" + regexId + ")"; //NON-NLS + rs = skCase.runQuery(query); + while (rs.next()) { + String value = rs.getString("value_text"); //NON-NLS + long artifactId = rs.getLong("artifact_id"); //NON-NLS + long typeId = rs.getLong("attribute_type_id"); //NON-NLS + if (!artifactIds.containsKey(artifactId)) { + artifactIds.put(artifactId, new LinkedHashMap()); + } + if (!value.equals("")) { + artifactIds.get(artifactId).put(typeId, value); + } + } + } catch (SQLException ex) { + logger.log(Level.WARNING, "SQL Exception occurred: ", ex); //NON-NLS + } finally { + if (rs != null) { + try { + skCase.closeRunQuery(rs); + } catch (SQLException ex) { + logger.log(Level.WARNING, "Error closing result set after getting keyword hits", ex); //NON-NLS + } } } + populateMaps(artifactIds); } } @@ -157,15 +184,14 @@ public class KeywordHits implements AutopsyVisitableItem { return v.visit(this); } - public class KeywordHitsRootNode extends DisplayableItemNode { + // Created by CreateAutopsyNodeVisitor + public class RootNode extends DisplayableItemNode { - public KeywordHitsRootNode() { - super(Children.create(new KeywordHitsRootChildren(), true), Lookups.singleton(KEYWORD_HITS)); + public RootNode() { + super(Children.create(new ListFactory(), true), Lookups.singleton(KEYWORD_HITS)); super.setName(NAME); super.setDisplayName(KEYWORD_HITS); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS - initArtifacts(); - initMaps(); } @Override @@ -196,36 +222,73 @@ public class KeywordHits implements AutopsyVisitableItem { } } - private class KeywordHitsRootChildren extends ChildFactory { + private class ListFactory extends ChildFactory.Detachable implements Observer { + + private final PropertyChangeListener pcl = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + String eventType = evt.getPropertyName(); + + if (eventType.equals(IngestManager.IngestEvent.DATA.toString())) { + if (((ModuleDataEvent) evt.getOldValue()).getArtifactType() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT) { + keywordResults.update(); + } + } + else if (eventType.equals(IngestManager.IngestEvent.INGEST_JOB_COMPLETED.toString()) + || eventType.equals(IngestManager.IngestEvent.INGEST_JOB_CANCELLED.toString())) { + keywordResults.update(); + } + } + }; + + @Override + protected void addNotify() { + IngestManager.addPropertyChangeListener(pcl); + keywordResults.addObserver(this); + } + @Override + protected void removeNotify() { + IngestManager.removePropertyChangeListener(pcl); + keywordResults.deleteObserver(this); + } + @Override protected boolean createKeys(List list) { - list.addAll(topLevelMap.keySet()); + list.addAll(keywordResults.getListNames()); return true; } @Override protected Node createNodeForKey(String key) { - return new KeywordHitsListNode(key, topLevelMap.get(key)); + return new ListNode(key); + } + + @Override + public void update(Observable o, Object arg) { + refresh(true); } } - public class KeywordHitsListNode extends DisplayableItemNode { + public class ListNode extends DisplayableItemNode implements Observer { + private String listName; - private String name; - private Map> children; - - public KeywordHitsListNode(String name, Map> children) { - super(Children.create(new KeywordHitsListChildren(children), true), Lookups.singleton(name)); - super.setName(name); - int totalDescendants = 0; - for (Set grandChildren : children.values()) { - totalDescendants += grandChildren.size(); - } - super.setDisplayName(name + " (" + totalDescendants + ")"); + public ListNode(String listName) { + super(Children.create(new TermFactory(listName), true), Lookups.singleton(listName)); + super.setName(listName); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS - this.name = name; - this.children = children; + this.listName = listName; + updateDisplayName(); + keywordResults.addObserver(this); + } + + private void updateDisplayName() { + int totalDescendants = 0; + for (String word : keywordResults.getKeywords(listName)) { + Set ids = keywordResults.getArtifactIds(listName, word); + totalDescendants += ids.size(); + } + super.setDisplayName(listName + " (" + totalDescendants + ")"); } @Override @@ -240,13 +303,13 @@ public class KeywordHits implements AutopsyVisitableItem { ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.listName.name"), NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.listName.displayName"), NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.listName.desc"), - name)); + listName)); ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.numChildren.name"), NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.numChildren.displayName"), NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.numChildren.desc"), - children.size())); + keywordResults.getKeywords(listName).size())); return s; } @@ -260,39 +323,71 @@ public class KeywordHits implements AutopsyVisitableItem { public T accept(DisplayableItemNodeVisitor v) { return v.visit(this); } + + @Override + public void update(Observable o, Object arg) { + updateDisplayName(); + } } - private class KeywordHitsListChildren extends ChildFactory { - - private Map> children; - - private KeywordHitsListChildren(Map> children) { + private class TermFactory extends ChildFactory.Detachable implements Observer { + private String setName; + + private TermFactory(String setName) { super(); - this.children = children; + this.setName = setName; } + @Override + protected void addNotify() { + keywordResults.addObserver(this); + } + + @Override + protected void removeNotify() { + keywordResults.deleteObserver(this); + } + @Override protected boolean createKeys(List list) { - list.addAll(children.keySet()); + list.addAll(keywordResults.getKeywords(setName)); return true; } @Override protected Node createNodeForKey(String key) { - return new KeywordHitsKeywordNode(key, children.get(key)); + return new TermNode(setName, key); + } + + @Override + public void update(Observable o, Object arg) { + refresh(true); } } - public class KeywordHitsKeywordNode extends DisplayableItemNode { + public class TermNode extends DisplayableItemNode implements Observer { - private Set children; + private String setName; + private String keyword; - public KeywordHitsKeywordNode(String name, Set children) { - super(Children.create(new KeywordHitsKeywordChildren(children), true), Lookups.singleton(name)); - super.setName(name); - super.setDisplayName(name + " (" + children.size() + ")"); + public TermNode(String setName, String keyword) { + super(Children.create(new HitsFactory (setName, keyword), true), Lookups.singleton(keyword)); + super.setName(keyword); + this.setName = setName; + this.keyword = keyword; this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS - this.children = children; + updateDisplayName(); + keywordResults.addObserver(this); + } + + private void updateDisplayName() { + super.setDisplayName(keyword + " (" + keywordResults.getArtifactIds(setName, keyword).size() + ")"); + } + + + @Override + public void update(Observable o, Object arg) { + updateDisplayName(); } @Override @@ -322,70 +417,83 @@ public class KeywordHits implements AutopsyVisitableItem { ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.filesWithHits.name"), NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.filesWithHits.displayName"), NbBundle.getMessage(this.getClass(), "KeywordHits.createSheet.filesWithHits.desc"), - children.size())); + keywordResults.getArtifactIds(setName, keyword).size())); return s; } } - private class KeywordHitsKeywordChildren extends ChildFactory { - - private Set children; - - private KeywordHitsKeywordChildren(Set children) { + public class HitsFactory extends ChildFactory.Detachable implements Observer { + private String keyword; + private String setName; + + public HitsFactory(String setName, String keyword) { super(); - this.children = children; + this.setName = setName; + this.keyword = keyword; + } + + @Override + protected void addNotify() { + keywordResults.addObserver(this); } @Override - protected boolean createKeys(List list) { - List tempList = new ArrayList<>(); - for (long l : children) { - try { - //TODO: bulk artifact gettings - tempList.add(skCase.getBlackboardArtifact(l)); - } catch (TskException ex) { - logger.log(Level.WARNING, "TSK Exception occurred", ex); //NON-NLS - } - } - list.addAll(tempList); + protected void removeNotify() { + keywordResults.deleteObserver(this); + } + + @Override + protected boolean createKeys(List list) { + list.addAll(keywordResults.getArtifactIds(setName, keyword)); return true; } + @Override - protected Node createNodeForKey(BlackboardArtifact artifact) { - BlackboardArtifactNode n = new BlackboardArtifactNode(artifact); - AbstractFile file; + protected Node createNodeForKey(Long artifactId) { try { - file = artifact.getSleuthkitCase().getAbstractFileById(artifact.getObjectID()); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren"); //NON-NLS - return n; + BlackboardArtifact art = skCase.getBlackboardArtifact(artifactId); + BlackboardArtifactNode n = new BlackboardArtifactNode(art); + AbstractFile file; + try { + file = art.getSleuthkitCase().getAbstractFileById(art.getObjectID()); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren"); //NON-NLS + return n; + } + + n.addNodeProperty(new NodeProperty<>( + NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.modTime.name"), + NbBundle.getMessage(this.getClass(), + "KeywordHits.createNodeForKey.modTime.displayName"), + NbBundle.getMessage(this.getClass(), + "KeywordHits.createNodeForKey.modTime.desc"), + ContentUtils.getStringTime(file.getMtime(), file))); + n.addNodeProperty(new NodeProperty<>( + NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.accessTime.name"), + NbBundle.getMessage(this.getClass(), + "KeywordHits.createNodeForKey.accessTime.displayName"), + NbBundle.getMessage(this.getClass(), + "KeywordHits.createNodeForKey.accessTime.desc"), + ContentUtils.getStringTime(file.getAtime(), file))); + n.addNodeProperty(new NodeProperty<>( + NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.chgTime.name"), + NbBundle.getMessage(this.getClass(), + "KeywordHits.createNodeForKey.chgTime.displayName"), + NbBundle.getMessage(this.getClass(), + "KeywordHits.createNodeForKey.chgTime.desc"), + ContentUtils.getStringTime(file.getCtime(), file))); + return n; + } catch (TskException ex) { + logger.log(Level.WARNING, "TSK Exception occurred", ex); //NON-NLS } + return null; + } - n.addNodeProperty(new NodeProperty<>( - NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.modTime.name"), - NbBundle.getMessage(this.getClass(), - "KeywordHits.createNodeForKey.modTime.displayName"), - NbBundle.getMessage(this.getClass(), - "KeywordHits.createNodeForKey.modTime.desc"), - ContentUtils.getStringTime(file.getMtime(), file))); - n.addNodeProperty(new NodeProperty<>( - NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.accessTime.name"), - NbBundle.getMessage(this.getClass(), - "KeywordHits.createNodeForKey.accessTime.displayName"), - NbBundle.getMessage(this.getClass(), - "KeywordHits.createNodeForKey.accessTime.desc"), - ContentUtils.getStringTime(file.getAtime(), file))); - n.addNodeProperty(new NodeProperty<>( - NbBundle.getMessage(this.getClass(), "KeywordHits.createNodeForKey.chgTime.name"), - NbBundle.getMessage(this.getClass(), - "KeywordHits.createNodeForKey.chgTime.displayName"), - NbBundle.getMessage(this.getClass(), - "KeywordHits.createNodeForKey.chgTime.desc"), - ContentUtils.getStringTime(file.getCtime(), file))); - - return n; + @Override + public void update(Observable o, Object arg) { + refresh(true); } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java index da6843c8d2..ffc00fe367 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java @@ -65,6 +65,8 @@ public class RootContentChildren extends AbstractContentChildren { //TODO this will be removed, Children should be listening for interesting //events from datamodel and calling refresh / refreshKey() themselves public void refreshKeys(BlackboardArtifact.ARTIFACT_TYPE... types) { + // find the corresponding top-level node and refresh its children. + // should be more effeciently stored than a list. for (Object o : contentKeys) { for (BlackboardArtifact.ARTIFACT_TYPE type : types) { switch (type) { @@ -91,8 +93,10 @@ public class RootContentChildren extends AbstractContentChildren { this.refreshKey(o); break; default: - if (o instanceof ExtractedContent) + if (o instanceof ExtractedContent) { + this.refreshKey(o); + } break; } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/TagNameNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/TagNameNode.java index 1ccf5a8465..8949671495 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/TagNameNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/TagNameNode.java @@ -27,6 +27,7 @@ import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.directorytree.BlackboardArtifactTagTypeNode; import org.sleuthkit.datamodel.TagName; @@ -52,22 +53,26 @@ public class TagNameNode extends DisplayableItemNode { NbBundle.getMessage(TagNameNode.class, "TagNameNode.namePlusTags.text", tagName.getDisplayName()))); this.tagName = tagName; - long tagsCount = 0; - try { - tagsCount = Case.getCurrentCase().getServices().getTagsManager().getContentTagsCountByTagName(tagName); - tagsCount += Case.getCurrentCase().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName); - } catch (TskCoreException ex) { - Logger.getLogger(TagNameNode.class.getName()).log(Level.SEVERE, "Failed to get tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS - } - - super.setName(tagName.getDisplayName()); - super.setDisplayName(tagName.getDisplayName() + " (" + tagsCount + ")"); + setName(tagName.getDisplayName()); + updateDisplayName(); if (tagName.getDisplayName().equals(NbBundle.getMessage(this.getClass(), "TagNameNode.bookmark.text"))) { setIconBaseWithExtension(BOOKMARK_TAG_ICON_PATH); } else { setIconBaseWithExtension(ICON_PATH); } } + + private void updateDisplayName() { + long tagsCount = 0; + try { + TagsManager tm = Case.getCurrentCase().getServices().getTagsManager(); + tagsCount = tm.getContentTagsCountByTagName(tagName); + tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName); + } catch (TskCoreException ex) { + Logger.getLogger(TagNameNode.class.getName()).log(Level.SEVERE, "Failed to get tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS + } + setDisplayName(tagName.getDisplayName() + " (" + tagsCount + ")"); + } @Override protected Sheet createSheet() { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/TagsNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/TagsNode.java index 81b5c53c8b..d6ceff425c 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/TagsNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/TagsNode.java @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.datamodel; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.util.List; import java.util.logging.Level; import org.openide.nodes.ChildFactory; @@ -28,6 +30,9 @@ import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.autopsy.ingest.ModuleDataEvent; +import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskCoreException; @@ -76,8 +81,36 @@ class TagsNode extends DisplayableItemNode { return propertySheet; } - private static class TagNameNodeFactory extends ChildFactory { + private static class TagNameNodeFactory extends ChildFactory.Detachable { + private final PropertyChangeListener pcl = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + String eventType = evt.getPropertyName(); + + if (eventType.equals(IngestManager.IngestEvent.DATA.toString())) { + if ((((ModuleDataEvent) evt.getOldValue()).getArtifactType() == BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT) || + ((ModuleDataEvent) evt.getOldValue()).getArtifactType() == BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE) { + refresh(true); + } + } + else if (eventType.equals(IngestManager.IngestEvent.INGEST_JOB_COMPLETED.toString()) + || eventType.equals(IngestManager.IngestEvent.INGEST_JOB_CANCELLED.toString())) { + refresh(true); + } + } + }; + + @Override + protected void addNotify() { + IngestManager.addPropertyChangeListener(pcl); + } + + @Override + protected void removeNotify() { + IngestManager.removePropertyChangeListener(pcl); + } + @Override protected boolean createKeys(List keys) { try { diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index 4facc9603a..fe0f34f2a1 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -33,14 +33,12 @@ import javax.swing.AbstractAction; import javax.swing.Action; import org.openide.explorer.ExplorerManager; import org.openide.nodes.AbstractNode; -import org.openide.nodes.ChildFactory; import org.openide.nodes.FilterNode; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType; import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode; -import org.sleuthkit.autopsy.datamodel.ArtifactTypeNode; import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; import org.sleuthkit.autopsy.datamodel.ContentTagTypeNode; import org.sleuthkit.autopsy.datamodel.LocalFileNode; @@ -48,27 +46,27 @@ import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsChildren.De import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; -import org.sleuthkit.autopsy.datamodel.EmailExtracted.EmailExtractedAccountNode; -import org.sleuthkit.autopsy.datamodel.EmailExtracted.EmailExtractedFolderNode; -import org.sleuthkit.autopsy.datamodel.EmailExtracted.EmailExtractedRootNode; -import org.sleuthkit.autopsy.datamodel.ExtractedContentNode; +import org.sleuthkit.autopsy.datamodel.EmailExtracted.AccountNode; +import org.sleuthkit.autopsy.datamodel.EmailExtracted.FolderNode; +import org.sleuthkit.autopsy.datamodel.EmailExtracted; +import org.sleuthkit.autopsy.datamodel.ExtractedContent.TypeNode; +import org.sleuthkit.autopsy.datamodel.ExtractedContent; import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.datamodel.FileTypeNode; import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootChildren.FileSizeNode; import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootNode; -import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsRootNode; -import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsSetNode; -import org.sleuthkit.autopsy.datamodel.InterestingHits.InterestingHitsRootNode; -import org.sleuthkit.autopsy.datamodel.InterestingHits.InterestingHitsSetNode; +import org.sleuthkit.autopsy.datamodel.HashsetHits; +import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetNameNode; import org.sleuthkit.autopsy.datamodel.ImageNode; -import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsKeywordNode; -import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsListNode; -import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsRootNode; +import org.sleuthkit.autopsy.datamodel.InterestingHits; +import org.sleuthkit.autopsy.datamodel.KeywordHits.TermNode; +import org.sleuthkit.autopsy.datamodel.KeywordHits.ListNode; import org.sleuthkit.autopsy.datamodel.VirtualDirectoryNode; import org.sleuthkit.autopsy.datamodel.LayoutFileNode; import org.sleuthkit.autopsy.datamodel.RecentFilesFilterNode; import org.sleuthkit.autopsy.datamodel.RecentFilesNode; import org.sleuthkit.autopsy.datamodel.FileTypesNode; +import org.sleuthkit.autopsy.datamodel.KeywordHits; import org.sleuthkit.autopsy.datamodel.TagNameNode; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -367,47 +365,47 @@ public class DataResultFilterNode extends FilterNode { } @Override - public AbstractAction visit(ExtractedContentNode ecn) { + public AbstractAction visit(ExtractedContent.RootNode ecn) { return openChild(ecn); } @Override - public AbstractAction visit(KeywordHitsRootNode khrn) { + public AbstractAction visit(KeywordHits.RootNode khrn) { return openChild(khrn); } @Override - public AbstractAction visit(HashsetHitsRootNode hhrn) { + public AbstractAction visit(HashsetHits.RootNode hhrn) { return openChild(hhrn); } @Override - public AbstractAction visit(HashsetHitsSetNode hhsn) { + public AbstractAction visit(HashsetNameNode hhsn) { return openChild(hhsn); } @Override - public AbstractAction visit(InterestingHitsRootNode iarn) { + public AbstractAction visit(InterestingHits.RootNode iarn) { return openChild(iarn); } @Override - public AbstractAction visit(InterestingHitsSetNode iasn) { + public AbstractAction visit(InterestingHits.SetNameNode iasn) { return openChild(iasn); } @Override - public AbstractAction visit(EmailExtractedRootNode eern) { + public AbstractAction visit(EmailExtracted.RootNode eern) { return openChild(eern); } @Override - public AbstractAction visit(EmailExtractedAccountNode eean) { + public AbstractAction visit(AccountNode eean) { return openChild(eean); } @Override - public AbstractAction visit(EmailExtractedFolderNode eefn) { + public AbstractAction visit(FolderNode eefn) { return openChild(eefn); } @@ -443,7 +441,7 @@ public class DataResultFilterNode extends FilterNode { } @Override - public AbstractAction visit(ArtifactTypeNode atn) { + public AbstractAction visit(TypeNode atn) { return openChild(atn); } @@ -516,12 +514,12 @@ public class DataResultFilterNode extends FilterNode { } @Override - public AbstractAction visit(KeywordHitsListNode khsn) { + public AbstractAction visit(ListNode khsn) { return openChild(khsn); } @Override - public AbstractAction visit(KeywordHitsKeywordNode khmln) { + public AbstractAction visit(TermNode khmln) { return openChild(khmln); } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index a03933abf6..f4ffbd3ee3 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -53,7 +53,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.BlackboardResultViewer; import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent; import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; -import org.sleuthkit.autopsy.datamodel.ExtractedContentNode; +import org.sleuthkit.autopsy.datamodel.ExtractedContent.RootNode; import org.sleuthkit.autopsy.datamodel.DataSources; import org.sleuthkit.autopsy.datamodel.DataSourcesNode; import org.sleuthkit.autopsy.datamodel.KeywordHits; @@ -73,6 +73,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.datamodel.ExtractedContent; /** * Top component which displays something. @@ -387,7 +388,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat Children resultsChilds = results.getChildren(); tree.expandNode(resultsChilds.findChild(KeywordHits.NAME)); - tree.expandNode(resultsChilds.findChild(ExtractedContentNode.NAME)); + tree.expandNode(resultsChilds.findChild(ExtractedContent.NAME)); Node views = childNodes.findChild(ViewsNode.NAME); @@ -587,7 +588,8 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat // change in node selection else if (changed.equals(ExplorerManager.PROP_SELECTED_NODES)) { respondSelection((Node[]) oldValue, (Node[]) newValue); - } else if (changed.equals(IngestEvent.DATA.toString())) { + } + else if (changed.equals(IngestEvent.DATA.toString())) { final ModuleDataEvent event = (ModuleDataEvent) oldValue; if (event.getArtifactType() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GEN_INFO) { return; @@ -595,7 +597,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - refreshTree(event.getArtifactType()); + // @@@ refreshResultsTree(event.getArtifactType()); } }); } else if (changed.equals(IngestEvent.INGEST_JOB_COMPLETED.toString()) @@ -603,15 +605,15 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - refreshContentTree(); - refreshTree(); + refreshDataSourceTree(); + refreshResultsTree(); } }); } else if (changed.equals(IngestEvent.CONTENT_CHANGED.toString())) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - refreshContentTree(); + refreshDataSourceTree(); } }); } @@ -768,7 +770,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - refreshContentTree(); + refreshDataSourceTree(); } }); } @@ -776,7 +778,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat /** * Refreshes changed content nodes */ - void refreshContentTree() { + private void refreshDataSourceTree() { Node selectedNode = getSelectedNode(); final String[] selectedPath = NodeOp.createPath(selectedNode, em.getRootContext()); @@ -809,7 +811,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat * Refreshes the nodes in the tree to reflect updates in the database should * be called in the gui thread */ - public void refreshTree(final BlackboardArtifact.ARTIFACT_TYPE... types) { + public void refreshResultsTree(final BlackboardArtifact.ARTIFACT_TYPE... types) { //save current selection Node selectedNode = getSelectedNode(); final String[] selectedPath = NodeOp.createPath(selectedNode, em.getRootContext()); @@ -820,44 +822,40 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat Children dirChilds = em.getRootContext().getChildren(); Node results = dirChilds.findChild(ResultsNode.NAME); - if (results == null) { logger.log(Level.SEVERE, "Cannot find Results filter node, won't refresh the bb tree"); //NON-NLS return; } + OriginalNode original = results.getLookup().lookup(OriginalNode.class); ResultsNode resultsNode = (ResultsNode) original.getNode(); RootContentChildren resultsNodeChilds = (RootContentChildren) resultsNode.getChildren(); resultsNodeChilds.refreshKeys(types); + final TreeView tree = getTree(); - - tree.expandNode(results); + // @@@ tree.expandNode(results); Children resultsChilds = results.getChildren(); - - if (resultsChilds == null) //intermediate state check - { + if (resultsChilds == null) { return; } Node childNode = resultsChilds.findChild(KeywordHits.NAME); - if (childNode == null) //intermediate state check - { + if (childNode == null) { return; } - tree.expandNode(childNode); + // @@@tree.expandNode(childNode); - childNode = resultsChilds.findChild(ExtractedContentNode.NAME); - if (childNode == null) //intermediate state check - { + childNode = resultsChilds.findChild(ExtractedContent.NAME); + if (childNode == null) { return; } tree.expandNode(childNode); //restores selection if it was under the Results node - setSelectedNode(selectedPath, ResultsNode.NAME); - + //@@@ setSelectedNode(selectedPath, ResultsNode.NAME); + } /** @@ -983,7 +981,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat logger.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS } } else { - Node extractedContent = resultsChilds.findChild(ExtractedContentNode.NAME); + Node extractedContent = resultsChilds.findChild(ExtractedContent.NAME); Children extractedChilds = extractedContent.getChildren(); treeNode = extractedChilds.findChild(type.getLabel()); } diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index 28783ec7c0..76c162a96e 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Tue, 22 Apr 2014 16:06:14 -0400 +#Sat, 03 May 2014 22:45:39 -0400 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=288 SPLASH_WIDTH=538 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index fe9124784d..8d4a5daefb 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Tue, 22 Apr 2014 16:06:14 -0400 +#Sat, 03 May 2014 22:45:39 -0400 CTL_MainWindow_Title=Autopsy 3.1.0_Beta CTL_MainWindow_Title_No_Project=Autopsy 3.1.0_Beta