diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedMatchesSource.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedMatchesSource.java
index dbd3e41f80..a1eba61173 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedMatchesSource.java
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedMatchesSource.java
@@ -57,11 +57,11 @@ class HighlightedMatchesSource implements MarkupSource {
public String getMarkup() {
SolrQuery q = new SolrQuery();
- final String queryEscaped = KeywordSearchUtil.escapeLuceneQuery(solrQuery, true);
+ final String queryEscaped = KeywordSearchUtil.escapeLuceneQuery(solrQuery, true, false);
q.setQuery(queryEscaped);
q.addFilterQuery("id:" + content.getId());
- q.addHighlightField("content");
+ q.addHighlightField("content"); //for exact highlighting, try content_ws field (with stored="true" in Solr schema)
q.setHighlightSimplePre(HIGHLIGHT_PRE);
q.setHighlightSimplePost(HIGHLIGHT_POST);
q.setHighlightFragsize(0); // don't fragment the highlight
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListTopComponent.form b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListTopComponent.form
index f8b1dc40ec..a952affca6 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListTopComponent.form
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListTopComponent.form
@@ -155,6 +155,9 @@
+
+
+
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListTopComponent.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListTopComponent.java
index 2cc28b5460..876985025b 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListTopComponent.java
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListTopComponent.java
@@ -21,9 +21,12 @@ package org.sleuthkit.autopsy.keywordsearch;
import java.awt.Component;
import java.awt.event.ActionListener;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
@@ -75,9 +78,6 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
searchButton.setToolTipText("Execute the keyword list search using the current list");
deleteWordButton.setToolTipText("Delete selected keyword(s) from the list");
deleteAllWordsButton.setToolTipText("Delete all keywords from the list (clear it)");
-
- loadListButton.setEnabled(false);
- saveListButton.setEnabled(false);
keywordTable.setAutoscrolls(true);
keywordTable.setTableHeader(null);
@@ -85,15 +85,15 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
keywordTable.setShowVerticalLines(false);
keywordTable.getParent().setBackground(keywordTable.getBackground());
-
+
//customize column witdhs
- keywordTable.setSize(260,200);
+ keywordTable.setSize(260, 200);
final int width = keywordTable.getSize().width;
TableColumn column = null;
for (int i = 0; i < 2; i++) {
column = keywordTable.getColumnModel().getColumn(i);
if (i == 1) {
- column.setPreferredWidth(((int) (width *0.2)));
+ column.setPreferredWidth(((int) (width * 0.2)));
//column.setCellRenderer(new CellTooltipRenderer());
} else {
column.setCellRenderer(new CellTooltipRenderer());
@@ -104,10 +104,10 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
loadDefaultKeywords();
}
-
+
private void loadDefaultKeywords() {
//some hardcoded keywords for testing
-
+
//phone number
tableModel.addKeyword("\\d\\d\\d[\\.-]\\d\\d\\d[\\.-]\\d\\d\\d\\d");
tableModel.addKeyword("\\d{8,10}");
@@ -174,6 +174,11 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
});
org.openide.awt.Mnemonics.setLocalizedText(loadListButton, org.openide.util.NbBundle.getMessage(KeywordSearchListTopComponent.class, "KeywordSearchListTopComponent.loadListButton.text")); // NOI18N
+ loadListButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ loadListButtonActionPerformed(evt);
+ }
+ });
org.openide.awt.Mnemonics.setLocalizedText(deleteWordButton, org.openide.util.NbBundle.getMessage(KeywordSearchListTopComponent.class, "KeywordSearchListTopComponent.deleteWordButton.text")); // NOI18N
deleteWordButton.addActionListener(new java.awt.event.ActionListener() {
@@ -274,7 +279,6 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
}// //GEN-END:initComponents
private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed
- // TODO add your handling code here:
}//GEN-LAST:event_searchButtonActionPerformed
private void addWordButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addWordButtonActionPerformed
@@ -318,11 +322,39 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
}//GEN-LAST:event_addWordButtonActionPerformed
private void saveListButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveListButtonActionPerformed
-
+ KeywordSearchListsXML writer = KeywordSearchListsXML.getInstance();
+
+ //TODO popup with name / check if overwrite, then save
+
+ String listName = "initial";
+
+ List keywords = tableModel.getAllKeywords();
+
+ boolean shouldWrite = false;
+ boolean written = false;
+ if (writer.listExists(listName)) {
+ boolean replace = KeywordSearchUtil.displayConfirmDialog("Save Keyword List", "Keyword List <" + listName + "> already exists, do you want to replace it?",
+ KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN);
+ if (replace) {
+ shouldWrite = true;
+ }
+
+ } else {
+ shouldWrite = true;
+ }
+
+ if (shouldWrite) {
+ writer.addList(listName, keywords);
+ written = writer.save();
+ }
+
+ if (written) {
+ KeywordSearchUtil.displayDialog("Save Keyword List", "Keyword List <" + listName + "> saved", KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO);
+ }
+
}//GEN-LAST:event_saveListButtonActionPerformed
private void chLiteralWordActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chLiteralWordActionPerformed
-
}//GEN-LAST:event_chLiteralWordActionPerformed
private void deleteWordButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteWordButtonActionPerformed
@@ -332,6 +364,24 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
private void deleteAllWordsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteAllWordsButtonActionPerformed
tableModel.deleteAll();
}//GEN-LAST:event_deleteAllWordsButtonActionPerformed
+
+ private void loadListButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loadListButtonActionPerformed
+ KeywordSearchListsXML loader = KeywordSearchListsXML.getInstance();
+
+ //TODO popup widget with all lists in a a table, user picks name, then load into the model
+ String listName = "initial";
+
+ KeywordSearchList list = loader.getList(listName);
+ if (list != null) {
+ List keywords = list.getKeywords();
+
+ //TODO clear/append option ?
+ tableModel.deleteAll();
+ tableModel.addKeywords(keywords);
+ KeywordSearchUtil.displayDialog("Save Keyword List", "Keyword List <" + listName + "> loaded", KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO);
+ }
+
+ }//GEN-LAST:event_loadListButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton addWordButton;
private javax.swing.JTextField addWordField;
@@ -352,12 +402,10 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
@Override
public void componentOpened() {
-
}
@Override
public void componentClosed() {
-
}
void writeProperties(java.util.Properties p) {
@@ -441,7 +489,7 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
private static Logger logger = Logger.getLogger(KeywordTableModel.class.getName());
//data
- private List keywordData = new ArrayList();
+ private Set keywordData = new TreeSet();
@Override
public int getColumnCount() {
@@ -456,12 +504,18 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
Object ret = null;
+ TableEntry entry = null;
+ //iterate until row
+ Iterator it = keywordData.iterator();
+ for (int i = 0; i <= rowIndex; ++i) {
+ entry = it.next();
+ }
switch (columnIndex) {
case 0:
- ret = (Object) keywordData.get(rowIndex).keyword;
+ ret = (Object) entry.keyword;
break;
case 1:
- ret = (Object) keywordData.get(rowIndex).isActive;
+ ret = (Object) entry.isActive;
break;
default:
logger.log(Level.SEVERE, "Invalid table column index: " + columnIndex);
@@ -478,7 +532,13 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (columnIndex == 1) {
- keywordData.get(rowIndex).isActive = (Boolean) aValue;
+ TableEntry entry = null;
+ //iterate until row
+ Iterator it = keywordData.iterator();
+ for (int i = 0; i <= rowIndex; ++i) {
+ entry = it.next();
+ }
+ entry.isActive = (Boolean) aValue;
}
}
@@ -511,13 +571,24 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
}
void addKeyword(String keyword) {
- keywordData.add(0, new TableEntry(keyword));
- this.fireTableRowsInserted(keywordData.size() - 1, keywordData.size());
+ if (!keywordExists(keyword)) {
+ keywordData.add(new TableEntry(keyword));
+ }
+ fireTableDataChanged();
+ }
+
+ void addKeywords(List keywords) {
+ for (String keyword : keywords) {
+ if (!keywordExists(keyword)) {
+ keywordData.add(new TableEntry(keyword));
+ }
+ }
+ fireTableDataChanged();
}
void deleteAll() {
keywordData.clear();
- initEmpty();
+ fireTableDataChanged();
}
void deleteSelected() {
@@ -535,14 +606,7 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
}
- void initEmpty() {
- for (int i = 0; i < 10; ++i) {
- keywordData.add(0, new TableEntry("", false));
- }
- fireTableDataChanged();
- }
-
- class TableEntry {
+ class TableEntry implements Comparable {
String keyword;
Boolean isActive;
@@ -556,6 +620,11 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
this.keyword = keyword;
this.isActive = false;
}
+
+ @Override
+ public int compareTo(Object o) {
+ return this.keyword.compareTo(((TableEntry) o).keyword);
+ }
}
}
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListsXML.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListsXML.java
new file mode 100644
index 0000000000..d6e5e2ff3f
--- /dev/null
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListsXML.java
@@ -0,0 +1,334 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2011 Basis Technology Corp.
+ * Contact: carrier sleuthkit org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.autopsy.keywordsearch;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Result;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.sleuthkit.autopsy.coreutils.AutopsyPropFile;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+/**
+ * Manages reading and writing of keyword lists to user settings XML file keywords.xml
+ */
+public class KeywordSearchListsXML {
+
+ private static final String ROOT_EL = "keyword_lists";
+ private static final String LIST_EL = "keyword_list";
+ private static final String LIST_NAME_ATTR = "name";
+ private static final String LIST_CREATE_ATTR = "created";
+ private static final String LIST_MOD_ATTR = "modified";
+ private static final String KEYWORD_EL = "keyword";
+ private static final String LISTS_FILE_NAME = "keywords.xml";
+ private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
+ private static final String ENCODING = "UTF-8";
+ private String LISTS_FILE = AutopsyPropFile.getUserDirPath() + File.separator + LISTS_FILE_NAME;
+ private static final Logger logger = Logger.getLogger(KeywordSearchListsXML.class.getName());
+
+ Map theLists; //the keyword data
+
+ static KeywordSearchListsXML theInstance = null;
+
+ private KeywordSearchListsXML() {
+ }
+
+ static KeywordSearchListsXML getInstance() {
+ if (theInstance == null) {
+ theInstance = new KeywordSearchListsXML();
+ theInstance.reload();
+ }
+ return theInstance;
+ }
+
+ /**
+ * load the file or create new
+ */
+ public void reload() {
+ boolean created = false;
+ theLists = new LinkedHashMap();
+ if (!this.listFileExists()) {
+ //create new if it doesn't exist
+ save();
+ created = true;
+ }
+
+ if (!load() && !created) {
+ //create new if failed to load
+ save();
+ }
+
+ }
+
+ /**
+ * get all loaded keyword lists
+ * @return List of keyword list objects
+ */
+ Map getLists() {
+ return theLists;
+ }
+
+ /**
+ * get list by name or null
+ * @param name id of the list
+ * @return keyword list representation
+ */
+ KeywordSearchList getList(String name) {
+ return theLists.get(name);
+ }
+
+ /**
+ * check if list with given name id exists
+ * @param name id to check
+ * @return true if list already exists or false otherwise
+ */
+ boolean listExists(String name) {
+ return getList(name) != null;
+ }
+
+ /**
+ * adds the new word list using name id
+ * replacing old one if exists with the same name
+ * requires following call to save() to make permanent changes
+ * @param name the name of the new list or list to replace
+ * @param newList list of keywords
+ * @return true if old list was replaced
+ */
+ boolean addList(String name, List newList) {
+ boolean replaced = false;
+ KeywordSearchList curList = getList(name);
+ final Date now = new Date();
+ if (curList == null) {
+ theLists.put(name, new KeywordSearchList(name, now, now, newList));
+ } else {
+ theLists.put(name, new KeywordSearchList(name, curList.getDateCreated(), now, newList));
+ replaced = true;
+ }
+ return replaced;
+ }
+
+ /**
+ * writes out current list replacing the last lists file
+ */
+ boolean save() {
+ boolean success = false;
+ DateFormat dateFormatter = new SimpleDateFormat(DATE_FORMAT);
+ DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
+
+ try {
+ DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
+ Document doc = docBuilder.newDocument();
+
+ Element rootEl = doc.createElement(ROOT_EL);
+ doc.appendChild(rootEl);
+
+ for (String listName : theLists.keySet()) {
+ KeywordSearchList list = theLists.get(listName);
+ String created = dateFormatter.format(list.getDateCreated());
+ String modified = dateFormatter.format(list.getDateModified());
+ List keywords = list.getKeywords();
+
+ Element listEl = doc.createElement(LIST_EL);
+ listEl.setAttribute(LIST_NAME_ATTR, listName);
+ listEl.setAttribute(LIST_CREATE_ATTR, created);
+ listEl.setAttribute(LIST_MOD_ATTR, modified);
+
+ for (String keyword : keywords) {
+ Element keywordEl = doc.createElement(KEYWORD_EL);
+ keywordEl.setTextContent(keyword);
+ listEl.appendChild(keywordEl);
+ }
+ rootEl.appendChild(listEl);
+ }
+
+ success = saveDoc(doc);
+ } catch (ParserConfigurationException e) {
+ logger.log(Level.SEVERE, "Error saving keyword list: can't initialize parser.", e);
+ }
+ return success;
+ }
+
+
+
+ /**
+ * load and parse XML, then dispose
+ */
+ private boolean load() {
+ final Document doc = loadDoc();
+ if (doc == null) {
+ return false;
+ }
+ DateFormat dateFormatter = new SimpleDateFormat(DATE_FORMAT);
+
+
+ Element root = doc.getDocumentElement();
+ if (root == null) {
+ logger.log(Level.SEVERE, "Error loading keyword list: invalid file format.");
+ return false;
+ }
+ try {
+ NodeList listsNList = root.getElementsByTagName(LIST_EL);
+ int numLists = listsNList.getLength();
+ for (int i = 0; i < numLists; ++i) {
+ Element listEl = (Element) listsNList.item(i);
+ final String name = listEl.getAttribute(LIST_NAME_ATTR);
+ final String created = listEl.getAttribute(LIST_CREATE_ATTR);
+ final String modified = listEl.getAttribute(LIST_MOD_ATTR);
+ Date createdDate = dateFormatter.parse(created);
+ Date modDate = dateFormatter.parse(modified);
+ List words = new ArrayList();
+ KeywordSearchList list = new KeywordSearchList(name, createdDate, modDate, words);
+
+ //parse all words
+ NodeList wordsNList = listEl.getElementsByTagName(KEYWORD_EL);
+ final int numKeywords = wordsNList.getLength();
+ for (int j = 0; j < numKeywords; ++j) {
+ Element wordEl = (Element) wordsNList.item(j);
+ words.add(wordEl.getTextContent());
+
+ }
+ theLists.put(name, list);
+ }
+ } catch (ParseException e) {
+ //error parsing dates
+ logger.log(Level.SEVERE, "Error loading keyword list: can't parse dates.", e);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean listFileExists() {
+ File f = new File(LISTS_FILE);
+ return f.exists() && f.canRead() && f.canWrite();
+ }
+
+ private Document loadDoc() {
+ DocumentBuilderFactory builderFactory =
+ DocumentBuilderFactory.newInstance();
+
+ Document ret = null;
+
+
+ try {
+ DocumentBuilder builder = builderFactory.newDocumentBuilder();
+ ret = builder.parse(
+ new FileInputStream(LISTS_FILE));
+ } catch (ParserConfigurationException e) {
+ logger.log(Level.SEVERE, "Error loading keyword list: can't initialize parser.", e);
+
+ } catch (SAXException e) {
+ logger.log(Level.SEVERE, "Error loading keyword list: can't parse XML.", e);
+
+ } catch (IOException e) {
+ //error reading file
+ logger.log(Level.SEVERE, "Error loading keyword list: can't read file.", e);
+
+ }
+ return ret;
+
+ }
+
+ private boolean saveDoc(final Document doc) {
+ TransformerFactory xf = TransformerFactory.newInstance();
+ xf.setAttribute("indent-number", new Integer(1));
+ boolean success = false;
+ try {
+ Transformer xformer = xf.newTransformer();
+ xformer.setOutputProperty(OutputKeys.METHOD, "xml");
+ xformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ xformer.setOutputProperty(OutputKeys.ENCODING, ENCODING);
+ xformer.setOutputProperty(OutputKeys.STANDALONE, "yes");
+ xformer.setOutputProperty(OutputKeys.VERSION, "1.0");
+ Result out = new StreamResult(new OutputStreamWriter(new FileOutputStream(new File(LISTS_FILE)), ENCODING));
+ xformer.transform(new DOMSource(doc), out);
+ success = true;
+ } catch (UnsupportedEncodingException e) {
+ logger.log(Level.SEVERE, "Should not happen", e);
+ } catch (TransformerConfigurationException e) {
+ logger.log(Level.SEVERE, "Error writing keyword lists XML", e);
+ } catch (TransformerException e) {
+ logger.log(Level.SEVERE, "Error writing keyword lists XML", e);
+ } catch (FileNotFoundException e) {
+ logger.log(Level.SEVERE, "Error writing keyword lists XML: cannot write to file: " + LISTS_FILE, e);
+ }
+ return success;
+ }
+}
+
+/**
+ * a representation of a single keyword list
+ * created or loaded
+ */
+class KeywordSearchList {
+
+ private String name;
+ private Date created;
+ private Date modified;
+ private List keywords;
+
+ KeywordSearchList(String name, Date created, Date modified, List keywords) {
+ this.name = name;
+ this.created = created;
+ this.modified = modified;
+ this.keywords = keywords;
+ }
+
+ String getName() {
+ return name;
+ }
+
+ Date getDateCreated() {
+ return created;
+ }
+
+ Date getDateModified() {
+ return modified;
+ }
+
+ List getKeywords() {
+ return keywords;
+ }
+}
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java
index 77494d1640..8be35ba8a0 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java
@@ -70,8 +70,7 @@ public class KeywordSearchResultFactory extends ChildFactory {
public String toString() {
return "Match";
}
- },
- }
+ },}
private Presentation presentation;
private Collection queries;
private Collection things;
@@ -149,6 +148,7 @@ public class KeywordSearchResultFactory extends ChildFactory {
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);
@@ -199,11 +199,11 @@ public class KeywordSearchResultFactory extends ChildFactory {
final int lastTerm = terms.size() - 1;
int curTerm = 0;
for (Term term : terms) {
- final String termS = KeywordSearchUtil.escapeLuceneQuery(term.getTerm(), true);
+ final String termS = KeywordSearchUtil.escapeLuceneQuery(term.getTerm(), true, false);
if (!termS.contains("*")) {
highlightQuery.append(termS);
if (lastTerm != curTerm) {
- highlightQuery.append(" ");
+ highlightQuery.append(" "); //acts as OR ||
}
}
}
@@ -304,25 +304,27 @@ public class KeywordSearchResultFactory extends ChildFactory {
final String contentStr = KeywordSearch.getServer().getCore().getSolrContent(content);
- //make sure the file contains a match (this gets rid of large number of false positives)
- //TODO option in GUI to include approximate matches (faster)
- boolean matchFound = false;
- if (contentStr != null) {//if not null, some error getting from Solr, handle it by not filtering out
- //perform java regex to validate match from Solr
- String origQuery = thingContent.getQuery();
+ //postprocess
+ //make sure Solr result contains a match (this gets rid of large number of false positives)
+ boolean postprocess = true;
+ boolean matchFound = true;
+ if (postprocess) {
+ if (contentStr != null) {//if not null, some error getting from Solr, handle it by not filtering out
+ //perform java regex to validate match from Solr
+ String origQuery = thingContent.getQuery();
+
+ //since query is a match result, we can assume literal pattern
+ origQuery = Pattern.quote(origQuery);
+ Pattern p = Pattern.compile(origQuery, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
- //escape the regex query because it may contain special characters from the previous match
- //since it's a match result, we can assume literal pattern
- origQuery = Pattern.quote(origQuery);
- Pattern p = Pattern.compile(origQuery, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
-
- Matcher m = p.matcher(contentStr);
- matchFound = m.find();
+ Matcher m = p.matcher(contentStr);
+ matchFound = m.find();
+ }
}
if (matchFound) {
Node kvNode = new KeyValueNode(thingContent, Children.LEAF);
- //wrap in KeywordSearchFilterNode for the markup content, might need to override FilterNode for more customization
+ //wrap in KeywordSearchFilterNode for the markup content
HighlightedMatchesSource highlights = new HighlightedMatchesSource(content, query);
return new KeywordSearchFilterNode(highlights, kvNode, query);
} else {
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchUtil.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchUtil.java
index 9b8afa65a1..2940696605 100755
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchUtil.java
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchUtil.java
@@ -16,7 +16,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.sleuthkit.autopsy.keywordsearch;
import java.awt.Component;
@@ -31,8 +30,10 @@ import org.sleuthkit.datamodel.TskException;
public class KeywordSearchUtil {
- public enum DIALOG_MESSAGE_TYPE {ERROR, WARN, INFO};
-
+ public enum DIALOG_MESSAGE_TYPE {
+
+ ERROR, WARN, INFO
+ };
private static final Logger logger = Logger.getLogger(KeywordSearchUtil.class.getName());
public static String buildDirName(FsContent f) {
@@ -65,42 +66,44 @@ public class KeywordSearchUtil {
* such as /+-&|!(){}[]^"~*?:\ and treat the whole query as literal word
* @return encoded query
*/
- public static String escapeLuceneQuery(String query, boolean escapeLuceneChars) {
+ public static String escapeLuceneQuery(String query, boolean escapeLuceneChars, boolean encode) {
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) {
+ for (int i = 0; i < inputString.length(); ++i) {
char c = inputString.charAt(i);
- if (ESCAPE_CHARS.contains(Character.toString(c)) ) {
+ if (ESCAPE_CHARS.contains(Character.toString(c))) {
sb.append("\\");
}
sb.append(c);
}
- inputString = sb.toString();
+ queryEscaped = inputString = sb.toString();
}
-
- try {
- queryEscaped = URLEncoder.encode(inputString, "UTF-8");
- }
- catch (UnsupportedEncodingException ex) {
- logger.log(Level.SEVERE, "Error escaping URL query, should not happen.", ex);
- queryEscaped = query;
+
+ if (encode) {
+ try {
+ queryEscaped = URLEncoder.encode(inputString, "UTF-8");
+ } catch (UnsupportedEncodingException ex) {
+ logger.log(Level.SEVERE, "Error escaping URL query, should not happen.", ex);
+ queryEscaped = query;
+ }
}
return queryEscaped;
}
-
-
+
public static void displayDialog(final String title, final String message, final DIALOG_MESSAGE_TYPE type) {
int messageType;
- if (type == DIALOG_MESSAGE_TYPE.ERROR)
+ if (type == DIALOG_MESSAGE_TYPE.ERROR) {
messageType = JOptionPane.ERROR_MESSAGE;
- else if (type == DIALOG_MESSAGE_TYPE.WARN)
+ } else if (type == DIALOG_MESSAGE_TYPE.WARN) {
messageType = JOptionPane.WARNING_MESSAGE;
- else messageType = JOptionPane.INFORMATION_MESSAGE;
-
+ } else {
+ messageType = JOptionPane.INFORMATION_MESSAGE;
+ }
+
final Component parentComponent = null; // Use default window frame.
JOptionPane.showMessageDialog(
parentComponent,
@@ -108,4 +111,20 @@ public class KeywordSearchUtil {
title,
messageType);
}
+
+ public static boolean displayConfirmDialog(final String title, final String message, final DIALOG_MESSAGE_TYPE type) {
+ int messageType;
+ if (type == DIALOG_MESSAGE_TYPE.ERROR) {
+ messageType = JOptionPane.ERROR_MESSAGE;
+ } else if (type == DIALOG_MESSAGE_TYPE.WARN) {
+ messageType = JOptionPane.WARNING_MESSAGE;
+ } else {
+ messageType = JOptionPane.INFORMATION_MESSAGE;
+ }
+ if (JOptionPane.showConfirmDialog(null, message, title, JOptionPane.YES_NO_OPTION, messageType) == JOptionPane.YES_OPTION) {
+ return true;
+ } else {
+ return false;
+ }
+ }
}
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java
index 82d3799523..29e199b9e4 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java
@@ -56,7 +56,7 @@ public class LuceneQuery implements KeywordSearchQuery {
@Override
public void escape() {
- queryEscaped = KeywordSearchUtil.escapeLuceneQuery(query, true);
+ queryEscaped = KeywordSearchUtil.escapeLuceneQuery(query, true, true);
isEscaped = true;
}
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermComponentQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermComponentQuery.java
index db9f995bdd..83492307d0 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermComponentQuery.java
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermComponentQuery.java
@@ -49,7 +49,7 @@ import org.sleuthkit.autopsy.keywordsearch.KeywordSearchQueryManager.Presentatio
import org.sleuthkit.datamodel.FsContent;
public class TermComponentQuery implements KeywordSearchQuery {
-
+
private static final int TERMS_UNLIMITED = -1;
//corresponds to field in Solr schema, analyzed with white-space tokenizer only
private static final String TERMS_SEARCH_FIELD = "content_ws";
@@ -60,14 +60,14 @@ public class TermComponentQuery implements KeywordSearchQuery {
private String queryEscaped;
private boolean isEscaped;
private List terms;
-
+
public TermComponentQuery(String query) {
this.termsQuery = query;
this.queryEscaped = query;
isEscaped = false;
terms = null;
}
-
+
@Override
public void escape() {
//treat as literal
@@ -77,13 +77,13 @@ public class TermComponentQuery implements KeywordSearchQuery {
queryEscaped = Pattern.quote(termsQuery);
isEscaped = true;
}
-
+
@Override
public boolean validate() {
if (queryEscaped.equals("")) {
return false;
}
-
+
boolean valid = true;
try {
Pattern.compile(queryEscaped);
@@ -110,9 +110,9 @@ public class TermComponentQuery implements KeywordSearchQuery {
q.setTermsRegex(queryEscaped);
q.addTermsField(TERMS_SEARCH_FIELD);
q.setTimeAllowed(TERMS_TIMEOUT);
-
+
return q;
-
+
}
/*
@@ -120,7 +120,7 @@ public class TermComponentQuery implements KeywordSearchQuery {
*/
protected List executeQuery(SolrQuery q) {
Server.Core solrCore = KeywordSearch.getServer().getCore();
-
+
List termsCol = null;
try {
TermsResponse tr = solrCore.queryTerms(q);
@@ -131,17 +131,17 @@ public class TermComponentQuery implements KeywordSearchQuery {
return null; //no need to create result view, just display error dialog
}
}
-
+
@Override
public String getEscapedQueryString() {
return this.queryEscaped;
}
-
+
@Override
public String getQueryString() {
return this.termsQuery;
}
-
+
@Override
public Collection getTerms() {
return terms;
@@ -154,7 +154,7 @@ public class TermComponentQuery implements KeywordSearchQuery {
@Override
public List performQuery() {
List results = new ArrayList();
-
+
final SolrQuery q = createQuery();
terms = executeQuery(q);
@@ -168,20 +168,21 @@ public class TermComponentQuery implements KeywordSearchQuery {
final int lastTerm = terms.size() - 1;
int curTerm = 0;
for (Term term : terms) {
- final String termS = term.getTerm();
+ final String termS = KeywordSearchUtil.escapeLuceneQuery(term.getTerm(), true, false);
+ //final String termS = term.getTerm();
if (!termS.contains("*")) {
- filesQueryB.append(termS);
+ filesQueryB.append(TERMS_SEARCH_FIELD).append(":").append(termS);
if (curTerm != lastTerm) {
- filesQueryB.append(" ");
+ filesQueryB.append(" "); //acts as OR ||
}
}
++curTerm;
}
List uniqueMatches = new ArrayList();
-
+
if (!terms.isEmpty()) {
LuceneQuery filesQuery = new LuceneQuery(filesQueryB.toString());
- filesQuery.escape();
+ //filesQuery.escape();
try {
uniqueMatches = filesQuery.performQuery();
} catch (RuntimeException e) {
@@ -190,28 +191,33 @@ public class TermComponentQuery implements KeywordSearchQuery {
}
+ //result postprocessing
//filter out non-matching files using the original query (whether literal or not)
- //TODO this could be costly, for now just testing how it performs
- for (FsContent f : uniqueMatches) {
- Pattern p = Pattern.compile(queryEscaped, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
- final String contentStr = KeywordSearch.getServer().getCore().getSolrContent(f);
- Matcher m = p.matcher(contentStr);
- if (m.find()) {
- results.add(f);
+ boolean postprocess = false;
+ if (postprocess) {
+ for (FsContent f : uniqueMatches) {
+ Pattern p = Pattern.compile(queryEscaped, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
+ final String contentStr = KeywordSearch.getServer().getCore().getSolrContent(f);
+ Matcher m = p.matcher(contentStr);
+ if (m.find()) {
+ results.add(f);
+ }
}
+ } else {
+ results.addAll(uniqueMatches);
}
-
-
-
+
+
+
return results;
}
-
+
@Override
public void execute() {
SolrQuery q = createQuery();
-
+
logger.log(Level.INFO, "Executing TermsComponent query: " + q.toString());
-
+
final SwingWorker worker = new TermsQueryWorker(q);
worker.execute();
}
@@ -221,9 +227,9 @@ public class TermComponentQuery implements KeywordSearchQuery {
* @param terms
*/
private void publishNodes(List terms) {
-
+
Collection things = new ArrayList();
-
+
Iterator it = terms.iterator();
int termID = 0;
//long totalMatches = 0;
@@ -237,17 +243,17 @@ public class TermComponentQuery implements KeywordSearchQuery {
things.add(new KeyValueThing(match, kvs, ++termID));
//totalMatches += matches;
}
-
+
Node rootNode = null;
if (things.size() > 0) {
Children childThingNodes =
Children.create(new KeywordSearchResultFactory(termsQuery, things, Presentation.DETAIL), true);
-
+
rootNode = new AbstractNode(childThingNodes);
} else {
rootNode = Node.EMPTY;
}
-
+
final String pathText = "Term query";
// String pathText = "RegEx query: " + termsQuery
//+ " Files with exact matches: " + Long.toString(totalMatches) + " (also listing approximate matches)";
@@ -256,29 +262,29 @@ public class TermComponentQuery implements KeywordSearchQuery {
searchResultWin.requestActive(); // make it the active top component
}
-
+
class TermsQueryWorker extends SwingWorker, Void> {
-
+
private SolrQuery q;
private ProgressHandle progress;
-
+
TermsQueryWorker(SolrQuery q) {
this.q = q;
}
-
+
@Override
protected List doInBackground() throws Exception {
progress = ProgressHandleFactory.createHandle("Terms query task");
progress.start();
progress.progress("Running Terms query.");
-
+
terms = executeQuery(q);
-
+
progress.progress("Terms query completed.");
-
+
return terms;
}
-
+
@Override
protected void done() {
if (!this.isCancelled()) {
@@ -287,7 +293,7 @@ public class TermComponentQuery implements KeywordSearchQuery {
publishNodes(terms);
} catch (InterruptedException e) {
logger.log(Level.INFO, "Exception while executing regex query,", e);
-
+
} catch (ExecutionException e) {
logger.log(Level.INFO, "Exception while executing regex query,", e);
} finally {