Merge branch 'keyword-search-prototype' of github.com:sleuthkit/autopsy into keyword-search-prototype

This commit is contained in:
Dick Fickling 2012-01-09 13:39:06 -05:00
commit 6a6753868e
10 changed files with 126 additions and 38 deletions

View File

@ -61,4 +61,10 @@ public interface DataResultViewer {
* preparation for permanently disposing of it. * preparation for permanently disposing of it.
*/ */
public void clearComponent(); public void clearComponent();
/**
* Expand node, if supported by the viewed
* @param n Node to expand
*/
public void expandNode(Node n);
} }

View File

@ -38,6 +38,7 @@ import org.openide.nodes.Node;
import org.openide.nodes.Node.Property; import org.openide.nodes.Node.Property;
import org.openide.nodes.Node.PropertySet; import org.openide.nodes.Node.PropertySet;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.lookup.AbstractLookup;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
@ -65,6 +66,18 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
this.em.addPropertyChangeListener(this); this.em.addPropertyChangeListener(this);
} }
/**
* Expand node
* @param n Node to expand
*/
@Override
public void expandNode(Node n) {
if ( this.tableScrollPanel != null) {
OutlineView ov = ((OutlineView) this.tableScrollPanel);
ov.expandNode(n);
}
}
/** This method is called from within the constructor to /** This method is called from within the constructor to
* initialize the form. * initialize the form.
* WARNING: Do NOT modify this code. The content of this method is * WARNING: Do NOT modify this code. The content of this method is

View File

@ -91,6 +91,16 @@ public class DataResultViewerThumbnail extends AbstractDataResultViewer {
return result; return result;
} }
/**
* Expand node
* @param n Node to expand
*/
@Override
public void expandNode(Node n) {
}
@Override @Override
public void setNode(Node givenNode) { public void setNode(Node givenNode) {
// change the cursor to "waiting cursor" for this operation // change the cursor to "waiting cursor" for this operation

View File

@ -57,7 +57,7 @@ class HighlightedMatchesSource implements MarkupSource {
public String getMarkup() { public String getMarkup() {
SolrQuery q = new SolrQuery(); SolrQuery q = new SolrQuery();
final String queryEscaped = KeywordSearchUtil.escapeLuceneQuery(solrQuery); final String queryEscaped = KeywordSearchUtil.escapeLuceneQuery(solrQuery, true);
q.setQuery(queryEscaped); q.setQuery(queryEscaped);
q.addFilterQuery("id:" + content.getId()); q.addFilterQuery("id:" + content.getId());

View File

@ -18,13 +18,11 @@
*/ */
package org.sleuthkit.autopsy.keywordsearch; package org.sleuthkit.autopsy.keywordsearch;
import java.awt.Component;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import javax.swing.JOptionPane;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataExplorer; import org.sleuthkit.autopsy.corecomponentinterfaces.DataExplorer;
import org.sleuthkit.autopsy.keywordsearch.KeywordSearch.QueryType; import org.sleuthkit.autopsy.keywordsearch.KeywordSearch.QueryType;

View File

@ -18,9 +18,7 @@
*/ */
package org.sleuthkit.autopsy.keywordsearch; package org.sleuthkit.autopsy.keywordsearch;
import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -104,14 +102,25 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
} }
keywordTable.setCellSelectionEnabled(false); keywordTable.setCellSelectionEnabled(false);
//create some empty rows loadDefaultKeywords();
//tableModel.initEmpty(); }
//test private void loadDefaultKeywords() {
tableModel.addKeyword("\\d\\d\\d-\\d\\d\\d-\\d\\d\\d\\d"); //some hardcoded keywords for testing
tableModel.addKeyword("\\d\\d\\.\\d\\d\\d\\.*");
tableModel.addKeyword("text");
//phone number
tableModel.addKeyword("\\d\\d\\d[\\.-]\\d\\d\\d[\\.-]\\d\\d\\d\\d");
tableModel.addKeyword("\\d{8,10}");
tableModel.addKeyword("phone|fax");
//IP address
tableModel.addKeyword("(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])");
//email
tableModel.addKeyword("[e\\-]{0,2}mail");
tableModel.addKeyword("[A-Z0-9._%-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}");
//URL
tableModel.addKeyword("ftp|sftp|ssh|http|https|www");
//escaped literal word \d\d\d
tableModel.addKeyword("\\Q\\d\\d\\d\\E");
} }
/** This method is called from within the constructor to /** This method is called from within the constructor to
@ -309,11 +318,11 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
}//GEN-LAST:event_addWordButtonActionPerformed }//GEN-LAST:event_addWordButtonActionPerformed
private void saveListButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveListButtonActionPerformed private void saveListButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveListButtonActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_saveListButtonActionPerformed }//GEN-LAST:event_saveListButtonActionPerformed
private void chLiteralWordActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chLiteralWordActionPerformed private void chLiteralWordActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chLiteralWordActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_chLiteralWordActionPerformed }//GEN-LAST:event_chLiteralWordActionPerformed
private void deleteWordButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteWordButtonActionPerformed private void deleteWordButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteWordButtonActionPerformed
@ -343,12 +352,12 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
@Override @Override
public void componentOpened() { public void componentOpened() {
// TODO add custom code on component opening
} }
@Override @Override
public void componentClosed() { public void componentClosed() {
// TODO add custom code on component closing
} }
void writeProperties(java.util.Properties p) { void writeProperties(java.util.Properties p) {
@ -385,6 +394,10 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
Map<String, Boolean> ret = new LinkedHashMap<String, Boolean>(); Map<String, Boolean> ret = new LinkedHashMap<String, Boolean>();
for (String s : selected) { for (String s : selected) {
if (!s.trim().equals("")) { if (!s.trim().equals("")) {
//use false for isLiteral because we are currently escaping
//the keyword earlier as it is stored
//might need to change and pass isLiteral if the query object
//needs to treat it specially
ret.put(s, false); ret.put(s, false);
} }
} }

View File

@ -28,10 +28,14 @@ import java.util.Set;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.swing.SwingUtilities;
import org.apache.solr.client.solrj.response.TermsResponse.Term; import org.apache.solr.client.solrj.response.TermsResponse.Term;
import org.openide.nodes.ChildFactory; import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.util.Lookup;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode; import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode;
import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode.FsContentPropertyType; import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode.FsContentPropertyType;
import org.sleuthkit.autopsy.datamodel.KeyValueNode; import org.sleuthkit.autopsy.datamodel.KeyValueNode;
@ -140,18 +144,26 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueThing> {
@Override @Override
protected Node createNodeForKey(KeyValueThing thing) { protected Node createNodeForKey(KeyValueThing thing) {
ChildFactory<KeyValueThing> childFactory = null; ChildFactory<KeyValueThing> childFactory = null;
switch (presentation) {
case COLLAPSE:
childFactory = new ResultCollapsedChildFactory(thing);
break;
case DETAIL:
childFactory = new ResulTermsMatchesChildFactory(things);
break;
default:
}
if (presentation == Presentation.COLLAPSE) {
childFactory = new ResultCollapsedChildFactory(thing);
final Node ret = new KeyValueNode(thing, Children.create(childFactory, true));
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
//DataResultViewerTable view = Utilities.actionsGlobalContext().lookup(DataResultViewerTable.class);
for (DataResultViewer view : Lookup.getDefault().lookupAll(DataResultViewer.class)) {
view.expandNode(ret);
}
}
});
return ret;
} else {
childFactory = new ResulTermsMatchesChildFactory(things);
return new KeyValueNode(thing, Children.create(childFactory, true)); return new KeyValueNode(thing, Children.create(childFactory, true));
} }
}
/** /**
* factory produces collapsed view of all fscontent matches per query * factory produces collapsed view of all fscontent matches per query
@ -184,11 +196,17 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueThing> {
//the query is executed later on demand //the query is executed later on demand
StringBuilder highlightQuery = new StringBuilder(); StringBuilder highlightQuery = new StringBuilder();
Collection<Term> terms = tcq.getTerms(); Collection<Term> terms = tcq.getTerms();
final int lastTerm = terms.size() - 1;
int curTerm = 0;
for (Term term : terms) { for (Term term : terms) {
final String termS = KeywordSearchUtil.escapeLuceneQuery(term.getTerm()); final String termS = KeywordSearchUtil.escapeLuceneQuery(term.getTerm(), true);
if (!termS.contains("*")) {
highlightQuery.append(termS); highlightQuery.append(termS);
if (lastTerm != curTerm) {
highlightQuery.append(" "); highlightQuery.append(" ");
} }
}
}
//String highlightQueryEscaped = KeywordSearchUtil.escapeLuceneQuery(highlightQuery.toString()); //String highlightQueryEscaped = KeywordSearchUtil.escapeLuceneQuery(highlightQuery.toString());
String highlightQueryEscaped = highlightQuery.toString(); String highlightQueryEscaped = highlightQuery.toString();

View File

@ -58,10 +58,32 @@ public class KeywordSearchUtil {
return dirName; return dirName;
} }
public static String escapeLuceneQuery(String query) { /**
* Perform standard escaping / encoding into UTF-8 before sending over net
* @param query to be encoded
* @param escapeLuceneChars if true perform first escaping of Lucene specific special chars
* such as /+-&|!(){}[]^"~*?:\ and treat the whole query as literal word
* @return encoded query
*/
public static String escapeLuceneQuery(String query, boolean escapeLuceneChars) {
String queryEscaped = null; String queryEscaped = null;
String inputString = query;
if (escapeLuceneChars == true) {
final String ESCAPE_CHARS = "/+-&|!(){}[]^\"~*?:\\";
StringBuilder sb = new StringBuilder();
for (int i = 0; i< inputString.length(); ++i) {
char c = inputString.charAt(i);
if (ESCAPE_CHARS.contains(Character.toString(c)) ) {
sb.append("\\");
}
sb.append(c);
}
inputString = sb.toString();
}
try { try {
queryEscaped = URLEncoder.encode(query, "UTF-8"); queryEscaped = URLEncoder.encode(inputString, "UTF-8");
} }
catch (UnsupportedEncodingException ex) { catch (UnsupportedEncodingException ex) {
logger.log(Level.SEVERE, "Error escaping URL query, should not happen.", ex); logger.log(Level.SEVERE, "Error escaping URL query, should not happen.", ex);
@ -70,6 +92,7 @@ public class KeywordSearchUtil {
return queryEscaped; return queryEscaped;
} }
public static void displayDialog(final String title, final String message, final DIALOG_MESSAGE_TYPE type) { public static void displayDialog(final String title, final String message, final DIALOG_MESSAGE_TYPE type) {
int messageType; int messageType;
if (type == DIALOG_MESSAGE_TYPE.ERROR) if (type == DIALOG_MESSAGE_TYPE.ERROR)

View File

@ -56,7 +56,7 @@ public class LuceneQuery implements KeywordSearchQuery {
@Override @Override
public void escape() { public void escape() {
queryEscaped = KeywordSearchUtil.escapeLuceneQuery(query); queryEscaped = KeywordSearchUtil.escapeLuceneQuery(query, true);
isEscaped = true; isEscaped = true;
} }

View File

@ -165,17 +165,23 @@ public class TermComponentQuery implements KeywordSearchQuery {
//it's much more efficient and should yield the same file IDs as per match queries //it's much more efficient and should yield the same file IDs as per match queries
//requires http POST query method due to potentially large query size //requires http POST query method due to potentially large query size
StringBuilder filesQueryB = new StringBuilder(); StringBuilder filesQueryB = new StringBuilder();
final int lastTerm = terms.size() - 1;
int curTerm = 0;
for (Term term : terms) { for (Term term : terms) {
//final String termS = KeywordSearchUtil.escapeLuceneQuery(term.getTerm());
final String termS = term.getTerm(); final String termS = term.getTerm();
if (!termS.contains("*")) {
filesQueryB.append(termS); filesQueryB.append(termS);
if (curTerm != lastTerm) {
filesQueryB.append(" "); filesQueryB.append(" ");
} }
}
++curTerm;
}
List<FsContent> uniqueMatches = new ArrayList<FsContent>(); List<FsContent> uniqueMatches = new ArrayList<FsContent>();
if (!terms.isEmpty()) { if (!terms.isEmpty()) {
LuceneQuery filesQuery = new LuceneQuery(filesQueryB.toString()); LuceneQuery filesQuery = new LuceneQuery(filesQueryB.toString());
filesQuery.escape(); //TODO escaping invididual terms above instead could make a difference to Solr filesQuery.escape();
try { try {
uniqueMatches = filesQuery.performQuery(); uniqueMatches = filesQuery.performQuery();
} catch (RuntimeException e) { } catch (RuntimeException e) {
@ -196,6 +202,7 @@ public class TermComponentQuery implements KeywordSearchQuery {
} }
return results; return results;
} }