19 package org.sleuthkit.autopsy.keywordsearch;
 
   21 import java.awt.EventQueue;
 
   22 import java.util.ArrayList;
 
   23 import java.util.Arrays;
 
   24 import java.util.Collection;
 
   25 import java.util.HashMap;
 
   26 import java.util.LinkedHashMap;
 
   27 import java.util.List;
 
   29 import java.util.concurrent.CancellationException;
 
   30 import java.util.concurrent.ExecutionException;
 
   31 import java.util.logging.Level;
 
   32 import java.util.stream.Collectors;
 
   33 import java.util.stream.Stream;
 
   34 import javax.swing.SwingWorker;
 
   35 import org.netbeans.api.progress.ProgressHandle;
 
   36 import org.openide.nodes.ChildFactory;
 
   37 import org.openide.nodes.Children;
 
   38 import org.openide.nodes.Node;
 
   39 import org.openide.util.NbBundle;
 
   40 import org.openide.util.lookup.Lookups;
 
   55 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD;
 
   56 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW;
 
   57 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP;
 
   69 class AdHocSearchChildFactory 
extends ChildFactory<KeyValue> {
 
   71     private static final Logger logger = Logger.getLogger(AdHocSearchChildFactory.class.getName());
 
   74     static final List<String> COMMON_PROPERTIES
 
   80                             .map(BlackboardAttribute.ATTRIBUTE_TYPE::getDisplayName),
 
   81                     Arrays.stream(AbstractAbstractFileNode.AbstractFilePropertyType.values())
 
   82                             .map(Object::toString))
 
   83                     .collect(Collectors.toList());
 
   85     private final Collection<AdHocQueryRequest> queryRequests;
 
   86     private final boolean saveResults;
 
   94     AdHocSearchChildFactory(Collection<AdHocQueryRequest> queryRequests, 
boolean saveResults) {
 
   95         this.queryRequests = queryRequests;
 
   96         this.saveResults = saveResults;
 
  107     protected boolean createKeys(List<KeyValue> toPopulate) {
 
  109         for (AdHocQueryRequest queryRequest : queryRequests) {
 
  113             if (!queryRequest.getQuery().validate()) {
 
  119             Map<String, Object> map = queryRequest.getProperties();
 
  125             COMMON_PROPERTIES.stream()
 
  126                     .forEach((propertyType) -> map.put(propertyType, 
""));
 
  127             map.put(TSK_KEYWORD.getDisplayName(), queryRequest.getQueryString());
 
  128             map.put(TSK_KEYWORD_REGEXP.getDisplayName(), !queryRequest.getQuery().isLiteral());
 
  130             createFlatKeys(queryRequest.getQuery(), toPopulate);
 
  135         if (toPopulate.isEmpty()) {
 
  136             toPopulate.add(
new KeyValue(
"This KeyValue Is Empty", 0));
 
  149     @NbBundle.Messages({
"KeywordSearchResultFactory.query.exception.msg=Could not perform the query "})
 
  150     private boolean createFlatKeys(KeywordSearchQuery queryRequest, List<KeyValue> toPopulate) {
 
  155         QueryResults queryResults;
 
  157             queryResults = queryRequest.performQuery();
 
  158         } 
catch (KeywordSearchModuleException | NoOpenCoreException ex) {
 
  159             logger.log(Level.SEVERE, 
"Could not perform the query " + queryRequest.getQueryString(), ex); 
 
  160             MessageNotifyUtil.Notify.error(Bundle.KeywordSearchResultFactory_query_exception_msg() + queryRequest.getQueryString(), ex.getCause().getMessage());
 
  163         SleuthkitCase tskCase;
 
  165             tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
 
  166         } 
catch (NoCurrentCaseException ex) {
 
  167             logger.log(Level.SEVERE, 
"There was no case open.", ex); 
 
  172         List<KeywordHitKey> tempList = 
new ArrayList<>();
 
  173         for (KeywordHit hit : getOneHitPerObject(queryResults)) {
 
  178             Map<String, Object> properties = 
new LinkedHashMap<>();
 
  182                 content = tskCase.getContentById(hit.getContentID());
 
  183                 if (content == null) {
 
  184                     logger.log(Level.SEVERE, 
"There was a error getting content by id."); 
 
  187             } 
catch (TskCoreException ex) {
 
  188                 logger.log(Level.SEVERE, 
"There was a error getting content by id.", ex); 
 
  192             contentName = content.getName();
 
  193             if (content instanceof AbstractFile) {
 
  194                 AbstractFsContentNode.fillPropertyMap(properties, (AbstractFile) content);
 
  196                 properties.put(LOCATION.toString(), contentName);
 
  202             if (hit.hasSnippet()) {
 
  203                 properties.put(TSK_KEYWORD_PREVIEW.getDisplayName(), hit.getSnippet());
 
  207             BlackboardArtifact artifact = null;
 
  208             if (hit.isArtifactHit()) {
 
  210                     artifact = tskCase.getBlackboardArtifact(hit.getArtifactID().get());
 
  211                     hitName = artifact.getDisplayName() + 
" Artifact"; 
 
  212                 } 
catch (TskCoreException ex) {
 
  213                     logger.log(Level.SEVERE, 
"Error getting blckboard artifact by id", ex);
 
  217                 hitName = contentName;
 
  220             tempList.add(
new KeywordHitKey(hitName, properties, hitNumber, hit.getSolrObjectId(), content, artifact, queryRequest, queryResults));
 
  224         if (hitNumber != 0) {
 
  227             toPopulate.addAll(tempList);
 
  234         new BlackboardResultWriter(queryResults, queryRequest.getKeywordList().getName(), saveResults).execute();
 
  248     Collection<KeywordHit> getOneHitPerObject(QueryResults queryResults) {
 
  249         HashMap<Long, KeywordHit> hits = 
new HashMap<>();
 
  250         for (Keyword keyWord : queryResults.getKeywords()) {
 
  251             for (KeywordHit hit : queryResults.getResults(keyWord)) {
 
  253                 if (!hits.containsKey(hit.getSolrObjectId())) {
 
  254                     hits.put(hit.getSolrObjectId(), hit);
 
  255                 } 
else if (hit.getChunkId() < hits.get(hit.getSolrObjectId()).getChunkId()) {
 
  256                     hits.put(hit.getSolrObjectId(), hit);
 
  260         return hits.values();
 
  263     @NbBundle.Messages({
"KeywordSearchResultFactory.createNodeForKey.noResultsFound.text=No results found."})
 
  265     protected Node createNodeForKey(KeyValue key) {
 
  268         if (key instanceof KeywordHitKey) {
 
  269             AdHocQueryResult adHocQueryResult = 
new AdHocQueryResult((KeywordHitKey) key);
 
  275             ArrayList<Object> lookups = 
new ArrayList<>();
 
  276             lookups.add(adHocQueryResult);
 
  277             if (((KeywordHitKey) key).getContent() != null) {
 
  278                 lookups.add(((KeywordHitKey) key).getContent());
 
  280             if (((KeywordHitKey) key).getArtifact() != null) {
 
  281                 lookups.add(((KeywordHitKey) key).getArtifact());
 
  284             Node kvNode = 
new KeyValueNode(key, Children.LEAF, Lookups.fixed(lookups.toArray()));
 
  287             resultNode = 
new AdHocSearchFilterNode(kvNode);
 
  289             resultNode = 
new EmptyNode(
"This Node Is Empty");
 
  290             resultNode.setDisplayName(NbBundle.getMessage(
this.getClass(), 
"KeywordSearchResultFactory.createNodeForKey.noResultsFound.text"));
 
  301     final class AdHocQueryResult {
 
  303         private final long solrObjectId;
 
  304         private final QueryResults results;
 
  313         AdHocQueryResult(KeywordHitKey key) {
 
  314             this.solrObjectId = key.getSolrObjectId();
 
  315             this.results = key.getHits();
 
  324         long getSolrObjectId() {
 
  333         QueryResults getResults() {
 
  342     class KeywordHitKey 
extends KeyValue {
 
  344         private final long solrObjectId;
 
  346         private final Content content;
 
  347         private final BlackboardArtifact artifact;
 
  348         private final QueryResults hits;
 
  349         private final KeywordSearchQuery query;
 
  365         KeywordHitKey(String name, Map<String, Object> map, 
int id, 
long solrObjectId, Content content, BlackboardArtifact artifact, KeywordSearchQuery query, QueryResults hits) {
 
  366             super(name, map, 
id);
 
  367             this.solrObjectId = solrObjectId;
 
  368             this.content = content;
 
  369             this.artifact = artifact;
 
  375         Content getContent() {
 
  379         BlackboardArtifact getArtifact() {
 
  383         long getSolrObjectId() {
 
  387         QueryResults getHits() {
 
  391         KeywordSearchQuery getQuery() {
 
  400     static class BlackboardResultWriter 
extends SwingWorker<Void, Void> {
 
  402         private static final List<BlackboardResultWriter> WRITERS = 
new ArrayList<>();
 
  403         private ProgressHandle progress;
 
  404         private final KeywordSearchQuery query;
 
  405         private final QueryResults hits;
 
  406         private static final int QUERY_DISPLAY_LEN = 40;
 
  407         private final boolean saveResults;
 
  409         BlackboardResultWriter(QueryResults hits, String listName, 
boolean saveResults) {
 
  411             this.query = hits.getQuery();
 
  412             this.saveResults = saveResults;
 
  415         protected void finalizeWorker() {
 
  416             deregisterWriter(
this);
 
  417             EventQueue.invokeLater(progress::finish);
 
  421         protected Void doInBackground() throws Exception {
 
  422             registerWriter(
this); 
 
  423             final String queryStr = query.getQueryString();
 
  424             final String queryDisp = queryStr.length() > QUERY_DISPLAY_LEN ? queryStr.substring(0, QUERY_DISPLAY_LEN - 1) + 
" ..." : queryStr;
 
  426                 progress = ProgressHandle.createHandle(NbBundle.getMessage(
this.getClass(), 
"KeywordSearchResultFactory.progress.saving", queryDisp), () -> BlackboardResultWriter.this.cancel(
true));
 
  427                 hits.process(progress, null, 
this, 
false, saveResults);
 
  435         protected void done() {
 
  438             } 
catch (InterruptedException | CancellationException ex) {
 
  439                 logger.log(Level.WARNING, 
"User cancelled writing of ad hoc search query results for '{0}' to the blackboard", query.getQueryString()); 
 
  440             } 
catch (ExecutionException ex) {
 
  441                 logger.log(Level.SEVERE, 
"Error writing of ad hoc search query results for " + query.getQueryString() + 
" to the blackboard", ex); 
 
  445         private static synchronized void registerWriter(BlackboardResultWriter writer) {
 
  449         private static synchronized void deregisterWriter(BlackboardResultWriter writer) {
 
  450             WRITERS.remove(writer);
 
  453         static synchronized void stopAllWriters() {
 
  454             for (BlackboardResultWriter w : WRITERS) {