19 package org.sleuthkit.autopsy.keywordsearch;
21 import com.google.common.collect.SetMultimap;
22 import com.google.common.collect.TreeMultimap;
23 import java.awt.EventQueue;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.Comparator;
28 import java.util.HashMap;
29 import java.util.LinkedHashMap;
30 import java.util.List;
32 import java.util.concurrent.CancellationException;
33 import java.util.concurrent.ExecutionException;
34 import java.util.logging.Level;
35 import java.util.stream.Collectors;
36 import java.util.stream.Stream;
37 import javax.swing.SwingWorker;
38 import org.netbeans.api.progress.ProgressHandle;
39 import org.openide.nodes.ChildFactory;
40 import org.openide.nodes.Children;
41 import org.openide.nodes.Node;
42 import org.openide.util.NbBundle;
43 import org.openide.util.lookup.Lookups;
67 class KeywordSearchResultFactory
extends ChildFactory<KeyValueQueryContent> {
69 private static final Logger logger = Logger.getLogger(KeywordSearchResultFactory.class.getName());
72 static final List<String> COMMON_PROPERTIES
78 .map(BlackboardAttribute.ATTRIBUTE_TYPE::getDisplayName),
79 Arrays.stream(AbstractAbstractFileNode.AbstractFilePropertyType.values())
80 .map(Object::toString))
81 .collect(Collectors.toList());
83 private final Collection<QueryRequest> queryRequests;
85 KeywordSearchResultFactory(Collection<QueryRequest> queryRequests) {
86 this.queryRequests = queryRequests;
97 protected boolean createKeys(List<KeyValueQueryContent> toPopulate) {
99 for (QueryRequest queryRequest : queryRequests) {
103 if (!queryRequest.getQuery().validate()) {
109 Map<String, Object> map = queryRequest.getProperties();
115 COMMON_PROPERTIES.stream()
116 .forEach((propertyType) -> map.put(propertyType,
""));
117 map.put(TSK_KEYWORD.getDisplayName(), queryRequest.getQueryString());
118 map.put(TSK_KEYWORD_REGEXP.getDisplayName(), !queryRequest.getQuery().isLiteral());
120 createFlatKeys(queryRequest.getQuery(), toPopulate);
133 @NbBundle.Messages({
"KeywordSearchResultFactory.query.exception.msg=Could not perform the query "})
134 private boolean createFlatKeys(KeywordSearchQuery queryRequest, List<KeyValueQueryContent> toPopulate) {
139 QueryResults queryResults;
141 queryResults = queryRequest.performQuery();
142 }
catch (KeywordSearchModuleException | NoOpenCoreException ex) {
143 logger.log(Level.SEVERE,
"Could not perform the query " + queryRequest.getQueryString(), ex);
144 MessageNotifyUtil.Notify.error(Bundle.KeywordSearchResultFactory_query_exception_msg() + queryRequest.getQueryString(), ex.getCause().getMessage());
149 List<KeyValueQueryContent> tempList =
new ArrayList<>();
150 for (KeywordHit hit : getOneHitPerObject(queryResults)) {
155 Map<String, Object> properties =
new LinkedHashMap<>();
156 Content content = hit.getContent();
157 String contentName = content.
getName();
158 if (content instanceof AbstractFile) {
159 AbstractFsContentNode.fillPropertyMap(properties, (AbstractFile) content);
161 properties.put(LOCATION.toString(), contentName);
167 if (hit.hasSnippet()) {
168 properties.put(TSK_KEYWORD_PREVIEW.getDisplayName(), hit.getSnippet());
171 String hitName = hit.isArtifactHit()
172 ? hit.getArtifact().getDisplayName() +
" Artifact"
176 tempList.add(
new KeyValueQueryContent(hitName, properties, hitNumber, hit.getSolrObjectId(), content, queryRequest, queryResults));
181 toPopulate.addAll(tempList);
187 new BlackboardResultWriter(queryResults, queryRequest.getKeywordList().getName()).execute();
201 Collection<KeywordHit> getOneHitPerObject(QueryResults queryResults) {
202 HashMap<Long, KeywordHit> hits =
new HashMap<>();
203 for (Keyword keyWord : queryResults.getKeywords()) {
204 for (KeywordHit hit : queryResults.getResults(keyWord)) {
206 if (!hits.containsKey(hit.getSolrObjectId())) {
207 hits.put(hit.getSolrObjectId(), hit);
208 }
else if (hit.getChunkId() < hits.get(hit.getSolrObjectId()).getChunkId()) {
209 hits.put(hit.getSolrObjectId(), hit);
213 return hits.values();
217 protected Node createNodeForKey(KeyValueQueryContent key) {
218 final Content content = key.getContent();
219 QueryResults hits = key.getHits();
221 Node kvNode =
new KeyValueNode(key, Children.LEAF, Lookups.singleton(content));
224 return new KeywordSearchFilterNode(hits, kvNode);
231 class KeyValueQueryContent
extends KeyValue {
233 private final long solrObjectId;
235 private final Content content;
236 private final QueryResults hits;
237 private final KeywordSearchQuery query;
251 KeyValueQueryContent(String name, Map<String, Object> map,
int id,
long solrObjectId, Content content, KeywordSearchQuery query, QueryResults hits) {
252 super(name, map,
id);
253 this.solrObjectId = solrObjectId;
254 this.content = content;
260 Content getContent() {
264 long getSolrObjectId() {
268 QueryResults getHits() {
272 KeywordSearchQuery getQuery() {
281 static class BlackboardResultWriter
extends SwingWorker<Object, Void> {
283 private static final List<BlackboardResultWriter> writers =
new ArrayList<>();
284 private ProgressHandle progress;
285 private final KeywordSearchQuery query;
286 private final QueryResults hits;
287 private Collection<BlackboardArtifact> newArtifacts =
new ArrayList<>();
288 private static final int QUERY_DISPLAY_LEN = 40;
290 BlackboardResultWriter(QueryResults hits, String listName) {
292 this.query = hits.getQuery();
295 protected void finalizeWorker() {
296 deregisterWriter(
this);
297 EventQueue.invokeLater(progress::finish);
301 protected Object doInBackground() throws Exception {
302 registerWriter(
this);
303 final String queryStr = query.getQueryString();
304 final String queryDisp = queryStr.length() > QUERY_DISPLAY_LEN ? queryStr.substring(0, QUERY_DISPLAY_LEN - 1) +
" ..." : queryStr;
306 progress = ProgressHandle.createHandle(NbBundle.getMessage(
this.getClass(),
"KeywordSearchResultFactory.progress.saving", queryDisp), () -> BlackboardResultWriter.this.cancel(
true));
307 newArtifacts = hits.writeAllHitsToBlackBoard(progress, null,
this,
false);
315 protected void done() {
318 }
catch (InterruptedException | CancellationException ex) {
319 logger.log(Level.WARNING,
"User cancelled writing of ad hoc search query results for '{0}' to the blackboard", query.getQueryString());
320 }
catch (ExecutionException ex) {
321 logger.log(Level.SEVERE,
"Error writing of ad hoc search query results for " + query.getQueryString() +
" to the blackboard", ex);
325 private static synchronized void registerWriter(BlackboardResultWriter writer) {
329 private static synchronized void deregisterWriter(BlackboardResultWriter writer) {
330 writers.remove(writer);
333 static synchronized void stopAllWriters() {
334 for (BlackboardResultWriter w : writers) {