/*
* Autopsy Forensic Browser
*
* Copyright 2011-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.casemodule;
import java.awt.BorderLayout;
import java.awt.Component;
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.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.ListCellRenderer;
import javax.swing.event.DocumentEvent;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourceprocessors.RawDSProcessor;
/**
* visual component for the first panel of add image wizard. Allows the user to
* choose the data source type and then select the data source
*
*/
final class AddImageWizardChooseDataSourceVisual extends JPanel {
static final Logger logger = Logger.getLogger(AddImageWizardChooseDataSourceVisual.class.getName());
private AddImageWizardChooseDataSourcePanel wizPanel;
private JPanel currentPanel;
private Map<String, DataSourceProcessor> datasourceProcessorsMap = new HashMap<>();
List<String> coreDSPTypes = new ArrayList<>();
/**
* Creates new form AddImageVisualPanel1
*
* @param wizPanel corresponding WizardPanel to handle logic of wizard step
*/
AddImageWizardChooseDataSourceVisual(AddImageWizardChooseDataSourcePanel wizPanel) {
initComponents();
this.wizPanel = wizPanel;
customInit();
}
@SuppressWarnings({"rawtypes", "unchecked"})
private void customInit() {
typePanel.setLayout(new BorderLayout());
discoverDataSourceProcessors();
// set up the DSP type combobox
typeComboBox.removeAllItems();
Set<String> dspTypes = datasourceProcessorsMap.keySet();
// make a list of core DSPs
// ensure that the core DSPs are at the top and in a fixed order
coreDSPTypes.add(ImageDSProcessor.getType());
// Local disk processing is not allowed for multi-user cases
if (Case.getCurrentCase().getCaseType() != Case.CaseType.MULTI_USER_CASE) {
coreDSPTypes.add(LocalDiskDSProcessor.getType());
} else {
// remove LocalDiskDSProcessor from list of DSPs
datasourceProcessorsMap.remove(LocalDiskDSProcessor.getType());
}
coreDSPTypes.add(LocalFilesDSProcessor.getType());
coreDSPTypes.add(RawDSProcessor.getType());
for (String dspType : coreDSPTypes) {
typeComboBox.addItem(dspType);
}
// now add any addtional DSPs that haven't already been added
for (String dspType : dspTypes) {
if (!coreDSPTypes.contains(dspType)) {
typeComboBox.addItem(dspType);
}
}
typeComboBox.setRenderer(new ComboboxSeparatorRenderer(typeComboBox.getRenderer()) {
@Override
protected boolean addSeparatorAfter(JList list, Object value, int index) {
return (index == coreDSPTypes.size() - 1);
}
});
//add actionlistner to listen for change
ActionListener cbActionListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
dspSelectionChanged();
}
};
typeComboBox.addActionListener(cbActionListener);
typeComboBox.setSelectedIndex(0);
}
private void discoverDataSourceProcessors() {
for (DataSourceProcessor dsProcessor : Lookup.getDefault().lookupAll(DataSourceProcessor.class)) {
if (!datasourceProcessorsMap.containsKey(dsProcessor.getDataSourceType())) {
datasourceProcessorsMap.put(dsProcessor.getDataSourceType(), dsProcessor);
} else {
logger.log(Level.SEVERE, "discoverDataSourceProcessors(): A DataSourceProcessor already exists for type = {0}", dsProcessor.getDataSourceType()); //NON-NLS
}
}
}
private void dspSelectionChanged() {
// update the current panel to selection
currentPanel = getCurrentDSProcessor().getPanel();
updateCurrentPanel(currentPanel);
}
/**
* Changes the current panel to the given panel.
*
* @param panel instance of ImageTypePanel to change to
*/
@SuppressWarnings("deprecation")
private void updateCurrentPanel(JPanel panel) {
currentPanel = panel;
typePanel.removeAll();
typePanel.add(currentPanel, BorderLayout.CENTER);
typePanel.validate();
typePanel.repaint();
currentPanel.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString())) {
updateUI(null);
}
if (evt.getPropertyName().equals(DataSourceProcessor.DSP_PANEL_EVENT.FOCUS_NEXT.toString())) {
wizPanel.moveFocusToNext();
}
}
});
updateUI(null);
}
/**
* Returns the currently selected DS Processor
*
* @return DataSourceProcessor the DataSourceProcessor corresponding to the
* data source type selected in the combobox
*/
protected DataSourceProcessor getCurrentDSProcessor() {
// get the type of the currently selected panel and then look up
// the correspodning DS Handler in the map
String dsType = (String) typeComboBox.getSelectedItem();
DataSourceProcessor dsProcessor = datasourceProcessorsMap.get(dsType);
return dsProcessor;
}
/**
* Returns the name of the this panel. This name will be shown on the left
* panel of the "Add Image" wizard panel.
*
* @return name the name of this panel
*/
@Override
public String getName() {
return NbBundle.getMessage(this.getClass(), "AddImageWizardChooseDataSourceVisual.getName.text");
}
/**
* 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.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
buttonGroup1 = new javax.swing.ButtonGroup();
jLabel2 = new javax.swing.JLabel();
inputPanel = new javax.swing.JPanel();
typeTabel = new javax.swing.JLabel();
typePanel = new javax.swing.JPanel();
typeComboBox = new javax.swing.JComboBox<String>();
org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(AddImageWizardChooseDataSourceVisual.class, "AddImageWizardChooseDataSourceVisual.jLabel2.text")); // NOI18N
setPreferredSize(new java.awt.Dimension(588, 328));
org.openide.awt.Mnemonics.setLocalizedText(typeTabel, org.openide.util.NbBundle.getMessage(AddImageWizardChooseDataSourceVisual.class, "AddImageWizardChooseDataSourceVisual.typeTabel.text")); // NOI18N
typePanel.setMinimumSize(new java.awt.Dimension(0, 65));
typePanel.setPreferredSize(new java.awt.Dimension(521, 65));
javax.swing.GroupLayout typePanelLayout = new javax.swing.GroupLayout(typePanel);
typePanel.setLayout(typePanelLayout);
typePanelLayout.setHorizontalGroup(
typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 548, Short.MAX_VALUE)
);
typePanelLayout.setVerticalGroup(
typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 225, Short.MAX_VALUE)
);
javax.swing.GroupLayout inputPanelLayout = new javax.swing.GroupLayout(inputPanel);
inputPanel.setLayout(inputPanelLayout);
inputPanelLayout.setHorizontalGroup(
inputPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(inputPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(inputPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(inputPanelLayout.createSequentialGroup()
.addComponent(typeTabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(typeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 292, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 119, Short.MAX_VALUE))
.addComponent(typePanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 548, Short.MAX_VALUE))
.addContainerGap())
);
inputPanelLayout.setVerticalGroup(
inputPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(inputPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(inputPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(typeTabel)
.addComponent(typeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(typePanel, javax.swing.GroupLayout.PREFERRED_SIZE, 225, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
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(inputPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(6, 6, 6)
.addComponent(inputPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(44, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.ButtonGroup buttonGroup1;
private javax.swing.JPanel inputPanel;
private javax.swing.JLabel jLabel2;
private javax.swing.JComboBox<String> typeComboBox;
private javax.swing.JPanel typePanel;
private javax.swing.JLabel typeTabel;
// End of variables declaration//GEN-END:variables
/**
* The "listener" that updates the UI of this panel based on the changes of
* fields on this panel. This is also the method to check whether all the
* fields on this panel are correctly filled and decides whether to enable
* the "Next" button or not.
*
* @param e the document event
*/
public void updateUI(DocumentEvent e) {
// Enable the Next button if the current DSP panel is valid
this.wizPanel.enableNextButton(getCurrentDSProcessor().isPanelValid());
}
@SuppressWarnings("rawtypes")
public abstract class ComboboxSeparatorRenderer implements ListCellRenderer {
private ListCellRenderer delegate;
private JPanel separatorPanel = new JPanel(new BorderLayout());
private JSeparator separator = new JSeparator();
public ComboboxSeparatorRenderer(ListCellRenderer delegate) {
this.delegate = delegate;
}
@SuppressWarnings("unchecked")
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Component comp = delegate.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (index != -1 && addSeparatorAfter(list, value, index)) {
separatorPanel.removeAll();
separatorPanel.add(comp, BorderLayout.CENTER);
separatorPanel.add(separator, BorderLayout.SOUTH);
return separatorPanel;
} else {
return comp;
}
}
protected abstract boolean addSeparatorAfter(JList list, Object value, int index);
}
}