Merge pull request #2869 from millmanorama/2706-regex-hit-count

2706 regex hit count
This commit is contained in:
Richard Cordovano 2017-06-22 11:58:46 -04:00 committed by GitHub
commit 7394ac1244

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2016 Basis Technology Corp.
* Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -33,6 +33,8 @@ import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
@ -74,12 +76,12 @@ public class KeywordHits implements AutopsyVisitableItem {
keywordResults = new KeywordResults();
}
/* All of these maps and code assume the following:
* Regexps will have an 'instance' layer that shows the specific words that matched the regexp
* Exact match and substring will not have the instance layer and instead will have the specific hits
* below their term.
/*
* All of these maps and code assume the following: Regexps will have an
* 'instance' layer that shows the specific words that matched the regexp
* Exact match and substring will not have the instance layer and instead
* will have the specific hits below their term.
*/
private final class KeywordResults extends Observable {
// Map from listName/Type to Map of keywords/regexp to Map of instance terms to Set of artifact Ids
@ -104,10 +106,11 @@ public class KeywordHits implements AutopsyVisitableItem {
}
/**
* Get keywords used in a given list. Will be regexp patterns for regexps
* and search term for non-regexps.
* Get keywords used in a given list. Will be regexp patterns for
* regexps and search term for non-regexps.
*
* @param listName Keyword list name
*
* @return
*/
List<String> getKeywords(String listName) {
@ -120,12 +123,13 @@ public class KeywordHits implements AutopsyVisitableItem {
}
/**
* Get specific keyword terms that were found for a given list
* and keyword combination. For example, a specific phone number for a
* phone number regexp. Will be the default instance for non-regexp searches.
* Get specific keyword terms that were found for a given list and
* keyword combination. For example, a specific phone number for a phone
* number regexp. Will be the default instance for non-regexp searches.
*
* @param listName Keyword list name
* @param keyword search term (regexp pattern or exact match term)
*
* @return
*/
List<String> getKeywordInstances(String listName, String keyword) {
@ -139,9 +143,13 @@ public class KeywordHits implements AutopsyVisitableItem {
/**
* Get artifact ids for a given list, keyword, and instance triple
*
* @param listName Keyword list name
* @param keyword search term (regexp pattern or exact match term)
* @param keywordInstance specific term that matched (or default instance name)
* @param keyword search term (regexp pattern or exact match
* term)
* @param keywordInstance specific term that matched (or default
* instance name)
*
* @return
*/
Set<Long> getArtifactIds(String listName, String keyword, String keywordInstance) {
@ -152,7 +160,9 @@ public class KeywordHits implements AutopsyVisitableItem {
/**
* Add a hit for a regexp to the internal data structure.
* @param listMap Maps keywords/regexp to instances to artifact IDs
*
* @param listMap Maps keywords/regexp to instances to artifact
* IDs
* @param regExp Regular expression that was used in search
* @param keywordInstance Specific term that matched regexp
* @param artifactId Artifact id of file that had hit
@ -172,9 +182,10 @@ public class KeywordHits implements AutopsyVisitableItem {
instanceMap.get(keywordInstance).add(artifactId);
}
/**
* Add a hit for a exactmatch (or substring) to the internal data structure.
* Add a hit for a exactmatch (or substring) to the internal data
* structure.
*
* @param listMap Maps keywords/regexp to instances to artifact IDs
* @param keyWord Term that was hit
* @param artifactId Artifact id of file that had hit
@ -193,8 +204,11 @@ public class KeywordHits implements AutopsyVisitableItem {
}
/**
* Populate data structure for the tree based on the keyword hit artifacts
* @param artifactIds Maps Artifact ID to map of attribute types to attribute values
* Populate data structure for the tree based on the keyword hit
* artifacts
*
* @param artifactIds Maps Artifact ID to map of attribute types to
* attribute values
*/
void populateTreeMaps(Map<Long, Map<Long, String>> artifactIds) {
synchronized (topLevelMap) {
@ -241,8 +255,7 @@ public class KeywordHits implements AutopsyVisitableItem {
} else {
addNonRegExpMatchToList(listMap, word, id);
}
}
else if (reg != null) {
} else if (reg != null) {
addRegExpToList(listMap, reg, word, id);
} else {
addNonRegExpMatchToList(listMap, word, id);
@ -299,9 +312,9 @@ public class KeywordHits implements AutopsyVisitableItem {
long artifactId = resultSet.getLong("artifact_id"); //NON-NLS
long typeId = resultSet.getLong("attribute_type_id"); //NON-NLS
if (!artifactIds.containsKey(artifactId)) {
artifactIds.put(artifactId, new LinkedHashMap<Long, String>());
artifactIds.put(artifactId, new LinkedHashMap<>());
}
if (valueStr != null && !valueStr.equals("")) {
if (StringUtils.isNotEmpty(valueStr)) {
artifactIds.get(artifactId).put(typeId, valueStr);
} else {
// Keyword Search Type is an int
@ -459,7 +472,8 @@ public class KeywordHits implements AutopsyVisitableItem {
}
/**
* Represents the keyword search lists (or default groupings if list was not given)
* Represents the keyword search lists (or default groupings if list was not
* given)
*/
public class ListNode extends DisplayableItemNode implements Observer {
@ -583,17 +597,17 @@ public class KeywordHits implements AutopsyVisitableItem {
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
updateDisplayName();
keywordResults.addObserver(this);
}
private void updateDisplayName() {
int totalDescendants = 0;
for (String instance : keywordResults.getKeywordInstances(setName, keyword)) {
Set<Long> ids = keywordResults.getArtifactIds(setName, keyword, instance);
totalDescendants += ids.size();
super.setDisplayName(keyword + " (" + countTotalDescendants() + ")");
}
super.setDisplayName(keyword + " (" + totalDescendants + ")");
private int countTotalDescendants() {
return keywordResults.getKeywordInstances(setName, keyword).stream()
.mapToInt(instance -> keywordResults.getArtifactIds(setName, keyword, instance).size())
.sum();
}
@Override
@ -605,12 +619,7 @@ public class KeywordHits implements AutopsyVisitableItem {
public boolean isLeafTypeNode() {
List<String> instances = keywordResults.getKeywordInstances(setName, keyword);
// is this an exact/substring match (i.e. did we use the DEFAULT name)?
if (instances.size() == 1 && instances.get(0).equals(DEFAULT_INSTANCE_NAME)) {
return true;
}
else {
return false;
}
return instances.size() == 1 && instances.get(0).equals(DEFAULT_INSTANCE_NAME);
}
@Override
@ -635,7 +644,7 @@ 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"),
keywordResults.getKeywordInstances(setName, keyword).size()));
countTotalDescendants()));
return s;
}
@ -650,37 +659,45 @@ public class KeywordHits implements AutopsyVisitableItem {
// as they keys for different types of nodes at the
// same level. Probably a better way to do this, but
// it works.
class RegExpInstanceKey {
private class RegExpInstanceKey {
private final boolean isRegExp;
private String strKey;
private Long longKey;
public RegExpInstanceKey(String key) {
RegExpInstanceKey(String key) {
isRegExp = true;
strKey = key;
}
public RegExpInstanceKey(Long key) {
RegExpInstanceKey(Long key) {
isRegExp = false;
longKey = key;
}
boolean isRegExp() {
return isRegExp;
}
Long getIdKey() {
return longKey;
}
String getRegExpKey() {
return strKey;
}
}
/**
* Creates the nodes for a given regexp that represent the specific terms that were found
* Creates the nodes for a given regexp that represent the specific terms
* that were found
*/
public class RegExpInstancesFactory extends ChildFactory.Detachable<RegExpInstanceKey> implements Observer {
private final String keyword;
private final String setName;
private Map<RegExpInstanceKey, DisplayableItemNode > nodesMap = new HashMap<>();
private final Map<RegExpInstanceKey, DisplayableItemNode> nodesMap = new HashMap<>();
public RegExpInstancesFactory(String setName, String keyword) {
super();
@ -700,12 +717,12 @@ public class KeywordHits implements AutopsyVisitableItem {
@Override
protected boolean createKeys(List<RegExpInstanceKey> list) {
List <String>instances = keywordResults.getKeywordInstances(setName, keyword);
List<String> instances = keywordResults.getKeywordInstances(setName, keyword);
// The keys are different depending on what we are displaying.
// regexp get another layer to show instances.
// Exact/substring matches don't.
if ((instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME))) {
for (Long id : keywordResults.getArtifactIds(setName, keyword, DEFAULT_INSTANCE_NAME) ) {
for (Long id : keywordResults.getArtifactIds(setName, keyword, DEFAULT_INSTANCE_NAME)) {
RegExpInstanceKey key = new RegExpInstanceKey(id);
if (!nodesMap.containsKey(key)) {
nodesMap.put(key, createNode(key));
@ -739,6 +756,7 @@ public class KeywordHits implements AutopsyVisitableItem {
}
}
@Override
public void update(Observable o, Object arg) {
refresh(true);
@ -756,7 +774,7 @@ public class KeywordHits implements AutopsyVisitableItem {
private final String instance;
public RegExpInstanceNode(String setName, String keyword, String instance) {
super(Children.create(new HitsFactory(setName, keyword, instance), true), Lookups.singleton(keyword));
super(Children.create(new HitsFactory(setName, keyword, instance), true), Lookups.singleton(instance));
super.setName(instance); //the instance represents the name of the keyword hit at this point as the keyword is the regex
this.setName = setName;
this.keyword = keyword;
@ -803,7 +821,7 @@ 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"),
keywordResults.getKeywordInstances(setName, keyword).size()));
keywordResults.getArtifactIds(setName, keyword, instance).size()));
return s;
}
@ -816,10 +834,12 @@ public class KeywordHits implements AutopsyVisitableItem {
/**
* Create a blackboard node for the given Keyword Hit artifact
*
* @param artifactId
*
* @return Node or null on error
*/
private BlackboardArtifactNode createBlackboardArtifactNode (Long artifactId) {
private BlackboardArtifactNode createBlackboardArtifactNode(Long artifactId) {
if (skCase == null) {
return null;
}
@ -879,7 +899,7 @@ public class KeywordHits implements AutopsyVisitableItem {
private final String setName;
private final String instance;
private Map<Long, BlackboardArtifactNode > nodesMap = new HashMap<>();
private final Map<Long, BlackboardArtifactNode> nodesMap = new HashMap<>();
public HitsFactory(String setName, String keyword, String instance) {
super();
@ -900,7 +920,7 @@ public class KeywordHits implements AutopsyVisitableItem {
@Override
protected boolean createKeys(List<Long> list) {
for (Long id : keywordResults.getArtifactIds(setName, keyword, instance) ) {
for (Long id : keywordResults.getArtifactIds(setName, keyword, instance)) {
if (!nodesMap.containsKey(id)) {
nodesMap.put(id, createBlackboardArtifactNode(id));
}