fetches all relevant configurations

This commit is contained in:
Greg DiCristofaro 2022-01-26 12:30:40 -05:00
parent 0d9c9c4c28
commit afe8d9c5e7
8 changed files with 234 additions and 60 deletions

View File

@ -468,7 +468,7 @@ public final class DataResultTopComponent extends TopComponent implements DataRe
* Displays results of querying the DAO for an artifact type and set name.
* @param params The search parameters.
*/
public void displayAnalysisResultSet(AnalysisResultConfigSearchParam params) {
public void displayAnalysisResultConfig(AnalysisResultConfigSearchParam params) {
dataResultPanel.displayAnalysisResultSet(params);
}

View File

@ -659,6 +659,7 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
String query = "res.search_term,\n"
+ " res.search_type,\n"
+ " MIN(res.configuration) AS configuration,\n"
+ " SUM(res.count) AS count,\n"
+ " -- when there are multiple keyword groupings, return true for has children\n"
+ " CASE\n"
@ -681,6 +682,7 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
+ " FROM (\n"
+ " -- get pertinent attribute values for artifacts\n"
+ " SELECT art.artifact_id, \n"
+ " ar.configuration,\n"
+ " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = "
+ BlackboardAttribute.Type.TSK_SET_NAME.getTypeID() + " LIMIT 1) AS set_name,\n"
+ " (SELECT value_int32 FROM blackboard_attributes attr WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = "
@ -690,6 +692,7 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
+ " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = "
+ BlackboardAttribute.Type.TSK_KEYWORD.getTypeID() + " LIMIT 1) AS keyword\n"
+ " FROM blackboard_artifacts art\n"
+ " LEFT JOIN tsk_analysis_results ar ON ar.artifact_obj_id = art.artifact_obj_id\n"
+ " WHERE art.artifact_type_id = " + BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID() + "\n"
+ dataSourceClause
+ " ) attr_res\n"
@ -719,6 +722,8 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
int searchType = resultSet.getInt("search_type");
long count = resultSet.getLong("count");
boolean hasChildren = resultSet.getBoolean("has_children");
// only a unique applicable configuration if no child tree nodes
String configuration = hasChildren ? null : resultSet.getString("configuration");
TskData.KeywordSearchQueryType searchTypeEnum
= Stream.of(TskData.KeywordSearchQueryType.values())
@ -734,7 +739,7 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
TreeItemDTO<KeywordSearchTermParams> treeItem = new TreeItemDTO<>(
KeywordSearchTermParams.getTypeId(),
new KeywordSearchTermParams(setName, searchTerm, TskData.KeywordSearchQueryType.valueOf(searchType), hasChildren, dataSourceId),
new KeywordSearchTermParams(setName, searchTerm, TskData.KeywordSearchQueryType.valueOf(searchType), configuration, hasChildren, dataSourceId),
searchTermModified,
searchTermModified,
displayCount
@ -810,9 +815,11 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
: "res.set_name = ?";
String query = "keyword, \n"
+ " MIN(configuration) AS configuration,\n"
+ " COUNT(*) AS count \n"
+ "FROM (\n"
+ " SELECT art.artifact_id, \n"
+ " ar.configuration,"
+ " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = "
+ BlackboardAttribute.Type.TSK_SET_NAME.getTypeID() + " LIMIT 1) AS set_name,\n"
+ " (SELECT value_int32 FROM blackboard_attributes attr WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = "
@ -822,6 +829,7 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
+ " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = "
+ BlackboardAttribute.Type.TSK_KEYWORD.getTypeID() + " LIMIT 1) AS keyword\n"
+ " FROM blackboard_artifacts art\n"
+ " LEFT JOIN tsk_analysis_results ar ON art.artifact_obj_id = ar.artifact_obj_id\n"
+ " WHERE art.artifact_type_id = " + BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID() + "\n"
+ dataSourceClause
+ ") res\n"
@ -867,13 +875,14 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
try {
while (resultSet.next()) {
String keyword = resultSet.getString("keyword");
String configuration = resultSet.getString("configuration");
long count = resultSet.getLong("count");
TreeDisplayCount displayCount = indeterminateMatches.contains(keyword)
? TreeDisplayCount.INDETERMINATE
: TreeDisplayCount.getDeterminate(count);
items.add(createKWHitsTreeItem(dataSourceId, setName, keyword, regexStr, searchType, displayCount));
items.add(createKWHitsTreeItem(dataSourceId, setName, keyword, regexStr, searchType, configuration, displayCount));
}
} catch (SQLException ex) {
logger.log(Level.WARNING, "An error occurred while fetching results from result set.", ex);
@ -888,11 +897,11 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
private static TreeItemDTO<KeywordHitSearchParam> createKWHitsTreeItem(
Long dataSourceId, String setName, String keyword, String regexStr,
TskData.KeywordSearchQueryType searchType, TreeDisplayCount displayCount) {
TskData.KeywordSearchQueryType searchType, String configuration, TreeDisplayCount displayCount) {
return new TreeItemDTO<>(
KeywordHitSearchParam.getTypeId(),
new KeywordHitSearchParam(dataSourceId, setName, keyword, regexStr, searchType),
new KeywordHitSearchParam(dataSourceId, setName, keyword, regexStr, searchType, configuration),
keyword == null ? "" : keyword,
keyword == null ? "" : keyword,
displayCount
@ -943,8 +952,10 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
}
}
String configuration = (art instanceof AnalysisResult) ? ((AnalysisResult) art).getConfiguration() : null;
// data source id is null for KeywordHitSearchParam so that key lookups can be done without data source id.
return Pair.of(new KeywordHitSearchParam(null, setName, keywordMatch, searchTerm, searchType), dataSourceId);
return Pair.of(new KeywordHitSearchParam(null, setName, keywordMatch, searchTerm, searchType, configuration), dataSourceId);
}
@Override
@ -997,7 +1008,7 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
SubDAOUtils.invalidateKeys(this.configHitCache, ar -> Pair.of(Pair.of(ar.getArtifactType(), ar.getConfiguration()), ar.getDataSourceId()), configMap);
SubDAOUtils.invalidateKeys(this.keywordHitCache, kw -> Pair.of(
// null data source for lookup
new KeywordHitSearchParam(null, kw.getConfiguration(), kw.getKeyword(), kw.getRegex(), kw.getSearchType()),
new KeywordHitSearchParam(null, kw.getSetName(), kw.getKeyword(), kw.getRegex(), kw.getSearchType(), kw.getConfiguration()),
kw.getDataSourceId()
), keywordHitsMap);
@ -1031,11 +1042,11 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
Map<Boolean, List<KeywordHitEvent>> keywordHitEvts = keywordHitsMap.entrySet().stream()
.flatMap(entry -> {
KeywordHitSearchParam params = entry.getKey();
String setName = params.getConfiguration();
String setName = params.getSetName();
String searchString = params.getRegex();
TskData.KeywordSearchQueryType queryType = params.getSearchType();
String match = params.getKeyword();
return entry.getValue().stream().map(dsId -> new KeywordHitEvent(setName, searchString, queryType, match, dsId));
return entry.getValue().stream().map(dsId -> new KeywordHitEvent(setName, searchString, queryType, match, params.getConfiguration(), dsId));
})
.collect(Collectors.partitioningBy(kwe -> kwe.getSetName() == null));
@ -1090,6 +1101,7 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
khEvt.getMatch(),
khEvt.getSearchString(),
khEvt.getSearchType(),
khEvt.getConfiguration(),
displayCount
);
} else if (arEvt instanceof AnalysisResultConfigEvent) {
@ -1138,6 +1150,102 @@ public class AnalysisResultDAO extends BlackboardArtifactDAO {
}
/**
* Returns all the configurations for keyword hits for the given filtering parameters.
* @param setName The set name as defined by TSK_SET_NAME. If null, assumed to be ad hoc result.
* @param regex The TSK_KEYWORD_REGEXP value. If null, no filtering by regex occurs.
* @param searchType The TSK_KEYWORD_SEARCH_TYPE value. If null, no filtering by search type occurs.
* @param dataSourceId The data source object id. If null, no filtering by data source occurs.
* @return The distinct configurations.
* @throws ExecutionException
*/
public List<String> getKeywordHitConfigurations(String setName, String regex, TskData.KeywordSearchQueryType searchType, Long dataSourceId) throws ExecutionException {
String setNameClause = setName == null
// if set name is null, then there should be no set name attribute associated with this
? "(SELECT "
+ " COUNT(*) FROM blackboard_attributes attr "
+ " WHERE attr.artifact_id = art.artifact_id "
+ " AND attr.attribute_type_id = " + BlackboardAttribute.Type.TSK_SET_NAME.getTypeID()
+ " AND attr.value_text IS NOT NULL "
+ " AND LEN(attr.value_text) > 0) = 0"
// otherwise, see if the set name attribute matches expected value
: "? IN (SELECT attr.value_text FROM blackboard_attributes attr "
+ " WHERE attr.artifact_id = art.artifact_id "
+ " AND attr.attribute_type_id = " + BlackboardAttribute.Type.TSK_SET_NAME.getTypeID()
+ " )";
String regexClause = regex == null
? null
: "? IN (SELECT attr.value_text FROM blackboard_attributes attr "
+ " WHERE attr.artifact_id = art.artifact_id "
+ " AND attr.attribute_type_id = " + BlackboardAttribute.Type.TSK_KEYWORD_REGEXP.getTypeID()
+ " )";
String searchTypeClause = searchType == null
? null
: "? IN (SELECT attr.value_int32 FROM blackboard_attributes attr "
+ " WHERE attr.artifact_id = art.artifact_id "
+ " AND attr.attribute_type_id = " + BlackboardAttribute.Type.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
+ " )";
String dataSourceClause = dataSourceId == null
? null
: "art.data_source_obj_id = ?";
String clauses = Stream.of(setNameClause, regexClause, searchTypeClause, dataSourceClause)
.filter(s -> s != null)
.map(s -> " (" + s + ") ")
.collect(Collectors.joining("AND"));
String query = "DISTINCT(ar.configuration) AS configuration \n"
+ "FROM tsk_analysis_results ar\n"
+ "LEFT JOIN blackboard_artifacts"
+ "WHERE " + clauses;
// get artifact types and counts
try (CaseDbPreparedStatement preparedStatement = getCase().getCaseDbAccessManager().prepareSelect(query)) {
int paramIdx = 0;
if (setName != null) {
preparedStatement.setString(++paramIdx, setName);
}
if (regex != null) {
preparedStatement.setString(++paramIdx, regex);
}
if (searchType != null) {
preparedStatement.setInt(++paramIdx, searchType.getType());
}
if (dataSourceId != null) {
preparedStatement.setLong(++paramIdx, dataSourceId);
}
List<String> configurations = new ArrayList<>();
getCase().getCaseDbAccessManager().select(preparedStatement, (resultSet) -> {
try {
while (resultSet.next()) {
configurations.add(resultSet.getString("configuration"));
}
} catch (SQLException ex) {
logger.log(Level.WARNING, "An error occurred while fetching results from result set.", ex);
}
});
return configurations;
} catch (SQLException | NoCurrentCaseException | TskCoreException ex) {
throw new ExecutionException(MessageFormat.format(
"An error occurred while fetching configurations for counts where setName = {0} regex = {1} and search type = {2}",
setName == null ? "<null>" : setName,
regex == null ? "<null>" : regex,
searchType == null ? "<null>" : searchType.name()),
ex);
}
}
/**
* A tree item for an analysis result that can indicate if it has child tree
* nodes due to configuration.

View File

@ -40,8 +40,8 @@ public class KeywordHitSearchParam extends KeywordSearchTermParams {
private final String regex;
private final TskData.KeywordSearchQueryType searchType;
public KeywordHitSearchParam(Long dataSourceId, String setName, String keyword, String regex, TskData.KeywordSearchQueryType searchType) {
super(setName, regex, searchType, StringUtils.isNotBlank(keyword) && !Objects.equals(regex, keyword), dataSourceId);
public KeywordHitSearchParam(Long dataSourceId, String setName, String keyword, String regex, TskData.KeywordSearchQueryType searchType, String configuration) {
super(setName, regex, searchType, configuration, StringUtils.isNotBlank(keyword) && !Objects.equals(regex, keyword), dataSourceId);
this.keyword = keyword;
this.regex = regex;
this.searchType = searchType;

View File

@ -25,7 +25,7 @@ import org.sleuthkit.datamodel.TskData;
/**
* Parameters for a keyword search term.
*/
public class KeywordSearchTermParams extends AnalysisResultConfigSearchParam {
public class KeywordSearchTermParams extends AnalysisResultSetSearchParam {
private static final String TYPE_ID = "KEYWORD_SEARCH_TERMS";
@ -36,10 +36,10 @@ public class KeywordSearchTermParams extends AnalysisResultConfigSearchParam {
return TYPE_ID;
}
private final String searchTerm;
private final Boolean hasChildren;
private final TskData.KeywordSearchQueryType searchType;
private final String configuration;
/**
* Main constructor.
@ -47,21 +47,21 @@ public class KeywordSearchTermParams extends AnalysisResultConfigSearchParam {
* @param setName The set name.
* @param searchTerm The search term (determined from regex or keyword).
* @param searchType The keyword search type attribute.
* @param configuration The configuration of the analysis results set if
* hasChildren is false.
* @param hasChildren Whether or not this search term has children tree
* nodes (i.e. url regex search that further divides
* into different urls).
* @param dataSourceId The data source id or null.
*/
public KeywordSearchTermParams(String setName, String searchTerm, TskData.KeywordSearchQueryType searchType, boolean hasChildren, Long dataSourceId) {
public KeywordSearchTermParams(String setName, String searchTerm, TskData.KeywordSearchQueryType searchType, String configuration, boolean hasChildren, Long dataSourceId) {
super(BlackboardArtifact.Type.TSK_KEYWORD_HIT, dataSourceId, setName);
this.searchTerm = searchTerm;
this.hasChildren = hasChildren;
this.searchType = searchType;
this.configuration = configuration;
}
/**
* @return The search term (determined from regex or keyword).
*/
@ -76,6 +76,11 @@ public class KeywordSearchTermParams extends AnalysisResultConfigSearchParam {
public boolean hasChildren() {
return hasChildren;
}
public String getConfiguration() {
return configuration;
}
/**
* @return The keyword search type value.
*/
@ -113,5 +118,4 @@ public class KeywordSearchTermParams extends AnalysisResultConfigSearchParam {
return super.equals(obj);
}
}

View File

@ -32,6 +32,7 @@ public class KeywordHitEvent extends AnalysisResultEvent {
private final String match;
private final TskData.KeywordSearchQueryType searchType;
private final String setName;
private final String configuration;
/**
* Main constructor.
@ -40,14 +41,20 @@ public class KeywordHitEvent extends AnalysisResultEvent {
* @param searchString The search string or regex.
* @param searchType THe search type.
* @param match The match string.
* @param configuration The configuration of the analysis result.
* @param dataSourceId The data source id.
*/
public KeywordHitEvent(String setName, String searchString, TskData.KeywordSearchQueryType searchType, String match, long dataSourceId) {
public KeywordHitEvent(String setName, String searchString, TskData.KeywordSearchQueryType searchType, String match, String configuration, long dataSourceId) {
super(BlackboardArtifact.Type.TSK_KEYWORD_HIT, dataSourceId);
this.setName = setName;
this.searchString = searchString;
this.match = match;
this.searchType = searchType;
this.configuration = configuration;
}
public String getConfiguration() {
return configuration;
}
public String getSetName() {

View File

@ -18,17 +18,20 @@
*/
package org.sleuthkit.autopsy.mainui.nodes;
import java.util.Collections;
import org.sleuthkit.autopsy.mainui.datamodel.KeywordSearchTermParams;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.util.NbBundle.Messages;
import org.openide.util.NotImplementedException;
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.utils.IconsUtil;
import org.sleuthkit.autopsy.mainui.datamodel.AnalysisResultDAO;
import org.sleuthkit.autopsy.mainui.datamodel.AnalysisResultDAO.AnalysisResultTreeItem;
@ -237,7 +240,7 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
@Override
protected TreeNode<AnalysisResultConfigSearchParam> createNewNode(TreeResultsDTO.TreeItemDTO<? extends AnalysisResultConfigSearchParam> rowData) {
return new TreeSetTypeNode(rowData);
return new TreeConfigTypeNode(rowData);
}
@Override
@ -282,14 +285,14 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
/**
* A node for a set within an artifact type.
*/
static class TreeSetTypeNode extends TreeNode<AnalysisResultConfigSearchParam> {
static class TreeConfigTypeNode extends TreeNode<AnalysisResultConfigSearchParam> {
/**
* Main constructor.
*
* @param itemData The data to display.
*/
TreeSetTypeNode(TreeResultsDTO.TreeItemDTO<? extends AnalysisResultConfigSearchParam> itemData) {
TreeConfigTypeNode(TreeResultsDTO.TreeItemDTO<? extends AnalysisResultConfigSearchParam> itemData) {
super(itemData.getSearchParams().getArtifactType().getTypeName() + "_SET_" + itemData.getSearchParams().getConfiguration(),
getIconPath(itemData.getSearchParams().getArtifactType()),
itemData,
@ -299,7 +302,7 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
@Override
public void respondSelection(DataResultTopComponent dataResultPanel) {
dataResultPanel.displayAnalysisResultSet(this.getItemData().getSearchParams());
dataResultPanel.displayAnalysisResultConfig(this.getItemData().getSearchParams());
}
@Override
@ -313,8 +316,13 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
}
@Override
public Optional<String> getAnalysisResultConfiguration() {
return Optional.of(this.getItemData().getSearchParams().getConfiguration());
public boolean hasAnalysisResultConfigurations() {
return true;
}
@Override
public List<String> getAnalysisResultConfigurations() {
return Collections.singletonList(this.getItemData().getSearchParams().getConfiguration());
}
}
@ -370,6 +378,8 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
static class KeywordSetNode extends TreeNode<AnalysisResultSetSearchParam> {
private static final Logger logger = Logger.getLogger(KeywordSetNode.class.getName());
/**
* Main constructor.
*
@ -394,8 +404,20 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
}
@Override
public Optional<String> getAnalysisResultConfiguration() {
throw new NotImplementedException("TODO");
public boolean hasAnalysisResultConfigurations() {
return true;
}
@Override
public List<String> getAnalysisResultConfigurations() {
try {
return MainDAO.getInstance().getAnalysisResultDAO().getKeywordHitConfigurations(
this.getItemData().getSearchParams().getSetName(), null, null,
this.getItemData().getSearchParams().getDataSourceId());
} catch (ExecutionException ex) {
logger.log(Level.WARNING, "An exception occurred while fetching configurations.", ex);
return Collections.emptyList();
}
}
}
@ -445,6 +467,7 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
this.setParams.getSetName(),
searchParam.getRegex(),
searchParam.getSearchType(),
searchParam.getConfiguration(),
searchParam.hasChildren(),
this.setParams.getDataSourceId()
),
@ -480,6 +503,8 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
*/
static class KeywordSearchTermNode extends TreeNode<KeywordSearchTermParams> {
private static final Logger logger = Logger.getLogger(KeywordSearchTermNode.class.getName());
/**
* Main constructor.
*
@ -507,7 +532,8 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
TskData.KeywordSearchQueryType.LITERAL.equals(searchTermParams.getSearchType()) ? searchTermParams.getRegex() : null,
// if literal, no regex
TskData.KeywordSearchQueryType.LITERAL.equals(searchTermParams.getSearchType()) ? null : searchTermParams.getRegex(),
searchTermParams.getSearchType());
searchTermParams.getSearchType(),
searchTermParams.getConfiguration());
dataResultPanel.displayKeywordHits(searchParams);
} else {
super.respondSelection(dataResultPanel);
@ -525,8 +551,26 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
}
@Override
public Optional<String> getAnalysisResultConfiguration() {
return Optional.of(this.getItemData().getSearchParams().getConfiguration());
public boolean hasAnalysisResultConfigurations() {
return true;
}
@Override
public List<String> getAnalysisResultConfigurations() {
KeywordSearchTermParams searchParams = this.getItemData().getSearchParams();
if (searchParams.hasChildren()) {
try {
return MainDAO.getInstance().getAnalysisResultDAO().getKeywordHitConfigurations(
searchParams.getSetName(), searchParams.getRegex(), searchParams.getSearchType(),
searchParams.getDataSourceId());
} catch (ExecutionException ex) {
logger.log(Level.WARNING, "An exception occurred while fetching configurations.", ex);
return Collections.emptyList();
}
} else {
return Collections.singletonList(searchParams.getConfiguration());
}
}
}
@ -578,10 +622,11 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
KeywordHitSearchParam.getTypeId(),
new KeywordHitSearchParam(
this.searchTermParams.getDataSourceId(),
this.searchTermParams.getConfiguration(),
this.searchTermParams.getSetName(),
searchParam.getKeyword(),
this.searchTermParams.getRegex(),
this.searchTermParams.getSearchType()
this.searchTermParams.getSearchType(),
this.searchTermParams.getConfiguration()
),
searchParam.getKeyword() == null ? "" : searchParam.getKeyword(),
searchParam.getKeyword() == null ? "" : searchParam.getKeyword(),
@ -644,8 +689,13 @@ public class AnalysisResultTypeFactory extends TreeChildFactory<AnalysisResultSe
}
@Override
public Optional<String> getAnalysisResultConfiguration() {
return Optional.of(this.getItemData().getSearchParams().getConfiguration());
public boolean hasAnalysisResultConfigurations() {
return true;
}
@Override
public List<String> getAnalysisResultConfigurations() {
return Collections.singletonList(this.getItemData().getSearchParams().getConfiguration());
}
}

View File

@ -18,6 +18,8 @@
*/
package org.sleuthkit.autopsy.mainui.nodes.actions;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.mainui.nodes.actions.ActionsFactory.ActionGroup;
@ -243,14 +245,15 @@ public interface ActionContext {
}
default boolean hasAnalysisResultConfigurations() {
return false;
}
/**
* @return Provides the node configuration if applicable or empty. The
* optional may be null if other analysis results of the same
* artifact type have a configuration but these analysis results
* do not.
* @return Provides the node configurations if applicable or an empty list.
*/
default Optional<String> getAnalysisResultConfiguration() {
return Optional.empty();
default List<String> getAnalysisResultConfigurations() {
return Collections.emptyList();
}
/**

View File

@ -30,6 +30,7 @@ import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.swing.AbstractAction;
import javax.swing.Action;
@ -173,17 +174,18 @@ public final class ActionsFactory {
}
Optional<BlackboardArtifact.Type> analysisResultType = actionContext.getAnalysisResultType();
if (analysisResultType.isPresent()) {
Optional<String> configuration = actionContext.getAnalysisResultConfiguration();
if (analysisResultType.isPresent() && actionContext.hasAnalysisResultConfigurations()) {
Optional<Long> dataSourceId = actionContext.getDataSourceIdForActions();
actionGroups.add(new ActionGroup(new AbstractAction("Delete Analysis Results of Type") {
@Override
public void actionPerformed(ActionEvent e) {
List<String> configurations = actionContext.getAnalysisResultConfigurations();
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
MessageFormat.format("Stub Action for deleting analysis result type: {0} with configuration {1} and data source of {2}",
MessageFormat.format("Stub Action for deleting analysis result type: {0} with configurations [{1}] and data source of {2}",
analysisResultType.get().getDisplayName(),
configuration.map(c -> c == null ? "<Null>" : c).orElse("<Empty>"),
configurations.stream().map(c -> c == null ? "<Null>" : "\"" + c + "\"").collect(Collectors.joining(",")),
dataSourceId.map(d -> Long.toString(d)).orElse("<Null or Empty>")),
"Deleting...",
JOptionPane.WARNING_MESSAGE);