Fixed bug that caused null pointer exception when displaying keyword hits. Other minor refactoring

This commit is contained in:
Brian Carrier 2014-05-27 14:20:05 -04:00
parent 9b546fcbec
commit ac6c6da3e3
13 changed files with 109 additions and 111 deletions

View File

@ -109,7 +109,7 @@ KeywordSearchIngestModule.init.badInitMsg=Keyword search server was not properly
KeywordSearchIngestModule.init.tryStopSolrMsg={0}<br />Please try stopping old java Solr process (if it exists) and restart the application. KeywordSearchIngestModule.init.tryStopSolrMsg={0}<br />Please try stopping old java Solr process (if it exists) and restart the application.
KeywordSearchIngestModule.init.noKwInLstMsg=No keywords in keyword list. KeywordSearchIngestModule.init.noKwInLstMsg=No keywords in keyword list.
KeywordSearchIngestModule.init.onlyIdxKwSkipMsg=Only indexing will be done and and keyword search will be skipped (you can still add keyword lists using the Keyword Lists - Add to Ingest). KeywordSearchIngestModule.init.onlyIdxKwSkipMsg=Only indexing will be done and and keyword search will be skipped (you can still add keyword lists using the Keyword Lists - Add to Ingest).
KeywordSearchIngestModule.doInBackGround.displayName=Keyword Search KeywordSearchIngestModule.doInBackGround.displayName=Periodic Keyword Search
KeywordSearchIngestModule.doInBackGround.finalizeMsg= - Finalizing KeywordSearchIngestModule.doInBackGround.finalizeMsg= - Finalizing
KeywordSearchIngestModule.doInBackGround.pendingMsg= (Pending) KeywordSearchIngestModule.doInBackGround.pendingMsg= (Pending)
SearchRunner.doInBackGround.cancelMsg= (Cancelling...) SearchRunner.doInBackGround.cancelMsg= (Cancelling...)

View File

@ -146,12 +146,12 @@
<Component class="javax.swing.JComboBox" name="sourceComboBox"> <Component class="javax.swing.JComboBox" name="sourceComboBox">
<Properties> <Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new javax.swing.DefaultComboBoxModel&lt;MarkupSource&gt;()" type="code"/> <Connection code="new javax.swing.DefaultComboBoxModel&lt;org.sleuthkit.autopsy.keywordsearch.TextMarkup&gt;()" type="code"/>
</Property> </Property>
</Properties> </Properties>
<AuxValues> <AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new javax.swing.JComboBox&lt;&gt;()"/> <AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new javax.swing.JComboBox&lt;&gt;()"/>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;MarkupSource&gt;"/> <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;org.sleuthkit.autopsy.keywordsearch.TextMarkup&gt;"/>
</AuxValues> </AuxValues>
</Component> </Component>
<Component class="javax.swing.JLabel" name="hitLabel"> <Component class="javax.swing.JLabel" name="hitLabel">

View File

@ -130,14 +130,14 @@ class ExtractedContentPanel extends javax.swing.JPanel {
@Override @Override
public void itemStateChanged(ItemEvent e) { public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) { if (e.getStateChange() == ItemEvent.SELECTED) {
MarkupSource source = (MarkupSource) e.getItem(); TextMarkup source = (TextMarkup) e.getItem();
setMarkup(source); setMarkup(source);
} }
} }
}); });
setSources(new ArrayList<MarkupSource>()); setSources(new ArrayList<TextMarkup>());
extractedTextPane.setComponentPopupMenu(rightClickMenu); extractedTextPane.setComponentPopupMenu(rightClickMenu);
ActionListener actList = new ActionListener() { ActionListener actList = new ActionListener() {
@ -205,7 +205,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
extractedTextPane.setPreferredSize(new java.awt.Dimension(700, 400)); extractedTextPane.setPreferredSize(new java.awt.Dimension(700, 400));
jScrollPane1.setViewportView(extractedTextPane); jScrollPane1.setViewportView(extractedTextPane);
sourceComboBox.setModel(new javax.swing.DefaultComboBoxModel<MarkupSource>()); sourceComboBox.setModel(new javax.swing.DefaultComboBoxModel<TextMarkup>());
hitLabel.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.hitLabel.text")); // NOI18N hitLabel.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.hitLabel.text")); // NOI18N
hitLabel.setToolTipText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.hitLabel.toolTipText")); // NOI18N hitLabel.setToolTipText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.hitLabel.toolTipText")); // NOI18N
@ -358,11 +358,11 @@ class ExtractedContentPanel extends javax.swing.JPanel {
private javax.swing.JLabel pagesLabel; private javax.swing.JLabel pagesLabel;
private javax.swing.JPopupMenu rightClickMenu; private javax.swing.JPopupMenu rightClickMenu;
private javax.swing.JMenuItem selectAllMenuItem; private javax.swing.JMenuItem selectAllMenuItem;
private javax.swing.JComboBox<MarkupSource> sourceComboBox; private javax.swing.JComboBox<TextMarkup> sourceComboBox;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
void refreshCurrentMarkup() { void refreshCurrentMarkup() {
MarkupSource ms = (MarkupSource) sourceComboBox.getSelectedItem(); TextMarkup ms = (TextMarkup) sourceComboBox.getSelectedItem();
setMarkup(ms); setMarkup(ms);
} }
@ -372,11 +372,11 @@ class ExtractedContentPanel extends javax.swing.JPanel {
* *
* @param sources * @param sources
*/ */
void setSources(List<MarkupSource> sources) { void setSources(List<TextMarkup> sources) {
sourceComboBox.removeAllItems(); sourceComboBox.removeAllItems();
setPanelText(null, false); setPanelText(null, false);
for (MarkupSource ms : sources) { for (TextMarkup ms : sources) {
sourceComboBox.addItem(ms); sourceComboBox.addItem(ms);
} }
@ -391,8 +391,8 @@ class ExtractedContentPanel extends javax.swing.JPanel {
* *
* @return currently available sources on the panel * @return currently available sources on the panel
*/ */
public List<MarkupSource> getSources() { public List<TextMarkup> getSources() {
ArrayList<MarkupSource> sources = new ArrayList<>(); ArrayList<TextMarkup> sources = new ArrayList<>();
for (int i = 0; i < sourceComboBox.getItemCount(); ++i) { for (int i = 0; i < sourceComboBox.getItemCount(); ++i) {
sources.add(sourceComboBox.getItemAt(i)); sources.add(sourceComboBox.getItemAt(i));
} }
@ -403,8 +403,8 @@ class ExtractedContentPanel extends javax.swing.JPanel {
* Get the source selected in the combo box * Get the source selected in the combo box
* @return currently selected Source * @return currently selected Source
*/ */
public MarkupSource getSelectedSource() { public TextMarkup getSelectedSource() {
return (MarkupSource) sourceComboBox.getSelectedItem(); return (TextMarkup) sourceComboBox.getSelectedItem();
} }
private void setPanelText(String text, boolean detectDirection) { private void setPanelText(String text, boolean detectDirection) {
@ -565,7 +565,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
* *
* @param source the selected source * @param source the selected source
*/ */
void updateControls(MarkupSource source) { void updateControls(TextMarkup source) {
updatePageControls(source); updatePageControls(source);
updateSearchControls(source); updateSearchControls(source);
} }
@ -575,7 +575,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
* *
* @param source selected source * @param source selected source
*/ */
void updatePageControls(MarkupSource source) { void updatePageControls(TextMarkup source) {
if (source == null) { if (source == null) {
enableNextPageControl(false); enableNextPageControl(false);
enablePrevPageControl(false); enablePrevPageControl(false);
@ -614,7 +614,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
* *
* @param source selected source * @param source selected source
*/ */
void updateSearchControls(MarkupSource source) { void updateSearchControls(TextMarkup source) {
//setup search controls //setup search controls
if (source != null && source.isSearchable()) { if (source != null && source.isSearchable()) {
@ -646,7 +646,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
* *
* @param source * @param source
*/ */
private void scrollToCurrentHit(final MarkupSource source) { private void scrollToCurrentHit(final TextMarkup source) {
if (source == null || !source.isSearchable()) { if (source == null || !source.isSearchable()) {
return; return;
} }
@ -666,7 +666,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
* Updates GUI in GUI thread and gets markup in * Updates GUI in GUI thread and gets markup in
* background thread. To be invoked from GUI thread only. * background thread. To be invoked from GUI thread only.
*/ */
private void setMarkup(MarkupSource source) { private void setMarkup(TextMarkup source) {
setPanelText(NbBundle.getMessage(this.getClass(), "ExtractedContentPanel.setMarkup.panelTxt"), false); setPanelText(NbBundle.getMessage(this.getClass(), "ExtractedContentPanel.setMarkup.panelTxt"), false);
new SetMarkupWorker(source).execute(); new SetMarkupWorker(source).execute();
} }
@ -678,11 +678,11 @@ class ExtractedContentPanel extends javax.swing.JPanel {
*/ */
private final class SetMarkupWorker extends SwingWorker<Object, Void> { private final class SetMarkupWorker extends SwingWorker<Object, Void> {
private MarkupSource source; private TextMarkup source;
private String markup; private String markup;
private ProgressHandle progress; private ProgressHandle progress;
SetMarkupWorker(MarkupSource source) { SetMarkupWorker(TextMarkup source) {
this.source = source; this.source = source;
} }

View File

@ -49,7 +49,7 @@ public class ExtractedContentViewer implements DataContentViewer {
private static final Logger logger = Logger.getLogger(ExtractedContentViewer.class.getName()); private static final Logger logger = Logger.getLogger(ExtractedContentViewer.class.getName());
private ExtractedContentPanel panel; private ExtractedContentPanel panel;
private volatile Node currentNode = null; private volatile Node currentNode = null;
private MarkupSource currentSource = null; private TextMarkup currentSource = null;
private final IsDirVisitor isDirVisitor = new IsDirVisitor(); private final IsDirVisitor isDirVisitor = new IsDirVisitor();
@ -80,10 +80,11 @@ public class ExtractedContentViewer implements DataContentViewer {
* for the text markedup by SOLR and another that just displayed * for the text markedup by SOLR and another that just displayed
* raw text. * raw text.
*/ */
final List<MarkupSource> sources = new ArrayList<MarkupSource>(); final List<TextMarkup> sources = new ArrayList<TextMarkup>();
//add additional registered sources for this node // See if the node has any sources attached to it and add them to our
sources.addAll(selectedNode.getLookup().lookupAll(MarkupSource.class)); // internal list
sources.addAll(selectedNode.getLookup().lookupAll(TextMarkup.class));
// if it doesn't have any SOLR content, then we won't add more sources // if it doesn't have any SOLR content, then we won't add more sources
@ -98,9 +99,7 @@ public class ExtractedContentViewer implements DataContentViewer {
} }
// make a new source for the raw content // make a new source for the raw content
MarkupSource rawSource = new RawMarkupSource(content); TextMarkup rawSource = new RawTextMarkup(content);
// @@@ NOTE: We used to do some level of caching between instances when this
// was an inner class. Consider doing so again and save rawSource at the class level
currentSource = rawSource; currentSource = rawSource;
sources.add(rawSource); sources.add(rawSource);
@ -118,7 +117,7 @@ public class ExtractedContentViewer implements DataContentViewer {
} }
private void scrollToCurrentHit() { private void scrollToCurrentHit() {
final MarkupSource source = panel.getSelectedSource(); final TextMarkup source = panel.getSelectedSource();
if (source == null || !source.isSearchable()) { if (source == null || !source.isSearchable()) {
return; return;
} }
@ -157,7 +156,7 @@ public class ExtractedContentViewer implements DataContentViewer {
@Override @Override
public void resetComponent() { public void resetComponent() {
setPanel(new ArrayList<MarkupSource>()); setPanel(new ArrayList<TextMarkup>());
panel.resetDisplay(); panel.resetDisplay();
currentNode = null; currentNode = null;
currentSource = null; currentSource = null;
@ -171,7 +170,7 @@ public class ExtractedContentViewer implements DataContentViewer {
// see if the node has a MarkupSource object in it // see if the node has a MarkupSource object in it
// BC @@@ This seems to be added from the upper right search. // BC @@@ This seems to be added from the upper right search.
Collection<? extends MarkupSource> sources = node.getLookup().lookupAll(MarkupSource.class); Collection<? extends TextMarkup> sources = node.getLookup().lookupAll(TextMarkup.class);
if (sources.isEmpty() == false) { if (sources.isEmpty() == false) {
return true; return true;
} }
@ -204,7 +203,7 @@ public class ExtractedContentViewer implements DataContentViewer {
* *
* @param sources * @param sources
*/ */
private void setPanel(List<MarkupSource> sources) { private void setPanel(List<TextMarkup> sources) {
if (panel != null) { if (panel != null) {
panel.setSources(sources); panel.setSources(sources);
} }
@ -266,7 +265,7 @@ public class ExtractedContentViewer implements DataContentViewer {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
MarkupSource source = panel.getSelectedSource(); TextMarkup source = panel.getSelectedSource();
if (source == null) { if (source == null) {
// reset // reset
panel.updateControls(null); panel.updateControls(null);
@ -306,7 +305,7 @@ public class ExtractedContentViewer implements DataContentViewer {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
MarkupSource source = panel.getSelectedSource(); TextMarkup source = panel.getSelectedSource();
final boolean hasPreviousItem = source.hasPreviousItem(); final boolean hasPreviousItem = source.hasPreviousItem();
final boolean hasPreviousPage = source.hasPreviousPage(); final boolean hasPreviousPage = source.hasPreviousPage();
int indexVal = 0; int indexVal = 0;

View File

@ -37,15 +37,14 @@ import org.sleuthkit.autopsy.keywordsearch.KeywordQueryFilter.FilterType;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
/** /**
* Gets extracted content from Solr with the parts that match the query * Highlights hits for a given document. Knows about pages and such for the content viewer.
* highlighted
*/ */
class HighlightedMatchesSource implements MarkupSource, HighlightLookup { class HighlightedTextMarkup implements TextMarkup, HighlightLookup {
private static final Logger logger = Logger.getLogger(HighlightedMatchesSource.class.getName()); private static final Logger logger = Logger.getLogger(HighlightedTextMarkup.class.getName());
private static final String HIGHLIGHT_PRE = "<span style='background:yellow'>"; //NON-NLS private static final String HIGHLIGHT_PRE = "<span style='background:yellow'>"; //NON-NLS
private static final String HIGHLIGHT_POST = "</span>"; //NON-NLS private static final String HIGHLIGHT_POST = "</span>"; //NON-NLS
private static final String ANCHOR_PREFIX = HighlightedMatchesSource.class.getName() + "_"; private static final String ANCHOR_PREFIX = HighlightedTextMarkup.class.getName() + "_";
private Content content; private Content content;
private String keywordHitQuery; private String keywordHitQuery;
@ -62,17 +61,17 @@ class HighlightedMatchesSource implements MarkupSource, HighlightLookup {
private List<Integer> pages; private List<Integer> pages;
private QueryResults hits = null; //original hits that may get passed in private QueryResults hits = null; //original hits that may get passed in
private String originalQuery = null; //or original query if hits are not available private String originalQuery = null; //or original query if hits are not available
private boolean inited = false; private boolean isPageInfoLoaded = false;
private static final boolean DEBUG = (Version.getBuildType() == Version.Type.DEVELOPMENT); private static final boolean DEBUG = (Version.getBuildType() == Version.Type.DEVELOPMENT);
HighlightedMatchesSource(Content content, String keywordHitQuery, boolean isRegex) { HighlightedTextMarkup(Content content, String keywordHitQuery, boolean isRegex) {
this.content = content; this.content = content;
this.keywordHitQuery = keywordHitQuery; this.keywordHitQuery = keywordHitQuery;
this.isRegex = isRegex; this.isRegex = isRegex;
this.group = true; this.group = true;
this.hitsPages = new LinkedHashMap<Integer, Integer>(); this.hitsPages = new LinkedHashMap<>();
this.pages = new ArrayList<Integer>(); this.pages = new ArrayList<>();
this.pagesToHits = new HashMap<Integer, Integer>(); this.pagesToHits = new HashMap<>();
this.solrServer = KeywordSearch.getServer(); this.solrServer = KeywordSearch.getServer();
this.numberPages = 0; this.numberPages = 0;
@ -82,23 +81,26 @@ class HighlightedMatchesSource implements MarkupSource, HighlightLookup {
} }
//when the results are not known and need to requery to get hits //when the results are not known and need to requery to get hits
HighlightedMatchesSource(Content content, String solrQuery, boolean isRegex, String originalQuery) { HighlightedTextMarkup(Content content, String solrQuery, boolean isRegex, String originalQuery) {
this(content, solrQuery, isRegex); this(content, solrQuery, isRegex);
this.originalQuery = originalQuery; this.originalQuery = originalQuery;
} }
HighlightedMatchesSource(Content content, String solrQuery, boolean isRegex, QueryResults hits) { HighlightedTextMarkup(Content content, String solrQuery, boolean isRegex, QueryResults hits) {
this(content, solrQuery, isRegex); this(content, solrQuery, isRegex);
this.hits = hits; this.hits = hits;
} }
HighlightedMatchesSource(Content content, String solrQuery, boolean isRegex, boolean group, QueryResults hits) { HighlightedTextMarkup(Content content, String solrQuery, boolean isRegex, boolean group, QueryResults hits) {
this(content, solrQuery, isRegex, hits); this(content, solrQuery, isRegex, hits);
this.group = group; this.group = group;
} }
private void init() { /**
if (inited) { * The main goal of this method is to figure out which pages / chunks have hits.
*/
private void loadPageInfo() {
if (isPageInfoLoaded) {
return; return;
} }
try { try {
@ -122,26 +124,22 @@ class HighlightedMatchesSource implements MarkupSource, HighlightLookup {
//extract pages of interest, sorted //extract pages of interest, sorted
final long contentId = content.getId(); final long contentId = content.getId();
if (hits == null) { /* If this is being called from the artifacts / dir tree, then we
//special case, aka in case of dir tree, we don't know which chunks * need to perform the search to get the highlights.
//reperform search query for the content to get matching chunks info
KeywordSearchQuery chunksQuery = null;
/**
* Keyword keywordQuery = new Keyword(this.originalQuery,
* !isRegex); if (this.isRegex) { chunksQuery = new
* TermComponentQuery(keywordQuery); } else { chunksQuery = new
* LuceneQuery(keywordQuery); chunksQuery.escape(); }
*/ */
if (hits == null) {
String queryStr = KeywordSearchUtil.escapeLuceneQuery(this.keywordHitQuery); String queryStr = KeywordSearchUtil.escapeLuceneQuery(this.keywordHitQuery);
if (isRegex) { if (isRegex) {
//use white-space sep. field to get exact matches only of regex query result //use white-space sep. field to get exact matches only of regex query result
queryStr = Server.Schema.CONTENT_WS + ":" + "\"" + queryStr + "\""; queryStr = Server.Schema.CONTENT_WS + ":" + "\"" + queryStr + "\"";
} }
Keyword keywordQuery = new Keyword(queryStr, false);
chunksQuery = new LuceneQuery(hits.getKeywordList(), keywordQuery); Keyword keywordQuery = new Keyword(queryStr, !isRegex);
KeywordQueryFilter contentIdFilter = new KeywordQueryFilter(FilterType.CHUNK, contentId); List<Keyword> keywords = new ArrayList<>();
chunksQuery.addFilter(contentIdFilter); keywords.add(keywordQuery);
KeywordSearchQuery chunksQuery = new LuceneQuery(new KeywordList(keywords), keywordQuery);
chunksQuery.addFilter(new KeywordQueryFilter(FilterType.CHUNK, contentId));
try { try {
hits = chunksQuery.performQuery(); hits = chunksQuery.performQuery();
} catch (NoOpenCoreException ex) { } catch (NoOpenCoreException ex) {
@ -182,11 +180,11 @@ class HighlightedMatchesSource implements MarkupSource, HighlightLookup {
pages.add(1); pages.add(1);
pagesToHits.put(1, 0); pagesToHits.put(1, 0);
} }
inited = true; isPageInfoLoaded = true;
} }
//constructor for dummy singleton factory instance for Lookup //constructor for dummy singleton factory instance for Lookup
private HighlightedMatchesSource() { private HighlightedTextMarkup() {
} }
@Override @Override
@ -291,7 +289,7 @@ class HighlightedMatchesSource implements MarkupSource, HighlightLookup {
@Override @Override
public String getMarkup() { public String getMarkup() {
init(); //inits once loadPageInfo(); //inits once
String highLightField = null; String highLightField = null;
@ -446,7 +444,7 @@ class HighlightedMatchesSource implements MarkupSource, HighlightLookup {
//this instance does not actually work with Solr //this instance does not actually work with Solr
public static synchronized HighlightLookup getDefault() { public static synchronized HighlightLookup getDefault() {
if (instance == null) { if (instance == null) {
instance = new HighlightedMatchesSource(); instance = new HighlightedTextMarkup();
} }
return instance; return instance;
} }
@ -454,6 +452,6 @@ class HighlightedMatchesSource implements MarkupSource, HighlightLookup {
@Override @Override
//factory method, i.e. invoked on dummy (Lookup) instance //factory method, i.e. invoked on dummy (Lookup) instance
public HighlightLookup createInstance(Content c, String keywordHitQuery, boolean isRegex, String originalQuery) { public HighlightLookup createInstance(Content c, String keywordHitQuery, boolean isRegex, String originalQuery) {
return new HighlightedMatchesSource(c, keywordHitQuery, isRegex, originalQuery); return new HighlightedTextMarkup(c, keywordHitQuery, isRegex, originalQuery);
} }
} }

View File

@ -45,7 +45,7 @@ import org.sleuthkit.datamodel.File;
*/ */
class KeywordSearchFilterNode extends FilterNode { class KeywordSearchFilterNode extends FilterNode {
KeywordSearchFilterNode(HighlightedMatchesSource highlights, Node original) { KeywordSearchFilterNode(HighlightedTextMarkup highlights, Node original) {
super(original, null, new ProxyLookup(Lookups.singleton(highlights), original.getLookup())); super(original, null, new ProxyLookup(Lookups.singleton(highlights), original.getLookup()));
} }

View File

@ -18,8 +18,6 @@
*/ */
package org.sleuthkit.autopsy.keywordsearch; package org.sleuthkit.autopsy.keywordsearch;
import java.util.Collection;
import org.apache.solr.client.solrj.response.TermsResponse.Term;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
/** /**
@ -29,6 +27,8 @@ import org.sleuthkit.datamodel.AbstractFile;
*/ */
interface KeywordSearchQuery { interface KeywordSearchQuery {
KeywordList getKeywordList();
/** /**
* validate the query pre execution * validate the query pre execution
* @return true if the query passed validation * @return true if the query passed validation

View File

@ -164,14 +164,8 @@ class KeywordSearchResultFactory extends ChildFactory<KeyValueQueryContent> {
return false; return false;
} }
// Get listname
String listName = ""; String listName = queryRequest.getQuery().getKeywordList().getName();
if (queryRequests.size() > 1) {
KeywordList list = XmlKeywordSearchList.getCurrent().getListWithKeyword(keywordSearchQuery.getQueryString());
if (list != null) {
listName = list.getName();
}
}
final boolean literal_query = keywordSearchQuery.isLiteral(); final boolean literal_query = keywordSearchQuery.isLiteral();
@ -305,7 +299,7 @@ class KeywordSearchResultFactory extends ChildFactory<KeyValueQueryContent> {
//wrap in KeywordSearchFilterNode for the markup content, might need to override FilterNode for more customization //wrap in KeywordSearchFilterNode for the markup content, might need to override FilterNode for more customization
// store the data in HighlightedMatchesSource so that it can be looked up (in content viewer) // store the data in HighlightedMatchesSource so that it can be looked up (in content viewer)
HighlightedMatchesSource highlights = new HighlightedMatchesSource(content, queryStr, !key.getQuery().isLiteral(), false, hits); HighlightedTextMarkup highlights = new HighlightedTextMarkup(content, queryStr, !key.getQuery().isLiteral(), false, hits);
return new KeywordSearchFilterNode(highlights, kvNode); return new KeywordSearchFilterNode(highlights, kvNode);
} }

View File

@ -22,7 +22,6 @@ package org.sleuthkit.autopsy.keywordsearch;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -32,10 +31,8 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest.METHOD; import org.apache.solr.client.solrj.SolrRequest.METHOD;
import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.TermsResponse.Term;
import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.SolrDocumentList;
import org.openide.util.Exceptions;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.EscapeUtil; import org.sleuthkit.autopsy.coreutils.EscapeUtil;
import org.sleuthkit.autopsy.coreutils.Version; import org.sleuthkit.autopsy.coreutils.Version;
@ -76,20 +73,15 @@ class LuceneQuery implements KeywordSearchQuery {
* @param keywordQuery * @param keywordQuery
*/ */
public LuceneQuery(KeywordList keywordList, Keyword keywordQuery) { public LuceneQuery(KeywordList keywordList, Keyword keywordQuery) {
this(keywordQuery.getQuery());
this.keywordList = keywordList; this.keywordList = keywordList;
this.keywordQuery = keywordQuery; this.keywordQuery = keywordQuery;
// @@@ BC: Long-term, we should try to get rid of this string and use only the
// keyword object. Refactoring did not make its way through this yet.
this.keywordString = keywordQuery.getQuery();
this.keywordStringEscaped = this.keywordString;
} }
/**
* Constructor with keyword string to process
* @param queryStr Keyword to search for
*/
public LuceneQuery(String queryStr) {
this.keywordString = queryStr;
this.keywordStringEscaped = queryStr;
isEscaped = false;
}
@Override @Override
public void addFilter(KeywordQueryFilter filter) { public void addFilter(KeywordQueryFilter filter) {
@ -196,9 +188,9 @@ class LuceneQuery implements KeywordSearchQuery {
/** /**
* Perform the query and return result * Perform the query and return results of unique files.
* @param snippets True if results should have a snippet * @param snippets True if results should have a snippet
* @return list of ContentHit objects * @return list of ContentHit objects. One per file with hit (ignores multiple hits of the word in the same doc)
* @throws NoOpenCoreException * @throws NoOpenCoreException
*/ */
private List<ContentHit> performLuceneQuery(boolean snippets) throws NoOpenCoreException { private List<ContentHit> performLuceneQuery(boolean snippets) throws NoOpenCoreException {
@ -215,10 +207,12 @@ class LuceneQuery implements KeywordSearchQuery {
try { try {
QueryResponse response = solrServer.query(q, METHOD.POST); QueryResponse response = solrServer.query(q, METHOD.POST);
SolrDocumentList resultList = response.getResults(); SolrDocumentList resultList = response.getResults();
// objectId_chunk -> "text" -> List of previews
Map<String, Map<String, List<String>>> highlightResponse = response.getHighlighting(); Map<String, Map<String, List<String>>> highlightResponse = response.getHighlighting();
// get the unique set of files with hits // get the unique set of files with hits
Set<SolrDocument> solrDocumentsWithMatches = filterDuplicateSolrDocuments(resultList); Set<SolrDocument> uniqueSolrDocumentsWithHits = filterDuplicateSolrDocuments(resultList);
allMatchesFetched = start + MAX_RESULTS >= resultList.getNumFound(); allMatchesFetched = start + MAX_RESULTS >= resultList.getNumFound();
@ -230,7 +224,7 @@ class LuceneQuery implements KeywordSearchQuery {
return matches; return matches;
} }
for (SolrDocument resultDoc : solrDocumentsWithMatches) { for (SolrDocument resultDoc : uniqueSolrDocumentsWithHits) {
ContentHit contentHit; ContentHit contentHit;
try { try {
contentHit = createContentHitFromQueryResults(resultDoc, highlightResponse, snippets, sleuthkitCase); contentHit = createContentHitFromQueryResults(resultDoc, highlightResponse, snippets, sleuthkitCase);
@ -486,6 +480,11 @@ class LuceneQuery implements KeywordSearchQuery {
} }
} }
@Override
public KeywordList getKeywordList() {
return keywordList;
}
/** /**
* Compares SolrDocuments based on their ID's. Two SolrDocuments with * Compares SolrDocuments based on their ID's. Two SolrDocuments with
* different chunk numbers are considered equal. * different chunk numbers are considered equal.
@ -493,6 +492,8 @@ class LuceneQuery implements KeywordSearchQuery {
private class SolrDocumentComparatorIgnoresChunkId implements Comparator<SolrDocument> { private class SolrDocumentComparatorIgnoresChunkId implements Comparator<SolrDocument> {
@Override @Override
public int compare(SolrDocument left, SolrDocument right) { public int compare(SolrDocument left, SolrDocument right) {
// ID is in the form of ObjectId_Chunk
String idName = Server.Schema.ID.toString(); String idName = Server.Schema.ID.toString();
String leftID = left.getFieldValue(idName).toString(); String leftID = left.getFieldValue(idName).toString();
int index = leftID.indexOf(Server.ID_CHUNK_SEP); int index = leftID.indexOf(Server.ID_CHUNK_SEP);

View File

@ -32,18 +32,18 @@ import org.sleuthkit.datamodel.TskData;
* Display content with just raw text, no markup * Display content with just raw text, no markup
* *
*/ */
class RawMarkupSource implements MarkupSource { class RawTextMarkup implements TextMarkup {
private int numPages = 0; private int numPages = 0;
private int currentPage = 0; private int currentPage = 0;
private boolean hasChunks = false; private boolean hasChunks = false;
private Content currentContent; private final Content currentContent;
//keep last content cached //keep last content cached
private String cachedString; private String cachedString;
private int cachedChunk; private int cachedChunk;
private static final Logger logger = Logger.getLogger(RawMarkupSource.class.getName()); private static final Logger logger = Logger.getLogger(RawTextMarkup.class.getName());
RawMarkupSource(Content content) { RawTextMarkup(Content content) {
currentContent = content; currentContent = content;
initialize(); initialize();
} }

View File

@ -49,15 +49,15 @@ class TermComponentQuery implements KeywordSearchQuery {
private static final String TERMS_SEARCH_FIELD = Server.Schema.CONTENT_WS.toString(); private static final String TERMS_SEARCH_FIELD = Server.Schema.CONTENT_WS.toString();
private static final String TERMS_HANDLER = "/terms"; //NON-NLS private static final String TERMS_HANDLER = "/terms"; //NON-NLS
private static final int TERMS_TIMEOUT = 90 * 1000; //in ms private static final int TERMS_TIMEOUT = 90 * 1000; //in ms
private static Logger logger = Logger.getLogger(TermComponentQuery.class.getName()); private static final Logger logger = Logger.getLogger(TermComponentQuery.class.getName());
private String queryEscaped; private String queryEscaped;
private KeywordList keywordList; private final KeywordList keywordList;
private Keyword keyword; private final Keyword keyword;
private boolean isEscaped; private boolean isEscaped;
private List<Term> terms; private List<Term> terms;
private final List<KeywordQueryFilter> filters = new ArrayList<>(); private final List<KeywordQueryFilter> filters = new ArrayList<>();
private String field; private String field;
private static int MAX_TERMS_RESULTS = 20000; private static final int MAX_TERMS_RESULTS = 20000;
private static final boolean DEBUG = (Version.getBuildType() == Version.Type.DEVELOPMENT); private static final boolean DEBUG = (Version.getBuildType() == Version.Type.DEVELOPMENT);
@ -221,7 +221,8 @@ class TermComponentQuery implements KeywordSearchQuery {
for (Term term : terms) { for (Term term : terms) {
final String termStr = KeywordSearchUtil.escapeLuceneQuery(term.getTerm()); final String termStr = KeywordSearchUtil.escapeLuceneQuery(term.getTerm());
LuceneQuery filesQuery = new LuceneQuery(termStr); LuceneQuery filesQuery = new LuceneQuery(keywordList, new Keyword(termStr, true));
//filesQuery.setField(TERMS_SEARCH_FIELD); //filesQuery.setField(TERMS_SEARCH_FIELD);
for (KeywordQueryFilter filter : filters) { for (KeywordQueryFilter filter : filters) {
//set filter //set filter
@ -252,4 +253,9 @@ class TermComponentQuery implements KeywordSearchQuery {
return results; return results;
} }
@Override
public KeywordList getKeywordList() {
return keywordList;
}
} }

View File

@ -26,7 +26,7 @@ import java.util.LinkedHashMap;
* highlight the keyword hits and a version that does not do markup * highlight the keyword hits and a version that does not do markup
* so that you can simply view the stored text. * so that you can simply view the stored text.
*/ */
interface MarkupSource { interface TextMarkup {
/** /**
* @return text optionally marked up with the subsest of HTML that Swing * @return text optionally marked up with the subsest of HTML that Swing

View File

@ -19,9 +19,9 @@
Services Services
======================================================= --> ======================================================= -->
<folder name="Services"> <folder name="Services">
<file name="org-sleuthkit-autopsy-keywordsearch-HighlightedMatchesSource.instance"> <file name="org-sleuthkit-autopsy-keywordsearch-HighlightedTextMarkup.instance">
<attr name="instanceOf" stringvalue="org.sleuthkit.autopsy.datamodel.HighlightLookup"/> <attr name="instanceOf" stringvalue="org.sleuthkit.autopsy.datamodel.HighlightLookup"/>
<attr name="instanceCreate" methodvalue="org.sleuthkit.autopsy.keywordsearch.HighlightedMatchesSource.getDefault"/> <attr name="instanceCreate" methodvalue="org.sleuthkit.autopsy.keywordsearch.HighlightedTextMarkup.getDefault"/>
<attr name="position" intvalue="250"/> <attr name="position" intvalue="250"/>
</file> </file>
</folder> </folder>