/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.modules.interestingitems;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.TreeMap;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
/**
* Ingest job settings panel for interesting files identifier ingest modules.
*/
final class FilesIdentifierIngestJobSettingsPanel extends IngestModuleIngestJobSettingsPanel implements Observer {
@Messages({
"FilesIdentifierIngestJobSettingsPanel.updateError=Error updating interesting files sets settings file.",
"FilesIdentifierIngestJobSettingsPanel.getError=Error getting interesting files sets from settings file."
})
private final FilesSetsTableModel tableModel;
// This map of interesting interesting files set names to files sets is used
// both to sort interesting files sets by name and to detect when a new
// files set is defined, so that the new set can be enabled by default.
private TreeMap<String, FilesSet> filesSetSnapshot;
/**
* A factory method that avoids publishing the "this" reference from the
* constructor.
*
* @return An instance of the ingest job settings panel interesting files
* identifier ingest modules.
*/
static FilesIdentifierIngestJobSettingsPanel makePanel(FilesIdentifierIngestJobSettings settings) {
FilesIdentifierIngestJobSettingsPanel panel = new FilesIdentifierIngestJobSettingsPanel(settings);
// Observe the interesting item definitions manager for changes to the
// interesting file set definitions. This is used to keep this panel in
// synch with changes made using the global settings/option panel for
// this module.
InterestingItemDefsManager.getInstance().addObserver(panel);
return panel;
}
/**
* Constructs an ingest job settings panel for interesting files identifier
* ingest modules.
*/
private FilesIdentifierIngestJobSettingsPanel(FilesIdentifierIngestJobSettings settings) {
initComponents();
/*
* Make a table row object for each interesting files set, bundling the
* set with an enabled flag. The files sets are loaded into a tree map
* so they are sorted by name. The keys set also serves as a cache of
* set names so that new sets can be easily detected in the override of
* Observer.update().
*/
List<FilesSetRow> filesSetRows = new ArrayList<>();
try {
this.filesSetSnapshot = new TreeMap<>(InterestingItemDefsManager.getInstance().getInterestingFilesSets());
} catch (InterestingItemDefsManager.InterestingItemDefsManagerException ex) {
MessageNotifyUtil.Message.error(Bundle.FilesIdentifierIngestJobSettingsPanel_getError());
this.filesSetSnapshot = new TreeMap<>();
}
for (FilesSet set : this.filesSetSnapshot.values()) {
filesSetRows.add(new FilesSetRow(set, settings.interestingFilesSetIsEnabled(set.getName())));
}
// Make a table model to manage the row objects.
this.tableModel = new FilesSetsTableModel(filesSetRows);
// Set up the table component that presents the table model that allows
// users to enable and disable interesting files set definitions for an
// ingest job.
this.filesSetTable.setModel(tableModel);
this.filesSetTable.setTableHeader(null);
this.filesSetTable.setRowSelectionAllowed(false);
final int width = this.filesSetScrollPane.getPreferredSize().width;
this.filesSetTable.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
TableColumn column;
for (int i = 0; i < this.filesSetTable.getColumnCount(); i++) {
column = this.filesSetTable.getColumnModel().getColumn(i);
if (i == 0) {
column.setPreferredWidth(((int) (width * 0.07)));
} else {
column.setPreferredWidth(((int) (width * 0.92)));
}
}
}
@Override
public IngestModuleIngestJobSettings getSettings() {
List<String> enabledInterestingFilesSets = new ArrayList<>();
List<String> disabledInterestingFilesSets = new ArrayList<>();
for (FilesSetRow rowModel : this.tableModel.filesSetRows) {
if (rowModel.isEnabled()) {
enabledInterestingFilesSets.add(rowModel.getFilesSet().getName());
} else {
disabledInterestingFilesSets.add(rowModel.getFilesSet().getName());
}
}
return new FilesIdentifierIngestJobSettings(enabledInterestingFilesSets, disabledInterestingFilesSets);
}
@Override
public void update(Observable o, Object arg
) {
// Get the user's current enabled/disabled settings.
FilesIdentifierIngestJobSettings settings = (FilesIdentifierIngestJobSettings) this.getSettings();
// Refresh the view of the interesting files set definitions.
List<FilesSetRow> rowModels = new ArrayList<>();
TreeMap<String, FilesSet> newFilesSetSnapshot;
try {
newFilesSetSnapshot = new TreeMap<>(InterestingItemDefsManager.getInstance().getInterestingFilesSets());
} catch (InterestingItemDefsManager.InterestingItemDefsManagerException ex) {
MessageNotifyUtil.Message.error(Bundle.FilesIdentifierIngestJobSettingsPanel_updateError());
return;
}
for (FilesSet set : newFilesSetSnapshot.values()) {
if (this.filesSetSnapshot.keySet().contains(set.getName())) {
// Preserve the current enabled/diabled state of the set.
rowModels.add(new FilesSetRow(set, settings.interestingFilesSetIsEnabled(set.getName())));
} else {
// New sets are enabled by default.
rowModels.add(new FilesSetRow(set, true));
}
}
this.tableModel.resetTableData(rowModels);
// Cache the snapshot so it will be avaialble for the next update.
this.filesSetSnapshot = newFilesSetSnapshot;
}
/**
* Table model for a JTable component that allows users to enable and
* disable interesting files set definitions for an ingest job.
*/
private final static class FilesSetsTableModel extends AbstractTableModel {
private List<FilesSetRow> filesSetRows;
/**
* Constructs a table model for a JTable component that allows users to
* enable and disable interesting files set definitions for an ingest
* job.
*
* @param filesSetRows A collection of row objects that bundles an
* interesting files set with an enabled flag
*/
FilesSetsTableModel(List<FilesSetRow> filesSetRows) {
this.filesSetRows = filesSetRows;
}
/**
* Refreshes the table with a new set of rows.
*
* @param filesSetRows A collection of row objects that bundles an
* interesting files set with an enabled flag
*/
void resetTableData(List<FilesSetRow> filesSetRows) {
this.filesSetRows = filesSetRows;
this.fireTableDataChanged();
}
@Override
public int getRowCount() {
return this.filesSetRows.size();
}
@Override
public int getColumnCount() {
return 2;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (columnIndex == 0) {
return this.filesSetRows.get(rowIndex).isEnabled();
} else {
return this.filesSetRows.get(rowIndex).getFilesSet().getName();
}
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return (columnIndex == 0);
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (columnIndex == 0) {
this.filesSetRows.get(rowIndex).setEnabled((Boolean) aValue);
}
}
@Override
public Class<?> getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
}
/**
* Bundles an interesting files set with an enabled flag.
*/
private final static class FilesSetRow {
private final FilesSet set;
private boolean enabled;
FilesSetRow(FilesSet set, boolean enabled) {
this.set = set;
this.enabled = enabled;
}
FilesSet getFilesSet() {
return this.set;
}
boolean isEnabled() {
return this.enabled;
}
void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
/**
* 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() {
filesSetScrollPane = new javax.swing.JScrollPane();
filesSetTable = new javax.swing.JTable();
setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(FilesIdentifierIngestJobSettingsPanel.class, "FilesIdentifierIngestJobSettingsPanel.border.title"))); // NOI18N
filesSetTable.setBackground(new java.awt.Color(240, 240, 240));
filesSetTable.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
},
new String [] {
}
));
filesSetTable.setShowHorizontalLines(false);
filesSetTable.setShowVerticalLines(false);
filesSetScrollPane.setViewportView(filesSetTable);
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()
.addComponent(filesSetScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 242, Short.MAX_VALUE)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addComponent(filesSetScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 165, Short.MAX_VALUE)
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JScrollPane filesSetScrollPane;
private javax.swing.JTable filesSetTable;
// End of variables declaration//GEN-END:variables
}