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