- preliminary keyword list load/save support (more gui work needed)

- keyword list is now auto sorted alphanumerically
This commit is contained in:
adam-m 2012-01-10 17:14:19 -05:00
parent 52b29a2892
commit 973235ecd7
3 changed files with 434 additions and 28 deletions

View File

@ -155,6 +155,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchListTopComponent.loadListButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchListTopComponent.loadListButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="loadListButtonActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JButton" name="deleteWordButton"> <Component class="javax.swing.JButton" name="deleteWordButton">
<Properties> <Properties>

View File

@ -21,9 +21,12 @@ package org.sleuthkit.autopsy.keywordsearch;
import java.awt.Component; import java.awt.Component;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.regex.Pattern; 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"); searchButton.setToolTipText("Execute the keyword list search using the current list");
deleteWordButton.setToolTipText("Delete selected keyword(s) from the list"); deleteWordButton.setToolTipText("Delete selected keyword(s) from the list");
deleteAllWordsButton.setToolTipText("Delete all keywords from the list (clear it)"); deleteAllWordsButton.setToolTipText("Delete all keywords from the list (clear it)");
loadListButton.setEnabled(false);
saveListButton.setEnabled(false);
keywordTable.setAutoscrolls(true); keywordTable.setAutoscrolls(true);
keywordTable.setTableHeader(null); keywordTable.setTableHeader(null);
@ -85,15 +85,15 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
keywordTable.setShowVerticalLines(false); keywordTable.setShowVerticalLines(false);
keywordTable.getParent().setBackground(keywordTable.getBackground()); keywordTable.getParent().setBackground(keywordTable.getBackground());
//customize column witdhs //customize column witdhs
keywordTable.setSize(260,200); keywordTable.setSize(260, 200);
final int width = keywordTable.getSize().width; final int width = keywordTable.getSize().width;
TableColumn column = null; TableColumn column = null;
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
column = keywordTable.getColumnModel().getColumn(i); column = keywordTable.getColumnModel().getColumn(i);
if (i == 1) { if (i == 1) {
column.setPreferredWidth(((int) (width *0.2))); column.setPreferredWidth(((int) (width * 0.2)));
//column.setCellRenderer(new CellTooltipRenderer()); //column.setCellRenderer(new CellTooltipRenderer());
} else { } else {
column.setCellRenderer(new CellTooltipRenderer()); column.setCellRenderer(new CellTooltipRenderer());
@ -104,10 +104,10 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
loadDefaultKeywords(); loadDefaultKeywords();
} }
private void loadDefaultKeywords() { private void loadDefaultKeywords() {
//some hardcoded keywords for testing //some hardcoded keywords for testing
//phone number //phone number
tableModel.addKeyword("\\d\\d\\d[\\.-]\\d\\d\\d[\\.-]\\d\\d\\d\\d"); tableModel.addKeyword("\\d\\d\\d[\\.-]\\d\\d\\d[\\.-]\\d\\d\\d\\d");
tableModel.addKeyword("\\d{8,10}"); 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 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 org.openide.awt.Mnemonics.setLocalizedText(deleteWordButton, org.openide.util.NbBundle.getMessage(KeywordSearchListTopComponent.class, "KeywordSearchListTopComponent.deleteWordButton.text")); // NOI18N
deleteWordButton.addActionListener(new java.awt.event.ActionListener() { deleteWordButton.addActionListener(new java.awt.event.ActionListener() {
@ -274,7 +279,6 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_searchButtonActionPerformed }//GEN-LAST:event_searchButtonActionPerformed
private void addWordButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addWordButtonActionPerformed 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 }//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
KeywordSearchListsXML writer = KeywordSearchListsXML.getInstance();
//TODO popup with name / check if overwrite, then save
String listName = "initial";
List<String> 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 }//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
}//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
@ -332,6 +364,24 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
private void deleteAllWordsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteAllWordsButtonActionPerformed private void deleteAllWordsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteAllWordsButtonActionPerformed
tableModel.deleteAll(); tableModel.deleteAll();
}//GEN-LAST:event_deleteAllWordsButtonActionPerformed }//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<String> 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 // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton addWordButton; private javax.swing.JButton addWordButton;
private javax.swing.JTextField addWordField; private javax.swing.JTextField addWordField;
@ -352,12 +402,10 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
@Override @Override
public void componentOpened() { public void componentOpened() {
} }
@Override @Override
public void componentClosed() { public void componentClosed() {
} }
void writeProperties(java.util.Properties p) { 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()); private static Logger logger = Logger.getLogger(KeywordTableModel.class.getName());
//data //data
private List<TableEntry> keywordData = new ArrayList<TableEntry>(); private Set<TableEntry> keywordData = new TreeSet<TableEntry>();
@Override @Override
public int getColumnCount() { public int getColumnCount() {
@ -456,12 +504,18 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
@Override @Override
public Object getValueAt(int rowIndex, int columnIndex) { public Object getValueAt(int rowIndex, int columnIndex) {
Object ret = null; Object ret = null;
TableEntry entry = null;
//iterate until row
Iterator<TableEntry> it = keywordData.iterator();
for (int i = 0; i <= rowIndex; ++i) {
entry = it.next();
}
switch (columnIndex) { switch (columnIndex) {
case 0: case 0:
ret = (Object) keywordData.get(rowIndex).keyword; ret = (Object) entry.keyword;
break; break;
case 1: case 1:
ret = (Object) keywordData.get(rowIndex).isActive; ret = (Object) entry.isActive;
break; break;
default: default:
logger.log(Level.SEVERE, "Invalid table column index: " + columnIndex); logger.log(Level.SEVERE, "Invalid table column index: " + columnIndex);
@ -478,7 +532,13 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
@Override @Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) { public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (columnIndex == 1) { if (columnIndex == 1) {
keywordData.get(rowIndex).isActive = (Boolean) aValue; TableEntry entry = null;
//iterate until row
Iterator<TableEntry> 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) { void addKeyword(String keyword) {
keywordData.add(0, new TableEntry(keyword)); if (!keywordExists(keyword)) {
this.fireTableRowsInserted(keywordData.size() - 1, keywordData.size()); keywordData.add(new TableEntry(keyword));
}
fireTableDataChanged();
}
void addKeywords(List<String> keywords) {
for (String keyword : keywords) {
if (!keywordExists(keyword)) {
keywordData.add(new TableEntry(keyword));
}
}
fireTableDataChanged();
} }
void deleteAll() { void deleteAll() {
keywordData.clear(); keywordData.clear();
initEmpty(); fireTableDataChanged();
} }
void deleteSelected() { void deleteSelected() {
@ -535,14 +606,7 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
} }
void initEmpty() { class TableEntry implements Comparable {
for (int i = 0; i < 10; ++i) {
keywordData.add(0, new TableEntry("", false));
}
fireTableDataChanged();
}
class TableEntry {
String keyword; String keyword;
Boolean isActive; Boolean isActive;
@ -556,6 +620,11 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
this.keyword = keyword; this.keyword = keyword;
this.isActive = false; this.isActive = false;
} }
@Override
public int compareTo(Object o) {
return this.keyword.compareTo(((TableEntry) o).keyword);
}
} }
} }

View File

@ -0,0 +1,334 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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<String, KeywordSearchList> 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<String, KeywordSearchList>();
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<String, KeywordSearchList> 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<String> 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<String> 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<String> words = new ArrayList<String>();
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<String> keywords;
KeywordSearchList(String name, Date created, Date modified, List<String> 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<String> getKeywords() {
return keywords;
}
}