diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties
index 1138936b08..84d03317a8 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties
@@ -1,7 +1,10 @@
CTL_KeywordSearchListAction=List
+CTL_KeywordSearchListImportExportAction=Keyword Search List Import Export
+CTL_KeywordSearchListImportExportTopComponent=Keyword Search List Import Export Window
CTL_KeywordSearchListTopComponent=List
CTL_KeywordSearchTabsTopComponentAction=Keyword Search
CTL_KeywordSearchTabsTopComponentTopComponent=Keyword Search
+HINT_KeywordSearchListImportExportTopComponent=Keyword Search List Import Export window
HINT_KeywordSearchListTopComponent=Keyword Search List
HINT_KeywordSearchTabsTopComponentTopComponent=Keyword Search window
OpenIDE-Module-Name=KeywordSearch
@@ -34,3 +37,13 @@ KeywordSearchListTopComponent.addWordLabel.text=Add a new keyword:
KeywordSearchListTopComponent.deleteWordButton.text=Delete
KeywordSearchListTopComponent.deleteAllWordsButton.text=Delete All
KeywordSearchSimpleTopComponent.chRegex.text=RegEx
+KeywordSearchListImportExportTopComponent.topLabel.text=Manage (import, export, delete) lists of keywords
+KeywordSearchListImportExportTopComponent.curKeywordsLabel.text=Current lists:
+KeywordSearchListImportExportTopComponent.importButton.text=Import
+KeywordSearchListImportExportTopComponent.exportButton.text=Export
+KeywordSearchListImportExportTopComponent.deleteButton.text=Delete
+KeywordSearchListImportExportTopComponent.filesIndexedNameLabel.text=Files indexed:
+KeywordSearchListImportExportTopComponent.filesIndexedValLabel.text=-
+KeywordSearchListTopComponent.curListNameLabel.text=Loaded list name:
+KeywordSearchListTopComponent.curListValLabel.text=-
+KeywordSearchListTopComponent.importButton.text=Import
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListImportExportTopComponent.form b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListImportExportTopComponent.form
new file mode 100644
index 0000000000..5cfbd3b6f3
--- /dev/null
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListImportExportTopComponent.form
@@ -0,0 +1,148 @@
+
+
+
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListImportExportTopComponent.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListImportExportTopComponent.java
new file mode 100644
index 0000000000..972420e533
--- /dev/null
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListImportExportTopComponent.java
@@ -0,0 +1,648 @@
+/*
+ * 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.awt.Component;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.logging.Logger;
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+import javax.swing.JTable;
+import javax.swing.filechooser.FileNameExtensionFilter;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.TableColumn;
+import org.openide.util.NbBundle;
+import org.openide.windows.TopComponent;
+import org.netbeans.api.settings.ConvertAsProperties;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionReference;
+
+/**
+ * Top component which displays something.
+ */
+@ConvertAsProperties(dtd = "-//org.sleuthkit.autopsy.keywordsearch//KeywordSearchListImportExport//EN",
+autostore = false)
+@TopComponent.Description(preferredID = "KeywordSearchListImportExportTopComponent",
+//iconBase="SET/PATH/TO/ICON/HERE",
+persistenceType = TopComponent.PERSISTENCE_NEVER)
+@TopComponent.Registration(mode = "explorer", openAtStartup = false)
+@ActionID(category = "Window", id = "org.sleuthkit.autopsy.keywordsearch.KeywordSearchListImportExportTopComponent")
+@ActionReference(path = "Menu/Window" /*, position = 333 */)
+@TopComponent.OpenActionRegistration(displayName = "#CTL_KeywordSearchListImportExportAction",
+preferredID = "KeywordSearchListImportExportTopComponent")
+public final class KeywordSearchListImportExportTopComponent extends TopComponent implements KeywordSearchTopComponentInterface {
+
+ private Logger logger = Logger.getLogger(KeywordSearchListImportExportTopComponent.class.getName());
+ private KeywordListTableModel tableModel;
+
+ public KeywordSearchListImportExportTopComponent() {
+ tableModel = new KeywordListTableModel();
+ initComponents();
+ customizeComponents();
+ setName(NbBundle.getMessage(KeywordSearchListImportExportTopComponent.class, "CTL_KeywordSearchListImportExportTopComponent"));
+ setToolTipText(NbBundle.getMessage(KeywordSearchListImportExportTopComponent.class, "HINT_KeywordSearchListImportExportTopComponent"));
+
+
+ }
+
+ private void customizeComponents() {
+
+ importButton.setToolTipText("Import list(s) of keywords from an external file.");
+ exportButton.setToolTipText("Export selected list(s) of keywords to an external file.");
+ deleteButton.setToolTipText("Delete selected list(s) of keywords.");
+
+
+ listsTable.setAutoscrolls(true);
+ //listsTable.setTableHeader(null);
+ listsTable.setShowHorizontalLines(false);
+ listsTable.setShowVerticalLines(false);
+
+ listsTable.getParent().setBackground(listsTable.getBackground());
+
+ //customize column witdhs
+ listsTable.setSize(260, 200);
+ final int width = listsTable.getSize().width;
+ TableColumn column = null;
+ for (int i = 0; i < 4; i++) {
+ column = listsTable.getColumnModel().getColumn(i);
+ switch (i) {
+ case 0:
+ case 1:
+ case 2:
+ column.setCellRenderer(new CellTooltipRenderer());
+ column.setPreferredWidth(((int) (width * 0.28)));
+ column.setResizable(true);
+ break;
+ case 3:
+ column.setPreferredWidth(((int) (width * 0.15)));
+ column.setResizable(false);
+ break;
+ default:
+ break;
+
+ }
+
+ }
+ listsTable.setCellSelectionEnabled(false);
+ tableModel.resync();
+ if (KeywordSearchListsXML.getCurrent().getNumberLists() == 0) {
+ exportButton.setEnabled(false);
+ }
+
+ KeywordSearchListsXML.getCurrent().addPropertyChangeListener(new PropertyChangeListener() {
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (evt.getPropertyName().equals(KeywordSearchListsXML.ListsEvt.LIST_ADDED.toString())
+ || evt.getPropertyName().equals(KeywordSearchListsXML.ListsEvt.LIST_DELETED.toString())) {
+ tableModel.resync();
+
+ if (Integer.valueOf((Integer) evt.getNewValue()) == 0) {
+ exportButton.setEnabled(false);
+ } else if (Integer.valueOf((Integer) evt.getOldValue()) == 0) {
+ exportButton.setEnabled(true);
+ }
+ } else if (evt.getPropertyName().equals(KeywordSearchListsXML.ListsEvt.LIST_UPDATED.toString())) {
+ tableModel.resync((String) evt.getNewValue()); //changed list name
+ }
+ }
+ });
+
+
+ }
+
+ /** This method is called from within the constructor to
+ * initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is
+ * always regenerated by the Form Editor.
+ */
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ jScrollPane1 = new javax.swing.JScrollPane();
+ listsTable = new javax.swing.JTable();
+ topLabel = new javax.swing.JLabel();
+ curKeywordsLabel = new javax.swing.JLabel();
+ importButton = new javax.swing.JButton();
+ exportButton = new javax.swing.JButton();
+ deleteButton = new javax.swing.JButton();
+ filesIndexedNameLabel = new javax.swing.JLabel();
+ filesIndexedValLabel = new javax.swing.JLabel();
+
+ listsTable.setModel(tableModel);
+ listsTable.setShowHorizontalLines(false);
+ listsTable.setShowVerticalLines(false);
+ listsTable.getTableHeader().setReorderingAllowed(false);
+ jScrollPane1.setViewportView(listsTable);
+
+ org.openide.awt.Mnemonics.setLocalizedText(topLabel, org.openide.util.NbBundle.getMessage(KeywordSearchListImportExportTopComponent.class, "KeywordSearchListImportExportTopComponent.topLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(curKeywordsLabel, org.openide.util.NbBundle.getMessage(KeywordSearchListImportExportTopComponent.class, "KeywordSearchListImportExportTopComponent.curKeywordsLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(importButton, org.openide.util.NbBundle.getMessage(KeywordSearchListImportExportTopComponent.class, "KeywordSearchListImportExportTopComponent.importButton.text")); // NOI18N
+ importButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ importButtonActionPerformed(evt);
+ }
+ });
+
+ org.openide.awt.Mnemonics.setLocalizedText(exportButton, org.openide.util.NbBundle.getMessage(KeywordSearchListImportExportTopComponent.class, "KeywordSearchListImportExportTopComponent.exportButton.text")); // NOI18N
+ exportButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ exportButtonActionPerformed(evt);
+ }
+ });
+
+ org.openide.awt.Mnemonics.setLocalizedText(deleteButton, org.openide.util.NbBundle.getMessage(KeywordSearchListImportExportTopComponent.class, "KeywordSearchListImportExportTopComponent.deleteButton.text")); // NOI18N
+ deleteButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ deleteButtonActionPerformed(evt);
+ }
+ });
+
+ org.openide.awt.Mnemonics.setLocalizedText(filesIndexedNameLabel, org.openide.util.NbBundle.getMessage(KeywordSearchListImportExportTopComponent.class, "KeywordSearchListImportExportTopComponent.filesIndexedNameLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(filesIndexedValLabel, org.openide.util.NbBundle.getMessage(KeywordSearchListImportExportTopComponent.class, "KeywordSearchListImportExportTopComponent.filesIndexedValLabel.text")); // NOI18N
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 266, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(topLabel)
+ .addComponent(curKeywordsLabel)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(importButton)
+ .addGap(22, 22, 22)
+ .addComponent(exportButton)
+ .addGap(18, 18, 18)
+ .addComponent(deleteButton))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(filesIndexedNameLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(filesIndexedValLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 62, javax.swing.GroupLayout.PREFERRED_SIZE)))
+ .addContainerGap(20, Short.MAX_VALUE))
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(topLabel)
+ .addGap(35, 35, 35)
+ .addComponent(curKeywordsLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 227, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(29, 29, 29)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(importButton)
+ .addComponent(deleteButton)
+ .addComponent(exportButton))
+ .addGap(37, 37, 37)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(filesIndexedNameLabel)
+ .addComponent(filesIndexedValLabel))
+ .addContainerGap(62, Short.MAX_VALUE))
+ );
+ }// //GEN-END:initComponents
+
+ public void importButtonAction(java.awt.event.ActionEvent evt) {
+ importButtonActionPerformed(evt);
+ }
+
+ private void importButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importButtonActionPerformed
+ final String FEATURE_NAME = "Keyword List Import";
+
+ JFileChooser chooser = new JFileChooser();
+ final String EXTENSION = "xml";
+ FileNameExtensionFilter filter = new FileNameExtensionFilter(
+ "Keyword List XML file", EXTENSION);
+ chooser.setFileFilter(filter);
+ chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
+
+ int returnVal = chooser.showOpenDialog(this);
+ if (returnVal == JFileChooser.APPROVE_OPTION) {
+ File selFile = chooser.getSelectedFile();
+ if (selFile == null) {
+ return;
+ }
+
+ //force append extension if not given
+ String fileAbs = selFile.getAbsolutePath();
+
+ final KeywordSearchListsXML reader = new KeywordSearchListsXML(fileAbs);
+ if (!reader.load()) {
+ KeywordSearchUtil.displayDialog(FEATURE_NAME, "Error importing keyword list from file " + fileAbs, KeywordSearchUtil.DIALOG_MESSAGE_TYPE.ERROR);
+ return;
+ }
+
+ List toImport = reader.getListsL();
+ List toImportConfirmed = new ArrayList();
+
+ final KeywordSearchListsXML writer = KeywordSearchListsXML.getCurrent();
+
+ for (KeywordSearchList list : toImport) {
+ //check name collisions
+ if (writer.listExists(list.getName())) {
+ Object[] options = {"Yes, overwrite",
+ "No, skip",
+ "Cancel import"};
+ int choice = JOptionPane.showOptionDialog(this,
+ "Keyword list <" + list.getName() + "> already exists locally, overwrite?",
+ "Import list conflict",
+ JOptionPane.YES_NO_CANCEL_OPTION,
+ JOptionPane.QUESTION_MESSAGE,
+ null,
+ options,
+ options[0]);
+ if (choice == JOptionPane.OK_OPTION) {
+ toImportConfirmed.add(list);
+ } else if (choice == JOptionPane.CANCEL_OPTION) {
+ break;
+ }
+
+ } else {
+ //no conflict
+ toImportConfirmed.add(list);
+ }
+
+ }
+
+ if (toImportConfirmed.isEmpty())
+ return;
+
+ if (writer.writeLists(toImportConfirmed)) {
+ KeywordSearchUtil.displayDialog(FEATURE_NAME, "Keyword list imported", KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO);
+ }
+
+ }
+ }//GEN-LAST:event_importButtonActionPerformed
+
+ private void exportButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportButtonActionPerformed
+ final String FEATURE_NAME = "Keyword List Export";
+
+ List toExport = tableModel.getSelectedLists();
+ if (toExport.isEmpty()) {
+ KeywordSearchUtil.displayDialog(FEATURE_NAME, "Please select keyword lists to export", KeywordSearchUtil.DIALOG_MESSAGE_TYPE.ERROR);
+ return;
+ }
+
+ JFileChooser chooser = new JFileChooser();
+ final String EXTENSION = "xml";
+ FileNameExtensionFilter filter = new FileNameExtensionFilter(
+ "Keyword List XML file", EXTENSION);
+ chooser.setFileFilter(filter);
+ chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
+
+ int returnVal = chooser.showSaveDialog(this);
+ if (returnVal == JFileChooser.APPROVE_OPTION) {
+ File selFile = chooser.getSelectedFile();
+ if (selFile == null) {
+ return;
+ }
+
+ //force append extension if not given
+ String fileAbs = selFile.getAbsolutePath();
+ if (!fileAbs.endsWith("." + EXTENSION)) {
+ fileAbs = fileAbs + "." + EXTENSION;
+ selFile = new File(fileAbs);
+ }
+
+ boolean shouldWrite = true;
+ if (selFile.exists()) {
+ shouldWrite = KeywordSearchUtil.displayConfirmDialog(FEATURE_NAME, "File " + selFile.getName() + " exists, overwrite?", KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN);
+ }
+ if (!shouldWrite) {
+ return;
+ }
+
+
+ final KeywordSearchListsXML reader = KeywordSearchListsXML.getCurrent();
+
+ List toWrite = new ArrayList();
+ for (String listName : toExport) {
+ toWrite.add(reader.getList(listName));
+ }
+ final KeywordSearchListsXML exporter = new KeywordSearchListsXML(fileAbs);
+ boolean written = exporter.writeLists(toWrite);
+ if (written) {
+ KeywordSearchUtil.displayDialog(FEATURE_NAME, "Keyword lists exported", KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO);
+ return;
+ }
+ }
+
+ }//GEN-LAST:event_exportButtonActionPerformed
+
+ private void deleteButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteButtonActionPerformed
+ tableModel.deleteSelected();
+ }//GEN-LAST:event_deleteButtonActionPerformed
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JLabel curKeywordsLabel;
+ private javax.swing.JButton deleteButton;
+ private javax.swing.JButton exportButton;
+ private javax.swing.JLabel filesIndexedNameLabel;
+ private javax.swing.JLabel filesIndexedValLabel;
+ private javax.swing.JButton importButton;
+ private javax.swing.JScrollPane jScrollPane1;
+ private javax.swing.JTable listsTable;
+ private javax.swing.JLabel topLabel;
+ // End of variables declaration//GEN-END:variables
+
+ @Override
+ public void componentOpened() {
+ }
+
+ @Override
+ public void componentClosed() {
+ }
+
+ void writeProperties(java.util.Properties p) {
+
+ p.setProperty("version", "1.0");
+
+ }
+
+ void readProperties(java.util.Properties p) {
+ }
+
+ @Override
+ public void addSearchButtonListener(ActionListener l) {
+ }
+
+ @Override
+ public Map getQueryList() {
+ return null;
+ }
+
+ @Override
+ public String getQueryText() {
+ return null;
+ }
+
+ @Override
+ public boolean isLuceneQuerySelected() {
+ return false;
+ }
+
+ @Override
+ public boolean isMultiwordQuery() {
+ return false;
+ }
+
+ @Override
+ public boolean isRegexQuerySelected() {
+ return false;
+ }
+
+ @Override
+ public void setFilesIndexed(int filesIndexed) {
+ filesIndexedValLabel.setText(Integer.toString(filesIndexed));
+ }
+
+ static class KeywordListTableModel extends AbstractTableModel {
+ //data
+
+ private KeywordSearchListsXML listsHandle = KeywordSearchListsXML.getCurrent();
+ private Set listData = new TreeSet();
+
+ @Override
+ public int getColumnCount() {
+ return 4;
+ }
+
+ @Override
+ public int getRowCount() {
+ return listData.size();
+ }
+
+ @Override
+ public String getColumnName(int column) {
+ String colName = null;
+ switch (column) {
+ case 0:
+ colName = "Name";
+ break;
+ case 1:
+ colName = "Created";
+ break;
+ case 2:
+ colName = "Modified";
+ break;
+ case 3:
+ colName = "Sel.";
+ break;
+ default:
+ ;
+
+ }
+ return colName;
+ }
+
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ Object ret = null;
+ TableEntry entry = null;
+ //iterate until row
+ Iterator it = listData.iterator();
+ for (int i = 0; i <= rowIndex; ++i) {
+ entry = it.next();
+ }
+ switch (columnIndex) {
+ case 0:
+ ret = (Object) entry.name;
+ break;
+ case 1:
+ ret = (Object) entry.created;
+ break;
+ case 2:
+ ret = (Object) entry.modified;
+ break;
+ case 3:
+ ret = (Object) entry.isActive;
+ break;
+ default:
+ break;
+ }
+ return ret;
+ }
+
+ @Override
+ public boolean isCellEditable(int rowIndex, int columnIndex) {
+ return columnIndex == 3 ? true : false;
+ }
+
+ @Override
+ public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
+ if (columnIndex == 3) {
+ TableEntry entry = null;
+ //iterate until row
+ Iterator it = listData.iterator();
+ for (int i = 0; i <= rowIndex; ++i) {
+ entry = it.next();
+ }
+ entry.isActive = (Boolean) aValue;
+ }
+ }
+
+ @Override
+ public Class getColumnClass(int c) {
+ return getValueAt(0, c).getClass();
+ }
+
+ List getAllLists() {
+ List ret = new ArrayList();
+ for (TableEntry e : listData) {
+ ret.add(e.name);
+ }
+ return ret;
+ }
+
+ List getSelectedLists() {
+ List ret = new ArrayList();
+ for (TableEntry e : listData) {
+ if (e.isActive && !e.name.equals("")) {
+ ret.add(e.name);
+ }
+ }
+ return ret;
+ }
+
+ boolean listExists(String list) {
+ List all = getAllLists();
+ return all.contains(list);
+ }
+
+ //delete selected from handle, events are fired from the handle
+ void deleteSelected() {
+ List toDel = new ArrayList();
+ for (TableEntry e : listData) {
+ if (e.isActive && !e.name.equals("")) {
+ toDel.add(e);
+ }
+ }
+ for (TableEntry del : toDel) {
+ listsHandle.deleteList(del.name);
+ }
+
+ }
+
+ //resync model from handle, then update table
+ void resync() {
+ listData.clear();
+ addLists(listsHandle.getListsL());
+ fireTableDataChanged();
+ }
+
+ //resync single model entry from handle, then update table
+ void resync(String listName) {
+ TableEntry found = null;
+ for (TableEntry e : listData) {
+ if (e.name.equals(listName)) {
+ found = e;
+ break;
+ }
+ }
+ if (found != null) {
+ listData.remove(found);
+ addList(listsHandle.getList(listName));
+ }
+ fireTableDataChanged();
+ }
+
+ //add list to the model
+ private void addList(KeywordSearchList list) {
+ if (!listExists(list.getName())) {
+ listData.add(new TableEntry(list));
+ }
+ }
+
+ //add lists to the model
+ private void addLists(List lists) {
+ for (KeywordSearchList list : lists) {
+ if (!listExists(list.getName())) {
+ listData.add(new TableEntry(list));
+ }
+ }
+ }
+
+ //single model entry
+ class TableEntry implements Comparable {
+
+ private DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+ String name;
+ String created;
+ String modified;
+ Boolean isActive;
+
+ TableEntry(KeywordSearchList list, Boolean isActive) {
+ this.name = list.getName();
+ this.created = dateFormatter.format(list.getDateCreated());
+ this.modified = dateFormatter.format(list.getDateModified());
+ this.isActive = isActive;
+ }
+
+ TableEntry(KeywordSearchList list) {
+ this.name = list.getName();
+ this.created = dateFormatter.format(list.getDateCreated());
+ this.modified = dateFormatter.format(list.getDateModified());
+ this.isActive = false;
+ }
+
+ @Override
+ public int compareTo(Object o) {
+ return this.name.compareTo(((TableEntry) o).name);
+ }
+ }
+ }
+
+ /**
+ * tooltips that show text
+ */
+ private static class CellTooltipRenderer extends DefaultTableCellRenderer {
+
+ @Override
+ public Component getTableCellRendererComponent(
+ JTable table, Object value,
+ boolean isSelected, boolean hasFocus,
+ int row, int column) {
+
+ if (column < 3) {
+ String val = (String) table.getModel().getValueAt(row, column);
+ setToolTipText(val);
+ setText(val);
+ }
+
+ return this;
+ }
+ }
+}
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListTopComponent.form b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListTopComponent.form
index a952affca6..d6a0e9be0d 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListTopComponent.form
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListTopComponent.form
@@ -21,15 +21,12 @@
-
-
-
-
-
-
+
+
-
+
+
@@ -43,6 +40,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -54,7 +65,10 @@
-
+
+
+
+
@@ -76,7 +90,12 @@
-
+
+
+
+
+
+
@@ -218,5 +237,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListTopComponent.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListTopComponent.java
index a93e98dce2..40a35c8fda 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListTopComponent.java
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListTopComponent.java
@@ -23,6 +23,8 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -40,6 +42,7 @@ import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
+import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
@@ -49,6 +52,7 @@ import org.openide.windows.TopComponent;
import org.netbeans.api.settings.ConvertAsProperties;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
+import org.sleuthkit.autopsy.keywordsearch.KeywordSearchTabsTopComponent.TABS;
/**
* Top component which displays something.
@@ -58,7 +62,7 @@ autostore = false)
@TopComponent.Description(preferredID = "KeywordSearchListTopComponent",
//iconBase="SET/PATH/TO/ICON/HERE",
persistenceType = TopComponent.PERSISTENCE_NEVER)
-@TopComponent.Registration(mode = "output", openAtStartup = false)
+@TopComponent.Registration(mode = "explorer", openAtStartup = false)
@ActionID(category = "Window", id = "org.sleuthkit.autopsy.keywordsearch.KeywordSearchListTopComponent")
@ActionReference(path = "Menu/Window" /*, position = 333 */)
@TopComponent.OpenActionRegistration(displayName = "#CTL_KeywordSearchListAction",
@@ -84,6 +88,7 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
addWordField.setToolTipText("Enter a new word or regex to search");
loadListButton.setToolTipText("Load a new keyword list from file or delete an existing list");
+ importButton.setToolTipText("Import list(s) of keywords from an external file.");
saveListButton.setToolTipText("Save the current keyword list to a file");
searchButton.setToolTipText("Execute the keyword list search using the current list");
deleteWordButton.setToolTipText("Delete selected keyword(s) from the list");
@@ -114,7 +119,26 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
loadDefaultKeywords();
- if (KeywordSearchListsXML.getInstance().getNumberLists() == 0) {
+ KeywordSearchListsXML.getCurrent().addPropertyChangeListener(new PropertyChangeListener() {
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (evt.getPropertyName().equals(KeywordSearchListsXML.ListsEvt.LIST_DELETED.toString())) {
+ //still keep keywords from deleted list in widgetm just disassociate the name
+ currentKeywordList = null;
+ curListValLabel.setText("-");
+ if (Integer.valueOf((Integer) evt.getNewValue()) == 0) {
+ loadListButton.setEnabled(false);
+ }
+ } else if (evt.getPropertyName().equals(KeywordSearchListsXML.ListsEvt.LIST_ADDED.toString())) {
+ if (Integer.valueOf((Integer) evt.getOldValue()) == 0) {
+ loadListButton.setEnabled(true);
+ }
+ }
+ }
+ });
+
+ if (KeywordSearchListsXML.getCurrent().getNumberLists() == 0) {
loadListButton.setEnabled(false);
}
}
@@ -160,6 +184,9 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
chLiteralWord = new javax.swing.JCheckBox();
jScrollPane1 = new javax.swing.JScrollPane();
keywordTable = new javax.swing.JTable();
+ curListNameLabel = new javax.swing.JLabel();
+ curListValLabel = new javax.swing.JLabel();
+ importButton = new javax.swing.JButton();
org.openide.awt.Mnemonics.setLocalizedText(searchButton, org.openide.util.NbBundle.getMessage(KeywordSearchListTopComponent.class, "KeywordSearchListTopComponent.searchButton.text")); // NOI18N
searchButton.addActionListener(new java.awt.event.ActionListener() {
@@ -228,6 +255,17 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
keywordTable.setShowVerticalLines(false);
jScrollPane1.setViewportView(keywordTable);
+ org.openide.awt.Mnemonics.setLocalizedText(curListNameLabel, org.openide.util.NbBundle.getMessage(KeywordSearchListTopComponent.class, "KeywordSearchListTopComponent.curListNameLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(curListValLabel, org.openide.util.NbBundle.getMessage(KeywordSearchListTopComponent.class, "KeywordSearchListTopComponent.curListValLabel.text")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(importButton, org.openide.util.NbBundle.getMessage(KeywordSearchListTopComponent.class, "KeywordSearchListTopComponent.importButton.text")); // NOI18N
+ importButton.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ importButtonActionPerformed(evt);
+ }
+ });
+
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
@@ -237,14 +275,11 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(chLiteralWord)
.addComponent(titleLabel)
- .addComponent(loadListButton)
- .addComponent(addWordLabel)
.addGroup(layout.createSequentialGroup()
- .addComponent(deleteWordButton)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(deleteAllWordsButton)
+ .addComponent(loadListButton)
.addGap(18, 18, 18)
- .addComponent(saveListButton))
+ .addComponent(importButton))
+ .addComponent(addWordLabel)
.addGroup(layout.createSequentialGroup()
.addComponent(addWordField, javax.swing.GroupLayout.PREFERRED_SIZE, 152, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(31, 31, 31)
@@ -255,7 +290,18 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
.addComponent(filesIndexedNameLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(filesIndexedValLabel))
- .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 272, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 272, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
+ .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
+ .addComponent(curListNameLabel)
+ .addGap(18, 18, 18)
+ .addComponent(curListValLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
+ .addComponent(deleteWordButton)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(deleteAllWordsButton)
+ .addGap(18, 18, 18)
+ .addComponent(saveListButton))))
.addContainerGap(15, Short.MAX_VALUE))
);
layout.setVerticalGroup(
@@ -264,7 +310,9 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
.addContainerGap()
.addComponent(titleLabel)
.addGap(18, 18, 18)
- .addComponent(loadListButton)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(loadListButton)
+ .addComponent(importButton))
.addGap(19, 19, 19)
.addComponent(addWordLabel)
.addGap(18, 18, 18)
@@ -284,7 +332,11 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
.addComponent(saveListButton))
.addGap(29, 29, 29)
.addComponent(searchButton)
- .addGap(38, 38, 38)
+ .addGap(18, 18, 18)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(curListNameLabel)
+ .addComponent(curListValLabel))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(filesIndexedNameLabel)
.addComponent(filesIndexedValLabel))
@@ -337,7 +389,7 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
private void saveListButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveListButtonActionPerformed
final String FEATURE_NAME = "Save Keyword List";
- KeywordSearchListsXML writer = KeywordSearchListsXML.getInstance();
+ KeywordSearchListsXML writer = KeywordSearchListsXML.getCurrent();
String listName = (String) JOptionPane.showInputDialog(
null,
@@ -352,32 +404,27 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
}
List keywords = tableModel.getAllKeywords();
- boolean shouldWrite = false;
- boolean written = false;
+ boolean shouldAdd = false;
if (writer.listExists(listName)) {
boolean replace = KeywordSearchUtil.displayConfirmDialog(FEATURE_NAME, "Keyword List <" + listName + "> already exists, do you want to replace it?",
KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN);
if (replace) {
- shouldWrite = true;
+ shouldAdd = true;
}
} else {
- shouldWrite = true;
+ shouldAdd = true;
}
- if (shouldWrite) {
+ if (shouldAdd) {
writer.addList(listName, keywords);
- written = writer.save();
}
- if (written) {
- currentKeywordList = listName;
- KeywordSearchUtil.displayDialog(FEATURE_NAME, "Keyword List <" + listName + "> saved", KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO);
- //enable load button if it was previously disabled, as lists now exist
- if (loadListButton.isEnabled() == false) {
- loadListButton.setEnabled(true);
- }
- }
+ currentKeywordList = listName;
+ curListValLabel.setText(listName);
+ KeywordSearchUtil.displayDialog(FEATURE_NAME, "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
@@ -395,35 +442,44 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
final String FEATURE_NAME = "Load Keyword List";
- KeywordSearchListsXML loader = KeywordSearchListsXML.getInstance();
+ KeywordSearchListsXML loader = KeywordSearchListsXML.getCurrent();
final String listName = showLoadDeleteListDialog(FEATURE_NAME, loader.getListNames().toArray(), currentKeywordList, true);
if (listName == null || listName.equals("")) {
return;
}
+ currentKeywordList = listName;
+ tableModel.resync(currentKeywordList);
+ curListValLabel.setText(listName);
- KeywordSearchList list = loader.getList(listName);
- if (list != null) {
- List keywords = list.getKeywords();
+ KeywordSearchUtil.displayDialog(FEATURE_NAME, "Keyword List <" + listName + "> loaded", KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO);
- //TODO clear/append option ?
- tableModel.deleteAll();
- tableModel.addKeywords(keywords);
- currentKeywordList = listName;
- KeywordSearchUtil.displayDialog(FEATURE_NAME, "Keyword List <" + listName + "> loaded", KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO);
- }
}//GEN-LAST:event_loadListButtonActionPerformed
+
+ private void importButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importButtonActionPerformed
+ //delegate to lists component
+ JTabbedPane tabs = (JTabbedPane) this.getParent();
+ KeywordSearchListImportExportTopComponent lists = (KeywordSearchListImportExportTopComponent) tabs.getComponentAt(TABS.Lists.ordinal());
+ if (lists != null) {
+ lists.importButtonAction(evt);
+ }
+
+ }//GEN-LAST:event_importButtonActionPerformed
+
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton addWordButton;
private javax.swing.JTextField addWordField;
private javax.swing.JLabel addWordLabel;
private javax.swing.JCheckBox chLiteralWord;
+ private javax.swing.JLabel curListNameLabel;
+ private javax.swing.JLabel curListValLabel;
private javax.swing.JButton deleteAllWordsButton;
private javax.swing.JButton deleteWordButton;
private javax.swing.JLabel filesIndexedNameLabel;
private javax.swing.JLabel filesIndexedValLabel;
+ private javax.swing.JButton importButton;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTable keywordTable;
private javax.swing.JLabel listLabel;
@@ -432,6 +488,23 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
private javax.swing.JButton searchButton;
private javax.swing.JLabel titleLabel;
// End of variables declaration//GEN-END:variables
+ private JComboBox loadListCombo;
+
+ private JComboBox findDialogComponent(Component component) {
+ if (component instanceof JComboBox) {
+ loadListCombo = (JComboBox) component;
+ } else if (component instanceof JPanel) {
+ for (Component c : ((JPanel) component).getComponents()) {
+ findDialogComponent(c);
+ }
+ } else if (component instanceof JOptionPane) {
+ for (Component c : ((JOptionPane) component).getComponents()) {
+ findDialogComponent(c);
+ }
+
+ }
+ return loadListCombo;
+ }
private String showLoadDeleteListDialog(final String title, Object[] choices, Object initialChoice, boolean deleteOption) {
if (deleteOption) {
@@ -444,64 +517,11 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
loadPane.setWantsInput(true);
loadPane.setSelectionValues(choices);
loadPane.setInitialSelectionValue(initialChoice);
- //loadPane.setValue(initialChoice);
final JDialog loadDialog = loadPane.createDialog(null, title);
final JPopupMenu rightClickMenu = new JPopupMenu();
- JMenuItem delItem = new JMenuItem("Delete List");
- delItem.addActionListener(new ActionListener() {
- JComboBox combo;
- //find the combo component
- private JComboBox getDialogComponent(Component component) {
- if (component instanceof JComboBox) {
- combo = (JComboBox)component;
- } else if (component instanceof JPanel) {
- for (Component c : ((JPanel) component).getComponents()) {
- getDialogComponent(c);
- }
- } else if (component instanceof JOptionPane) {
- for (Component c : ((JOptionPane) component).getComponents()) {
- getDialogComponent(c);
- }
-
- }
- return combo;
- }
-
- @Override
- public void actionPerformed(ActionEvent e) {
- //there is no JOptionPane API to get current from combobox before OK is pressed
- //workaround traversing the widgets
- String selList = null;
- combo = getDialogComponent(loadPane);
- if (combo != null) {
- selList = (String) combo.getSelectedItem();
- }
-
-
- if (selList != null && selList != JOptionPane.UNINITIALIZED_VALUE) {
- KeywordSearchListsXML loader = KeywordSearchListsXML.getInstance();
- boolean deleted = loader.deleteList(selList);
- if (deleted) {
- Object[] choices = loader.getListNames().toArray();
- loadPane.setSelectionValues(choices);
- if (choices.length > 0) {
- loadPane.setInitialSelectionValue(choices[0]);
- }
- loadPane.selectInitialValue();
- KeywordSearchUtil.displayDialog(title, "Keyword List <" + selList + "> deleted", KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO);
- }
-
- }
-
- rightClickMenu.setVisible(false);
- }
- });
-
- rightClickMenu.add(delItem);
-
- loadPane.addMouseListener(new MouseListener() {
+ final MouseListener rightClickListener = new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
@@ -526,15 +546,59 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
public void mouseReleased(MouseEvent e) {
rightClickMenu.setVisible(false);
}
+ };
+ JMenuItem delItem = new JMenuItem("Delete List");
+
+ delItem.addActionListener(new ActionListener() {
+
+ JComboBox combo;
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ String selList = null;
+ //there is no JOptionPane API to get current from combobox before OK is pressed
+ //workaround traversing the widgets
+ combo = findDialogComponent(loadPane);
+
+ if (combo != null) {
+ selList = (String) combo.getSelectedItem();
+ }
+
+ if (selList != null && selList != JOptionPane.UNINITIALIZED_VALUE) {
+ KeywordSearchListsXML loader = KeywordSearchListsXML.getCurrent();
+ boolean deleted = loader.deleteList(selList);
+ if (deleted) {
+ Object[] choices = loader.getListNames().toArray();
+ loadPane.setSelectionValues(choices);
+ if (choices.length > 0) {
+ loadPane.setInitialSelectionValue(choices[0]);
+ }
+ loadPane.selectInitialValue();
+ combo = findDialogComponent(loadPane);
+ combo.addMouseListener(rightClickListener);
+ KeywordSearchUtil.displayDialog(title, "Keyword List <" + selList + "> deleted", KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO);
+ }
+ }
+ rightClickMenu.setVisible(false);
+ }
});
+ rightClickMenu.add(delItem);
+ JComboBox combo = findDialogComponent(loadPane);
+ combo.addMouseListener(rightClickListener);
loadPane.selectInitialValue();
loadDialog.setVisible(true);
loadDialog.dispose();
+ String retString = (String) loadPane.getInputValue();
+ if (retString == JOptionPane.UNINITIALIZED_VALUE) //no choice was made
+ {
+ retString = null;
+ }
- return (String) loadPane.getInputValue();
+ return retString;
} else {
return (String) JOptionPane.showInputDialog(
null,
@@ -629,9 +693,8 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
}
static class KeywordTableModel extends AbstractTableModel {
-
- private static Logger logger = Logger.getLogger(KeywordTableModel.class.getName());
//data
+
private Set keywordData = new TreeSet();
@Override
@@ -729,6 +792,15 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
fireTableDataChanged();
}
+ void resync(String listName) {
+ KeywordSearchListsXML loader = KeywordSearchListsXML.getCurrent();
+ KeywordSearchList list = loader.getList(listName);
+ List keywords = list.getKeywords();
+
+ deleteAll();
+ addKeywords(keywords);
+ }
+
void deleteAll() {
keywordData.clear();
fireTableDataChanged();
@@ -736,7 +808,7 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
void deleteSelected() {
List toDel = new ArrayList();
- int i = 0;
+
for (TableEntry e : keywordData) {
if (e.isActive && !e.keyword.equals("")) {
toDel.add(e);
@@ -774,7 +846,7 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
/**
* tooltips that show entire query string
*/
- public static class CellTooltipRenderer extends DefaultTableCellRenderer {
+ private static class CellTooltipRenderer extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListsXML.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListsXML.java
index 9a8b8d42bf..0167cd6134 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListsXML.java
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListsXML.java
@@ -18,6 +18,8 @@
*/
package org.sleuthkit.autopsy.keywordsearch;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -54,6 +56,7 @@ import org.xml.sax.SAXException;
/**
* Manages reading and writing of keyword lists to user settings XML file keywords.xml
+ * or to any file provided in constructor
*/
public class KeywordSearchListsXML {
@@ -63,25 +66,49 @@ public class KeywordSearchListsXML {
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 CUR_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 String CUR_LISTS_FILE = AutopsyPropFile.getUserDirPath() + File.separator + CUR_LISTS_FILE_NAME;
private static final Logger logger = Logger.getLogger(KeywordSearchListsXML.class.getName());
-
Map theLists; //the keyword data
-
- static KeywordSearchListsXML theInstance = null;
+ static KeywordSearchListsXML currentInstance = null;
+ private String xmlFile;
+ private DateFormat dateFormatter;
- private KeywordSearchListsXML() {
+ //property support
+ public enum ListsEvt {
+
+ LIST_ADDED, LIST_DELETED, LIST_UPDATED
+ };
+ private PropertyChangeSupport changeSupport;
+
+ /**
+ * Constructor to obtain handle on other that the current keyword list
+ * (such as for import or export)
+ * @param xmlFile xmlFile to obtain KeywordSearchListsXML handle on
+ */
+ KeywordSearchListsXML(String xmlFile) {
+ theLists = new LinkedHashMap();
+ this.xmlFile = xmlFile;
+ changeSupport = new PropertyChangeSupport(this);
+
+ dateFormatter = new SimpleDateFormat(DATE_FORMAT);
}
- static KeywordSearchListsXML getInstance() {
- if (theInstance == null) {
- theInstance = new KeywordSearchListsXML();
- theInstance.reload();
+ /**
+ * get instance for managing the current keyword list of the application
+ */
+ static KeywordSearchListsXML getCurrent() {
+ if (currentInstance == null) {
+ currentInstance = new KeywordSearchListsXML(CUR_LISTS_FILE);
+ currentInstance.reload();
}
- return theInstance;
+ return currentInstance;
+ }
+
+ void addPropertyChangeListener(PropertyChangeListener l) {
+ changeSupport.addPropertyChangeListener(l);
}
/**
@@ -89,36 +116,39 @@ public class KeywordSearchListsXML {
*/
public void reload() {
boolean created = false;
- theLists = new LinkedHashMap();
+
+ theLists.clear();
if (!this.listFileExists()) {
//create new if it doesn't exist
save();
created = true;
}
+ //load, if fails to laod create new
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;
+ List getListsL() {
+ List ret = new ArrayList();
+ for (KeywordSearchList list : theLists.values()) {
+ ret.add(list);
+ }
+ return ret;
}
-
+
/**
* get list of all loaded keyword list names
* @return List of keyword list names
*/
- ListgetListNames() {
+ List getListNames() {
return new ArrayList(theLists.keySet());
}
-
+
/**
* get number of lists currently stored
* @return number of lists currently stored
@@ -148,7 +178,6 @@ public class KeywordSearchListsXML {
/**
* 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
@@ -157,15 +186,47 @@ public class KeywordSearchListsXML {
boolean replaced = false;
KeywordSearchList curList = getList(name);
final Date now = new Date();
+ final int oldSize = this.getNumberLists();
if (curList == null) {
theLists.put(name, new KeywordSearchList(name, now, now, newList));
+ save();
+ changeSupport.firePropertyChange(ListsEvt.LIST_ADDED.toString(), oldSize, this.getNumberLists());
} else {
theLists.put(name, new KeywordSearchList(name, curList.getDateCreated(), now, newList));
+ save();
replaced = true;
+ changeSupport.firePropertyChange(ListsEvt.LIST_UPDATED.toString(), null, name);
}
+
return replaced;
}
+
+ /**
+ * write out multiple lists
+ * @param lists
+ * @return
+ */
+ boolean writeLists(List lists) {
+ int oldSize = this.getNumberLists();
+
+ List overwritten = new ArrayList();
+
+ for (KeywordSearchList list : lists) {
+ if (this.listExists(list.getName()))
+ overwritten.add(list);
+ theLists.put(list.getName(), list);
+ }
+ boolean saved = save();
+ if (saved) {
+ changeSupport.firePropertyChange(ListsEvt.LIST_ADDED.toString(), oldSize, this.getNumberLists());
+ for (KeywordSearchList over : overwritten) {
+ changeSupport.firePropertyChange(ListsEvt.LIST_UPDATED.toString(), null, over.getName());
+ }
+ }
+ return saved;
+ }
+
/**
* delete list if exists and save new list
* @param name of list to delete
@@ -173,21 +234,23 @@ public class KeywordSearchListsXML {
*/
boolean deleteList(String name) {
boolean deleted = false;
+ final int oldSize = this.getNumberLists();
KeywordSearchList delList = getList(name);
if (delList != null) {
theLists.remove(name);
deleted = save();
}
+ changeSupport.firePropertyChange(ListsEvt.LIST_DELETED.toString(), oldSize, this.getNumberLists());
return deleted;
-
+
}
/**
* writes out current list replacing the last lists file
*/
- boolean save() {
+ private boolean save() {
boolean success = false;
- DateFormat dateFormatter = new SimpleDateFormat(DATE_FORMAT);
+
DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
try {
@@ -223,18 +286,14 @@ public class KeywordSearchListsXML {
return success;
}
-
-
/**
* load and parse XML, then dispose
*/
- private boolean load() {
+ public boolean load() {
final Document doc = loadDoc();
if (doc == null) {
return false;
}
- DateFormat dateFormatter = new SimpleDateFormat(DATE_FORMAT);
-
Element root = doc.getDocumentElement();
if (root == null) {
@@ -273,7 +332,7 @@ public class KeywordSearchListsXML {
}
private boolean listFileExists() {
- File f = new File(LISTS_FILE);
+ File f = new File(xmlFile);
return f.exists() && f.canRead() && f.canWrite();
}
@@ -287,7 +346,7 @@ public class KeywordSearchListsXML {
try {
DocumentBuilder builder = builderFactory.newDocumentBuilder();
ret = builder.parse(
- new FileInputStream(LISTS_FILE));
+ new FileInputStream(xmlFile));
} catch (ParserConfigurationException e) {
logger.log(Level.SEVERE, "Error loading keyword list: can't initialize parser.", e);
@@ -314,9 +373,14 @@ public class KeywordSearchListsXML {
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));
+ File file = new File(xmlFile);
+ FileOutputStream stream = new FileOutputStream(file);
+ Result out = new StreamResult(new OutputStreamWriter(stream, ENCODING));
xformer.transform(new DOMSource(doc), out);
+ stream.flush();
+ stream.close();
success = true;
+
} catch (UnsupportedEncodingException e) {
logger.log(Level.SEVERE, "Should not happen", e);
} catch (TransformerConfigurationException e) {
@@ -324,7 +388,9 @@ public class KeywordSearchListsXML {
} 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);
+ logger.log(Level.SEVERE, "Error writing keyword lists XML: cannot write to file: " + xmlFile, e);
+ } catch (IOException e) {
+ logger.log(Level.SEVERE, "Error writing keyword lists XML: cannot write to file: " + xmlFile, e);
}
return success;
}
@@ -348,6 +414,27 @@ class KeywordSearchList {
this.keywords = keywords;
}
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final KeywordSearchList other = (KeywordSearchList) obj;
+ if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 5;
+ return hash;
+ }
+
String getName() {
return name;
}
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchTabsTopComponent.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchTabsTopComponent.java
index 2b25a7f8a6..4a02cc0d44 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchTabsTopComponent.java
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchTabsTopComponent.java
@@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.keywordsearch;
+import java.awt.Component;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
@@ -48,6 +49,8 @@ public final class KeywordSearchTabsTopComponent extends TopComponent implements
private Logger logger = Logger.getLogger(KeywordSearchTabsTopComponent.class.getName());
private PropertyChangeListener serverChangeListener;
+
+ public enum TABS{Simple, List, Lists};
public KeywordSearchTabsTopComponent() {
initComponents();
@@ -89,8 +92,9 @@ public final class KeywordSearchTabsTopComponent extends TopComponent implements
// End of variables declaration//GEN-END:variables
private void initTabs() {
- tabs.addTab("Simple", null, new KeywordSearchSimpleTopComponent(), "Single keyword or regex search");
- tabs.addTab("List", null, new KeywordSearchListTopComponent(), "Search for or load a saved list of keywords.");
+ tabs.addTab(TABS.Simple.name(), null, new KeywordSearchSimpleTopComponent(), "Single keyword or regex search");
+ tabs.addTab(TABS.List.name(), null, new KeywordSearchListTopComponent(), "Search for or load a saved list of keywords.");
+ tabs.addTab(TABS.Lists.name(), null, new KeywordSearchListImportExportTopComponent(), "Manage (import, export, delete) lists of keywords.");
}
@Override
@@ -100,7 +104,7 @@ public final class KeywordSearchTabsTopComponent extends TopComponent implements
@Override
public void componentClosed() {
}
-
+
void writeProperties(java.util.Properties p) {
// better to version settings since initial version as advocated at
// http://wiki.apidesign.org/wiki/PropertyFiles