/*
* Autopsy Forensic Browser
*
* Copyright 2011-2015 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.keywordsearch;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import javax.swing.JCheckBox;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import org.openide.util.NbBundle;
import org.openide.util.actions.SystemAction;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
/**
* Viewer panel widget for keyword lists that is used in the ingest config and
* options area.
*/
class DropdownListSearchPanel extends KeywordSearchPanel {
private static final Logger logger = Logger.getLogger(DropdownListSearchPanel.class.getName());
private static DropdownListSearchPanel instance;
private XmlKeywordSearchList loader;
private final KeywordListsTableModel listsTableModel;
private final KeywordsTableModel keywordsTableModel;
private ActionListener searchAddListener;
private boolean ingestRunning;
/**
* Creates new form DropdownListSearchPanel
*/
private DropdownListSearchPanel() {
listsTableModel = new KeywordListsTableModel();
keywordsTableModel = new KeywordsTableModel();
initComponents();
customizeComponents();
}
static synchronized DropdownListSearchPanel getDefault() {
if (instance == null) {
instance = new DropdownListSearchPanel();
}
return instance;
}
private void customizeComponents() {
listsTable.setTableHeader(null);
listsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
//customize column witdhs
final int leftWidth = leftPane.getPreferredSize().width;
TableColumn column;
for (int i = 0; i < listsTable.getColumnCount(); i++) {
column = listsTable.getColumnModel().getColumn(i);
if (i == 0) {
column.setPreferredWidth(((int) (leftWidth * 0.10)));
column.setCellRenderer(new LeftCheckBoxRenderer());
} else {
column.setPreferredWidth(((int) (leftWidth * 0.89)));
}
}
final int rightWidth = rightPane.getPreferredSize().width;
for (int i = 0; i < keywordsTable.getColumnCount(); i++) {
column = keywordsTable.getColumnModel().getColumn(i);
if (i == 0) {
column.setPreferredWidth(((int) (rightWidth * 0.60)));
} else {
column.setPreferredWidth(((int) (rightWidth * 0.38)));
}
}
loader = XmlKeywordSearchList.getCurrent();
listsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
ListSelectionModel listSelectionModel = (ListSelectionModel) e.getSource();
if (!listSelectionModel.isSelectionEmpty()) {
int index = listSelectionModel.getMinSelectionIndex();
KeywordList list = listsTableModel.getListAt(index);
keywordsTableModel.resync(list);
} else {
keywordsTableModel.deleteAll();
}
}
});
ingestRunning = IngestManager.getInstance().isIngestRunning();
updateComponents();
IngestManager.getInstance().addIngestJobEventListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
Object source = evt.getSource();
if (source instanceof String && ((String) source).equals("LOCAL")) { //NON-NLS
EventQueue.invokeLater(() -> {
ingestRunning = IngestManager.getInstance().isIngestRunning();
updateComponents();
});
}
}
});
searchAddListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (ingestRunning) {
SearchRunner.getInstance().addKeywordListsToAllJobs(listsTableModel.getSelectedLists());
logger.log(Level.INFO, "Submitted enqueued lists to ingest"); //NON-NLS
} else {
searchAction(e);
}
}
};
searchAddButton.addActionListener(searchAddListener);
}
private void updateComponents() {
ingestRunning = IngestManager.getInstance().isIngestRunning();
if (ingestRunning) {
searchAddButton.setText(NbBundle.getMessage(this.getClass(), "KeywordSearchListsViewerPanel.initIngest.addIngestTitle"));
searchAddButton.setToolTipText(NbBundle.getMessage(this.getClass(), "KeywordSearchListsViewerPanel.initIngest.addIngestMsg"));
} else {
searchAddButton.setText(NbBundle.getMessage(this.getClass(), "KeywordSearchListsViewerPanel.initIngest.searchIngestTitle"));
searchAddButton.setToolTipText(NbBundle.getMessage(this.getClass(), "KeywordSearchListsViewerPanel.initIngest.addIdxSearchMsg"));
}
listsTableModel.resync();
updateIngestIndexLabel();
}
private void updateIngestIndexLabel() {
if (ingestRunning) {
ingestIndexLabel.setText(NbBundle.getMessage(this.getClass(), "KeywordSearchListsViewerPanel.initIngest.ongoingIngestMsg", filesIndexed));
} else {
ingestIndexLabel.setText(NbBundle.getMessage(this.getClass(), "KeywordSearchListsViewerPanel.initIngest.fileIndexCtMsg", filesIndexed));
}
}
@Override
protected void postFilesIndexedChange() {
updateIngestIndexLabel();
}
/**
* Force resync the data view
*/
void resync() {
listsTableModel.resync();
}
/**
* 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.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jSplitPane1 = new javax.swing.JSplitPane();
leftPane = new javax.swing.JScrollPane();
listsTable = new javax.swing.JTable();
rightPane = new javax.swing.JScrollPane();
keywordsTable = new javax.swing.JTable();
manageListsButton = new javax.swing.JButton();
searchAddButton = new javax.swing.JButton();
ingestIndexLabel = new javax.swing.JLabel();
setFont(getFont().deriveFont(getFont().getStyle() & ~java.awt.Font.BOLD, 11));
jSplitPane1.setFont(leftPane.getFont());
leftPane.setFont(leftPane.getFont().deriveFont(leftPane.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
leftPane.setMinimumSize(new java.awt.Dimension(150, 23));
listsTable.setBackground(new java.awt.Color(240, 240, 240));
listsTable.setFont(listsTable.getFont().deriveFont(listsTable.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
listsTable.setModel(listsTableModel);
listsTable.setShowHorizontalLines(false);
listsTable.setShowVerticalLines(false);
listsTable.getTableHeader().setReorderingAllowed(false);
leftPane.setViewportView(listsTable);
jSplitPane1.setLeftComponent(leftPane);
rightPane.setFont(rightPane.getFont().deriveFont(rightPane.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
keywordsTable.setBackground(new java.awt.Color(240, 240, 240));
keywordsTable.setFont(keywordsTable.getFont().deriveFont(keywordsTable.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
keywordsTable.setModel(keywordsTableModel);
keywordsTable.setGridColor(new java.awt.Color(153, 153, 153));
rightPane.setViewportView(keywordsTable);
jSplitPane1.setRightComponent(rightPane);
manageListsButton.setFont(manageListsButton.getFont().deriveFont(manageListsButton.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
manageListsButton.setText(org.openide.util.NbBundle.getMessage(DropdownListSearchPanel.class, "KeywordSearchListsViewerPanel.manageListsButton.text")); // NOI18N
manageListsButton.setToolTipText(org.openide.util.NbBundle.getMessage(DropdownListSearchPanel.class, "KeywordSearchListsViewerPanel.manageListsButton.toolTipText")); // NOI18N
manageListsButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
manageListsButtonActionPerformed(evt);
}
});
searchAddButton.setFont(searchAddButton.getFont().deriveFont(searchAddButton.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
searchAddButton.setText(org.openide.util.NbBundle.getMessage(DropdownListSearchPanel.class, "KeywordSearchListsViewerPanel.searchAddButton.text")); // NOI18N
searchAddButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
searchAddButtonActionPerformed(evt);
}
});
ingestIndexLabel.setFont(ingestIndexLabel.getFont().deriveFont(ingestIndexLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 10));
ingestIndexLabel.setText(org.openide.util.NbBundle.getMessage(DropdownListSearchPanel.class, "KeywordSearchListsViewerPanel.ingestIndexLabel.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(searchAddButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 220, Short.MAX_VALUE)
.addComponent(manageListsButton))
.addGroup(layout.createSequentialGroup()
.addComponent(ingestIndexLabel)
.addGap(0, 317, Short.MAX_VALUE)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(jSplitPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 268, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 7, Short.MAX_VALUE)
.addComponent(ingestIndexLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(manageListsButton)
.addComponent(searchAddButton))
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
private void manageListsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_manageListsButtonActionPerformed
SystemAction.get(KeywordSearchConfigurationAction.class).performAction();
}//GEN-LAST:event_manageListsButtonActionPerformed
private void searchAddButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchAddButtonActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_searchAddButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel ingestIndexLabel;
private javax.swing.JSplitPane jSplitPane1;
private javax.swing.JTable keywordsTable;
private javax.swing.JScrollPane leftPane;
private javax.swing.JTable listsTable;
private javax.swing.JButton manageListsButton;
private javax.swing.JScrollPane rightPane;
private javax.swing.JButton searchAddButton;
// End of variables declaration//GEN-END:variables
private void searchAction(ActionEvent e) {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try {
search();
} finally {
setCursor(null);
}
}
@Override
List<KeywordList> getKeywordLists() {
return listsTableModel.getSelectedListsL();
}
void addSearchButtonActionListener(ActionListener al) {
searchAddButton.addActionListener(al);
}
private class KeywordListsTableModel extends AbstractTableModel {
//data
private final XmlKeywordSearchList listsHandle = XmlKeywordSearchList.getCurrent();
private final List<ListTableEntry> listData = new ArrayList<>();
@Override
public int getColumnCount() {
return 2;
}
@Override
public int getRowCount() {
return listData.size();
}
@Override
public String getColumnName(int column) {
String ret = null;
switch (column) {
case 0:
ret = NbBundle.getMessage(this.getClass(), "KeywordSearch.selectedColLbl");
break;
case 1:
ret = NbBundle.getMessage(this.getClass(), "KeywordSearch.nameColLbl");
break;
default:
break;
}
return ret;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
Object ret = null;
ListTableEntry entry = null;
//iterate until row
Iterator<ListTableEntry> it = listData.iterator();
for (int i = 0; i <= rowIndex; ++i) {
entry = it.next();
}
if (null != entry) {
switch (columnIndex) {
case 0:
ret = (Object) entry.selected;
break;
case 1:
ret = (Object) entry.name;
break;
default:
break;
}
}
return ret;
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return (columnIndex == 0 && !ingestRunning);
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (columnIndex == 0) {
ListTableEntry entry = null;
Iterator<ListTableEntry> it = listData.iterator();
for (int i = 0; i <= rowIndex; i++) {
entry = it.next();
}
if (entry != null) {
entry.selected = (Boolean) aValue;
if (ingestRunning) {
//updateUseForIngest(getListAt(rowIndex), (Boolean) aValue);
}
}
}
}
@Override
public Class<?> getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
List<String> getAllLists() {
List<String> ret = new ArrayList<>();
for (ListTableEntry e : listData) {
ret.add(e.name);
}
return ret;
}
KeywordList getListAt(int rowIndex) {
return listsHandle.getList((String) getValueAt(rowIndex, 1));
}
List<String> getSelectedLists() {
List<String> ret = new ArrayList<>();
for (ListTableEntry e : listData) {
if (e.selected) {
ret.add(e.name);
}
}
return ret;
}
List<KeywordList> getSelectedListsL() {
List<KeywordList> ret = new ArrayList<>();
for (String s : getSelectedLists()) {
ret.add(listsHandle.getList(s));
}
return ret;
}
boolean listExists(String list) {
List<String> all = getAllLists();
return all.contains(list);
}
//resync model from handle, then update table
void resync() {
listData.clear();
addLists(listsHandle.getListsL());
fireTableDataChanged();
}
//add lists to the model
private void addLists(List<KeywordList> lists) {
for (KeywordList list : lists) {
if (!listExists(list.getName())) {
listData.add(new ListTableEntry(list, ingestRunning));
}
}
}
//single model entry
private class ListTableEntry implements Comparable<ListTableEntry> {
String name;
Boolean selected;
ListTableEntry(KeywordList list, boolean ingestRunning) {
this.name = list.getName();
if (ingestRunning) {
this.selected = list.getUseForIngest();
} else {
this.selected = false;
}
}
@Override
public int compareTo(ListTableEntry e) {
return this.name.compareTo(e.name);
}
}
}
private class KeywordsTableModel extends AbstractTableModel {
List<KeywordTableEntry> listData = new ArrayList<>();
@Override
public int getRowCount() {
return listData.size();
}
@Override
public int getColumnCount() {
return 2;
}
@Override
public String getColumnName(int column) {
String ret = null;
switch (column) {
case 0:
ret = NbBundle.getMessage(this.getClass(), "KeywordSearch.nameColLbl");
break;
case 1:
ret = NbBundle.getMessage(this.getClass(), "KeywordSearch.typeColLbl");
break;
default:
break;
}
return ret;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
Object ret = null;
KeywordTableEntry entry = null;
//iterate until row
Iterator<KeywordTableEntry> it = listData.iterator();
for (int i = 0; i <= rowIndex; ++i) {
entry = it.next();
}
if (null != entry) {
switch (columnIndex) {
case 0:
ret = (Object) entry.name;
break;
case 1:
ret = (Object) entry.keywordType;
break;
default:
break;
}
}
return ret;
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
}
@Override
public Class<?> getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
void resync(KeywordList list) {
listData.clear();
for (Keyword k : list.getKeywords()) {
listData.add(new KeywordTableEntry(k));
}
fireTableDataChanged();
}
void deleteAll() {
listData.clear();
fireTableDataChanged();
}
//single model entry
private class KeywordTableEntry implements Comparable<KeywordTableEntry> {
String name;
String keywordType;
KeywordTableEntry(Keyword keyword) {
this.name = keyword.getSearchTerm();
this.keywordType = keyword.getSearchTermType();
}
@Override
public int compareTo(KeywordTableEntry e) {
return this.name.compareTo(e.name);
}
}
}
private class LeftCheckBoxRenderer extends JCheckBox implements TableCellRenderer {
@Override
public Component getTableCellRendererComponent(
JTable table, Object value,
boolean isSelected, boolean hasFocus,
int row, int column) {
this.setHorizontalAlignment(JCheckBox.CENTER);
this.setVerticalAlignment(JCheckBox.CENTER);
setEnabled(!ingestRunning);
boolean selected = (Boolean) table.getModel().getValueAt(row, 0);
setSelected(selected);
if (isSelected) {
setBackground(listsTable.getSelectionBackground());
} else {
setBackground(listsTable.getBackground());
}
return this;
}
}
}