Implemented an options panel for setting user external viewer rules

This commit is contained in:
Sophie Mori 2016-09-16 17:03:15 -04:00
parent fb7c184a30
commit ec432bbb47
13 changed files with 732 additions and 130 deletions

View File

@ -158,6 +158,7 @@ CueBannerPanel.title.text=Open Recent Case
GeneralFilter.rawImageDesc.text=Raw Images (*.img, *.dd, *.001, *.aa, *.raw, *.bin)
GeneralFilter.encaseImageDesc.text=Encase Images (*.e01)
GeneralFilter.virtualMachineImageDesc.text=Virtual Machines (*.vmdk, *.vhd)
GeneralFilter.executableDesc.text=Executables (*.exe)
ImageDSProcessor.dsType.text=Image or VM File
ImageDSProcessor.allDesc.text=All Supported Types
ImageFilePanel.moduleErr=Module Error

View File

@ -42,6 +42,9 @@ public class GeneralFilter extends FileFilter {
public static final String VIRTUAL_MACHINE_DESC = NbBundle.getMessage(GeneralFilter.class,
"GeneralFilter.virtualMachineImageDesc.text");
public static final List<String> EXECUTABLE_EXTS = Arrays.asList(new String[]{".exe"}); //NON-NLS
public static final String EXECUTABLE_DESC = NbBundle.getMessage(GeneralFilter.class, "GeneralFilter.executableDesc.text");
private List<String> extensions;
private String desc;

View File

@ -5,15 +5,173 @@
*/
package org.sleuthkit.autopsy.directorytree;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
/**
*
* @author smori
* A dialog for adding or editing an external viewer rule
*/
public class AddRuleDialog extends JDialog {
private ExternalViewerRule rule;
private final AddRulePanel addRulePanel;
private BUTTON_PRESSED result;
private JButton saveButton;
private JButton closeButton;
enum BUTTON_PRESSED {
OK, CANCEL;
}
/**
* Creates a dialog for creating an external viewer rule
*/
@Messages({"AddRuleDialog.title=External Viewer Rule"})
AddRuleDialog() {
super(new JFrame(Bundle.AddRuleDialog_title()), Bundle.AddRuleDialog_title(), true);
addRulePanel = new AddRulePanel();
this.display();
}
/**
* Creates a dialog for editing an external viewer rule
*
* @param rule ExternalViewerRule to be edited
*/
AddRuleDialog(ExternalViewerRule rule) {
super(new JFrame(Bundle.AddRuleDialog_title()), Bundle.AddRuleDialog_title(), true);
addRulePanel = new AddRulePanel(rule);
this.display();
}
/**
* Displays the add external viewer rule dialog.
*/
@NbBundle.Messages({
"AddRuleDialog.addButton.title=Save",
"AddRuleDialog.cancelButton.title=Cancel"})
private void display() {
setLayout(new BorderLayout());
/**
* Center the dialog.
*/
Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
int width = this.getSize().width;
int height = this.getSize().height;
setLocation((screenDimension.width - width) / 2, (screenDimension.height - height) / 2);
add(this.addRulePanel, BorderLayout.PAGE_START);
// Add a save button.
saveButton = new JButton(Bundle.AddRuleDialog_addButton_title());
saveButton.addActionListener((ActionEvent e) -> {
doButtonAction(true);
});
// Add a close button.
closeButton = new JButton(Bundle.AddRuleDialog_cancelButton_title());
closeButton.addActionListener((ActionEvent e) -> {
doButtonAction(false);
});
// Put the buttons in their own panel, under the settings panel.
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));
buttonPanel.add(new javax.swing.Box.Filler(new Dimension(10, 10), new Dimension(10, 10), new Dimension(10, 10)));
buttonPanel.add(saveButton);
buttonPanel.add(new javax.swing.Box.Filler(new Dimension(10, 10), new Dimension(10, 10), new Dimension(10, 10)));
buttonPanel.add(closeButton);
add(buttonPanel, BorderLayout.LINE_START);
/**
* Add a handler for when the dialog window is closed directly,
* bypassing the buttons.
*/
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
doButtonAction(false);
}
});
/**
* Add a listener to enable the save button when a text field in the
* AddRulePanel is changed or modified.
*/
this.addRulePanel.addPropertyChangeListener((PropertyChangeEvent evt) -> {
if (evt.getPropertyName().equals(AddRulePanel.EVENT.CHANGED.toString())) {
enableSaveButton();
}
});
enableSaveButton();
/**
* Show the dialog.
*/
pack();
setResizable(false);
setVisible(true);
getRootPane().setDefaultButton(saveButton);
}
/**
* Performs actions based on whether the save button was pressed or not.
*
* @param savePressed Whether save was pressed.
*/
private void doButtonAction(boolean savePressed) {
if (savePressed) {
ExternalViewerRule ruleFromPanel = addRulePanel.getRule();
if (null != ruleFromPanel) {
this.rule = ruleFromPanel;
this.result = BUTTON_PRESSED.OK;
setVisible(false);
}
} else {
this.rule = null;
this.result = BUTTON_PRESSED.CANCEL;
setVisible(false);
}
}
/**
* Gets the external viewer rule of this dialog
*
* @return The external viewer rule
*/
ExternalViewerRule getRule() {
return rule;
}
/**
* Gets the button pressed on this dialog
*
* @return The button pressed to close the dialog
*/
BUTTON_PRESSED getResult() {
return result;
}
/**
* Enables save button once addRulePanel's fields have text in them. Maps
* enter key to the save button.
*/
private void enableSaveButton() {
this.saveButton.setEnabled(addRulePanel.hasFields());
getRootPane().setDefaultButton(saveButton);
}
}

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.3" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
@ -16,13 +16,84 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="400" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="nameTextField" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="nameLabel" min="-2" max="-2" attributes="0"/>
<Component id="exePathLabel" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="80" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="exePathTextField" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="browseButton" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="300" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="nameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="nameTextField" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="exePathLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="exePathTextField" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="browseButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="nameLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="AddRulePanel.nameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="nameTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="AddRulePanel.nameTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="exePathLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="AddRulePanel.exePathLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="exePathTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="AddRulePanel.exePathTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="browseButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="AddRulePanel.browseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseButtonActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -5,17 +5,137 @@
*/
package org.sleuthkit.autopsy.directorytree;
import java.util.logging.Level;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.GeneralFilter;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
/**
*
* @author smori
* Panel found in an AddRuleDialog
*/
public class AddRulePanel extends javax.swing.JPanel {
class AddRulePanel extends javax.swing.JPanel {
private static final Logger logger = Logger.getLogger(AddRulePanel.class.getName());
private final JFileChooser fc = new JFileChooser();
private static final GeneralFilter exeFilter = new GeneralFilter(GeneralFilter.EXECUTABLE_EXTS, GeneralFilter.EXECUTABLE_DESC);
enum EVENT {
CHANGED
}
/**
* Creates new form AddRulePanel
*/
public AddRulePanel() {
AddRulePanel() {
initComponents();
fc.setDragEnabled(false);
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
fc.setMultiSelectionEnabled(false);
fc.setFileFilter(exeFilter);
addListener();
}
/**
* Creates new form AddRulePanel if the user is editing a rule. Loads
* information of the rule being edited.
*
* @param rule to be edited
*/
AddRulePanel(ExternalViewerRule rule) {
this();
nameTextField.setText(rule.getName());
exePathTextField.setText(rule.getExePath());
addListener();
}
/**
* Allows listeners for when the name or exePath text fields are modified.
*/
private void addListener() {
nameTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void changedUpdate(DocumentEvent e) {
fire();
}
@Override
public void removeUpdate(DocumentEvent e) {
fire();
}
@Override
public void insertUpdate(DocumentEvent e) {
fire();
}
private void fire() {
firePropertyChange(EVENT.CHANGED.toString(), null, null);
}
});
exePathTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void changedUpdate(DocumentEvent e) {
fire();
}
@Override
public void removeUpdate(DocumentEvent e) {
fire();
}
@Override
public void insertUpdate(DocumentEvent e) {
fire();
}
private void fire() {
firePropertyChange(EVENT.CHANGED.toString(), null, null);
}
});
}
/**
* Check if the text fields are empty or not.
*
* @return true if neither of the text fields are empty
*/
boolean hasFields() {
return !exePathTextField.getText().isEmpty() && !nameTextField.getText().isEmpty();
}
/**
* Returns the ExternalViewerRule created from input text. Returns null if
* the name is not a valid MIME type (as defined by both autopsy and the
* user, checked through FileTypeDetector) or in the form of a valid
* extension.
*
* @return ExternalViewerRule or null
*/
ExternalViewerRule getRule() {
String name = nameTextField.getText();
FileTypeDetector detector;
try {
detector = new FileTypeDetector();
} catch (FileTypeDetector.FileTypeDetectorInitException ex) {
logger.log(Level.WARNING, "Couldn't create file type detector for file ext mismatch settings.", ex);
return null;
}
// Regex for MIME: ^[-\\w]+/[-\\w]+$
if (name.isEmpty() || (!detector.isDetectable(name) && !name.matches("^\\.\\w+$"))) {
JOptionPane.showMessageDialog(null,
NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.JOptionPane.invalidExtOrMIME.message"),
NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.JOptionPane.invalidExtOrMIME.title"),
JOptionPane.ERROR_MESSAGE);
return null;
}
String exePath = exePathTextField.getText();
if (exePath.isEmpty()) {
JOptionPane.showMessageDialog(null,
NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.JOptionPane.invalidExePath.message"),
NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.JOptionPane.invalidExePath.title"),
JOptionPane.ERROR_MESSAGE);
return null;
}
return new ExternalViewerRule(name, exePath);
}
/**
@ -27,19 +147,77 @@ public class AddRulePanel extends javax.swing.JPanel {
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
nameLabel = new javax.swing.JLabel();
nameTextField = new javax.swing.JTextField();
exePathLabel = new javax.swing.JLabel();
exePathTextField = new javax.swing.JTextField();
browseButton = new javax.swing.JButton();
org.openide.awt.Mnemonics.setLocalizedText(nameLabel, org.openide.util.NbBundle.getMessage(AddRulePanel.class, "AddRulePanel.nameLabel.text")); // NOI18N
nameTextField.setText(org.openide.util.NbBundle.getMessage(AddRulePanel.class, "AddRulePanel.nameTextField.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(exePathLabel, org.openide.util.NbBundle.getMessage(AddRulePanel.class, "AddRulePanel.exePathLabel.text")); // NOI18N
exePathTextField.setText(org.openide.util.NbBundle.getMessage(AddRulePanel.class, "AddRulePanel.exePathTextField.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(AddRulePanel.class, "AddRulePanel.browseButton.text")); // NOI18N
browseButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
browseButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 400, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(nameTextField)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(nameLabel)
.addComponent(exePathLabel))
.addGap(0, 80, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addComponent(exePathTextField)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(browseButton)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 300, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(nameLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(nameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(exePathLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(exePathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(browseButton))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
int returnState = fc.showOpenDialog(this);
if (returnState == JFileChooser.APPROVE_OPTION) {
String path = fc.getSelectedFile().getPath();
exePathTextField.setText(path);
}
}//GEN-LAST:event_browseButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton browseButton;
private javax.swing.JLabel exePathLabel;
private javax.swing.JTextField exePathTextField;
private javax.swing.JLabel nameLabel;
private javax.swing.JTextField nameTextField;
// End of variables declaration//GEN-END:variables
}

View File

@ -99,6 +99,18 @@ ExternalViewerGlobalSettingsPanel.newRuleButton.text=New Rule
ExternalViewerGlobalSettingsPanel.editRuleButton.text=Edit Rule
ExternalViewerGlobalSettingsPanel.deleteRuleButton.toolTipText=
ExternalViewerGlobalSettingsPanel.deleteRuleButton.text=Delete Rule
ExternalViewerGlobalSettingsPanel.externalViewerTitleLabel.text=jLabel1
ExternalViewerGlobalSettingsPanel.ruleListLabel.text=jLabel2
ExternalViewerGlobalSettingsPanel.exePathLabel.text=jLabel1
ExternalViewerGlobalSettingsPanel.externalViewerTitleLabel.text=Add your custom rules for external viewers
ExternalViewerGlobalSettingsPanel.ruleListLabel.text=MIME type and extensions
ExternalViewerGlobalSettingsPanel.exePathLabel.text=Program associated with this MIME type or extension
AddRulePanel.nameLabel.text=MIME type or extension
AddRulePanel.nameTextField.text=
AddRulePanel.browseButton.text=Browse
AddRulePanel.exePathLabel.text=Path of the program to use for files with this type or extension
AddRulePanel.exePathTextField.text=
ExternalViewerGlobalSettingsPanel.JOptionPane.invalidExtOrMIME.message=Extension or MIME type is invalid. Extensions should start with a "." character.
ExternalViewerGlobalSettingsPanel.JOptionPane.invalidExtOrMIME.title=Invalid Name
ExternalViewerGlobalSettingsPanel.JOptionPane.invalidExePath.message=The path to the program executable is invalid
ExternalViewerGlobalSettingsPanel.JOptionPane.invalidExePath.title=Invalid Path
ExternalViewerGlobalSettingsPanel.exePathNameLabel.text=No MIME type or extension currently selected
ExternalViewerGlobalSettingsPanel.JOptionPane.ruleAlreadyExists.message=A rule already exists with this MIME type or extension. Please edit that one.
ExternalViewerGlobalSettingsPanel.JOptionPane.ruleAlreadyExists.title=Rule not added

View File

@ -33,14 +33,15 @@ import org.sleuthkit.autopsy.datamodel.ContentUtils;
/**
* Extracts a File object to a temporary file in the case directory, and then
* tries to open it in the user's system with the default associated
* application.
* tries to open it in the user's system with the default or user specified
* associated application.
*/
public class ExternalViewerAction extends AbstractAction {
private final static Logger logger = Logger.getLogger(ExternalViewerAction.class.getName());
private org.sleuthkit.datamodel.AbstractFile fileObject;
final static String[] EXECUTABLE_EXT = {".exe", ".dll", ".com", ".bat", ".msi", ".reg", ".scr"}; //NON-NLS
private String fileObjectExt;
public ExternalViewerAction(String title, Node fileNode) {
super(title);
@ -53,12 +54,15 @@ public class ExternalViewerAction extends AbstractAction {
boolean isExecutable = false;
if (extPos != -1) {
String extension = fileName.substring(extPos, fileName.length()).toLowerCase();
fileObjectExt = extension;
for (int i = 0; i < EXECUTABLE_EXT.length; ++i) {
if (EXECUTABLE_EXT[i].equals(extension)) {
isExecutable = true;
break;
}
}
} else {
fileObjectExt = "";
}
// no point opening a file if it's empty, and java doesn't know how to
@ -89,13 +93,31 @@ public class ExternalViewerAction extends AbstractAction {
logger.log(Level.WARNING, "Can't save to temporary file.", ex); //NON-NLS
}
try {
Desktop.getDesktop().open(tempFile);
} catch (IOException ex) {
logger.log(Level.WARNING, "Could not find a viewer for the given file: " + tempFile.getName(), ex); //NON-NLS
JOptionPane.showMessageDialog(null, Bundle.ExternalViewerAction_actionPerformed_failure_message(), Bundle.ExternalViewerAction_actionPerformed_failure_title(), JOptionPane.ERROR_MESSAGE);
/**
* Check if the file MIME type or extension exists in the user defined
* settings. Otherwise open with the default associated application.
*/
String exePath = ExternalViewerRulesManager.getInstance().getExePathForName(fileObject.getMIMEType());
if (exePath.equals("")) {
exePath = ExternalViewerRulesManager.getInstance().getExePathForName(fileObjectExt);
}
if (!exePath.equals("")) {
Runtime runtime = Runtime.getRuntime();
String[] s = new String[]{exePath, tempFile.getAbsolutePath()};
try {
runtime.exec(s);
} catch (IOException ex) {
logger.log(Level.WARNING, "Could not open the specified viewer for the given file: " + tempFile.getName(), ex); //NON-NLS
JOptionPane.showMessageDialog(null, Bundle.ExternalViewerAction_actionPerformed_failure_message(), Bundle.ExternalViewerAction_actionPerformed_failure_title(), JOptionPane.ERROR_MESSAGE);
}
} else {
try {
Desktop.getDesktop().open(tempFile);
} catch (IOException ex) {
logger.log(Level.WARNING, "Could not find a viewer for the given file: " + tempFile.getName(), ex); //NON-NLS
JOptionPane.showMessageDialog(null, Bundle.ExternalViewerAction_actionPerformed_failure_message(), Bundle.ExternalViewerAction_actionPerformed_failure_title(), JOptionPane.ERROR_MESSAGE);
}
}
// delete the file on exit
tempFile.deleteOnExit();
}

View File

@ -37,7 +37,7 @@
<EmptySpace max="-2" attributes="0"/>
<Component id="externalViewerTitleLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jSplitPane1" max="32767" attributes="0"/>
<Component id="jSplitPane1" pref="458" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
@ -53,7 +53,7 @@
</Component>
<Container class="javax.swing.JSplitPane" name="jSplitPane1">
<Properties>
<Property name="dividerLocation" type="int" value="400"/>
<Property name="dividerLocation" type="int" value="350"/>
<Property name="dividerSize" type="int" value="1"/>
</Properties>
@ -69,10 +69,13 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="exePathLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="284" max="32767" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="exePathLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="exePathNameLabel" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="114" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -81,7 +84,9 @@
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="exePathLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="435" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="exePathNameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="411" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -94,6 +99,13 @@
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="exePathNameLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="ExternalViewerGlobalSettingsPanel.exePathNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="rulesPanel">
@ -112,7 +124,7 @@
<Component id="ruleListLabel" alignment="1" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="1" max="-2" attributes="0">
<Component id="jScrollPane1" alignment="0" max="32767" attributes="0"/>
<Component id="rulesScrollPane" alignment="0" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="newRuleButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
@ -121,7 +133,7 @@
<Component id="deleteRuleButton" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="0" pref="68" max="32767" attributes="0"/>
<EmptySpace min="0" pref="18" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
@ -134,7 +146,7 @@
<EmptySpace max="-2" attributes="0"/>
<Component id="ruleListLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jScrollPane1" min="-2" pref="387" max="-2" attributes="0"/>
<Component id="rulesScrollPane" min="-2" pref="387" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="newRuleButton" alignment="3" min="-2" max="-2" attributes="0"/>
@ -154,7 +166,7 @@
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
<Container class="javax.swing.JScrollPane" name="rulesScrollPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
@ -164,13 +176,7 @@
<Component class="javax.swing.JList" name="rulesList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
<StringArray count="5">
<StringItem index="0" value="Item 1"/>
<StringItem index="1" value="Item 2"/>
<StringItem index="2" value="Item 3"/>
<StringItem index="3" value="Item 4"/>
<StringItem index="4" value="Item 5"/>
</StringArray>
<StringArray count="0"/>
</Property>
</Properties>
<AuxValues>

View File

@ -1,11 +1,12 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.sleuthkit.autopsy.directorytree;
import java.util.logging.Level;
import java.util.ArrayList;
import java.util.Collections;
import javax.swing.DefaultListModel;
import javax.swing.JOptionPane;
import javax.swing.event.ListSelectionEvent;
@ -16,8 +17,9 @@ import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
*
* @author smori
* An options panel for the user to create, edit, and delete associations for
* opening files in external viewers. Users can associate a file by either MIME
* type or by extension to an executable file.
*/
public class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel implements OptionsPanel {
@ -33,23 +35,25 @@ public class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel implem
customizeComponents();
}
/**
* Initializes field variables. Adds a listener to the list of rules.
*/
private void customizeComponents() {
rulesListModel = new DefaultListModel<>();
rules = new ArrayList<>();
rulesList.setModel(rulesListModel);
rulesList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting() == false) {
if (rulesList.getSelectedIndex() == -1) {
clearRuleDetailsComponents();
clearExePath();
} else {
populateRuleDetailsComponents();
populateExePath();
}
}
}
});
}
/**
@ -65,9 +69,10 @@ public class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel implem
jSplitPane1 = new javax.swing.JSplitPane();
exePanel = new javax.swing.JPanel();
exePathLabel = new javax.swing.JLabel();
exePathNameLabel = new javax.swing.JLabel();
rulesPanel = new javax.swing.JPanel();
ruleListLabel = new javax.swing.JLabel();
jScrollPane1 = new javax.swing.JScrollPane();
rulesScrollPane = new javax.swing.JScrollPane();
rulesList = new javax.swing.JList<>();
newRuleButton = new javax.swing.JButton();
editRuleButton = new javax.swing.JButton();
@ -77,38 +82,39 @@ public class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel implem
org.openide.awt.Mnemonics.setLocalizedText(externalViewerTitleLabel, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.externalViewerTitleLabel.text")); // NOI18N
jSplitPane1.setDividerLocation(400);
jSplitPane1.setDividerLocation(350);
jSplitPane1.setDividerSize(1);
org.openide.awt.Mnemonics.setLocalizedText(exePathLabel, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.exePathLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(exePathNameLabel, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.exePathNameLabel.text")); // NOI18N
javax.swing.GroupLayout exePanelLayout = new javax.swing.GroupLayout(exePanel);
exePanel.setLayout(exePanelLayout);
exePanelLayout.setHorizontalGroup(
exePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(exePanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(exePathLabel)
.addContainerGap(284, Short.MAX_VALUE))
.addGroup(exePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(exePathLabel)
.addComponent(exePathNameLabel))
.addContainerGap(114, Short.MAX_VALUE))
);
exePanelLayout.setVerticalGroup(
exePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(exePanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(exePathLabel)
.addContainerGap(435, Short.MAX_VALUE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(exePathNameLabel)
.addContainerGap(411, Short.MAX_VALUE))
);
jSplitPane1.setRightComponent(exePanel);
org.openide.awt.Mnemonics.setLocalizedText(ruleListLabel, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.ruleListLabel.text")); // NOI18N
rulesList.setModel(new javax.swing.AbstractListModel<ExternalViewerRule>() {
String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" };
public int getSize() { return strings.length; }
public Object getElementAt(int i) { return strings[i]; }
});
jScrollPane1.setViewportView(rulesList);
rulesScrollPane.setViewportView(rulesList);
newRuleButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/add16.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(newRuleButton, org.openide.util.NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.newRuleButton.text")); // NOI18N
@ -145,14 +151,14 @@ public class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel implem
.addComponent(ruleListLabel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(rulesPanelLayout.createSequentialGroup()
.addGroup(rulesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
.addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(rulesScrollPane, javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, rulesPanelLayout.createSequentialGroup()
.addComponent(newRuleButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(editRuleButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(deleteRuleButton)))
.addGap(0, 68, Short.MAX_VALUE)))
.addGap(0, 18, Short.MAX_VALUE)))
.addContainerGap())
);
rulesPanelLayout.setVerticalGroup(
@ -161,7 +167,7 @@ public class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel implem
.addContainerGap()
.addComponent(ruleListLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 387, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(rulesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 387, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(rulesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(newRuleButton)
@ -189,7 +195,7 @@ public class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel implem
.addContainerGap()
.addComponent(externalViewerTitleLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jSplitPane1)
.addComponent(jSplitPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 458, Short.MAX_VALUE)
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
@ -198,9 +204,18 @@ public class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel implem
AddRuleDialog dialog = new AddRuleDialog();
AddRuleDialog.BUTTON_PRESSED result = dialog.getResult();
if (result == AddRuleDialog.BUTTON_PRESSED.OK) {
rules.add(dialog.getRule());
updateRulesListModel();
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
ExternalViewerRule newRule = dialog.getRule();
// Only allow one association for each MIME type or extension.
if (rules.contains(newRule)) {
JOptionPane.showMessageDialog(null,
NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.JOptionPane.ruleAlreadyExists.message"),
NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.JOptionPane.ruleAlreadyExists.title"),
JOptionPane.ERROR_MESSAGE);
} else {
rules.add(dialog.getRule());
updateRulesListModel();
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}
}//GEN-LAST:event_newRuleButtonActionPerformed
@ -210,9 +225,18 @@ public class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel implem
AddRuleDialog.BUTTON_PRESSED result = dialog.getResult();
if (result == AddRuleDialog.BUTTON_PRESSED.OK) {
rules.remove(selected);
rules.add(selected, dialog.getRule());
updateRulesListModel();
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
ExternalViewerRule newRule = dialog.getRule();
// Only allow one association for each MIME type or extension.
if (rules.contains(newRule)) {
JOptionPane.showMessageDialog(null,
NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.JOptionPane.ruleAlreadyExists.message"),
NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.JOptionPane.ruleAlreadyExists.title"),
JOptionPane.ERROR_MESSAGE);
} else {
rules.add(selected, dialog.getRule());
updateRulesListModel();
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}
}//GEN-LAST:event_editRuleButtonActionPerformed
@ -220,63 +244,82 @@ public class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel implem
ExternalViewerRule rule = rulesList.getSelectedValue();
rules.remove(rule);
updateRulesListModel();
if (!rulesListModel.isEmpty()) {
rulesList.setSelectedIndex(0);
}
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_deleteRuleButtonActionPerformed
@Override
public void store() {
try {
ExternalViewerRulesManager.getInstance().setUserRules(rules);
} catch (ExternalViewerRulesManager.CustomRulesException ex) {
logger.log(Level.SEVERE, "Failed to set custom rules", ex);
JOptionPane.showMessageDialog(null,
NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.saveFileTypes.errorMessage"),
NbBundle.getMessage(ExternalViewerGlobalSettingsPanel.class, "ExternalViewerGlobalSettingsPanel.JOptionPane.storeFailed.title"),
JOptionPane.ERROR_MESSAGE);
}
ExternalViewerRulesManager.getInstance().setUserRules(rules);
}
@Override
public void load() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
rules = ExternalViewerRulesManager.getInstance().getUserRules();
updateRulesListModel();
if (!rulesListModel.isEmpty()) {
rulesList.setSelectedIndex(0);
}
enableButtons();
}
/**
* Sets the list model for the file types list component.
* Enable edit and delete buttons if there is a rule selected.
*/
private void enableButtons() {
boolean ruleIsSelected = rulesList.getSelectedIndex() != -1;
editRuleButton.setEnabled(ruleIsSelected);
deleteRuleButton.setEnabled(ruleIsSelected);
}
/**
* Sets the list model for the rules list component, sorted by the MIME
* type or extension alphabetically.
*/
private void updateRulesListModel() {
rulesListModel.clear();
Collections.sort(rules, (ExternalViewerRule rule1, ExternalViewerRule rule2) -> {
return rule1.getName().compareTo(rule2.getName());
});
for (ExternalViewerRule rule : rules) {
rulesListModel.addElement(rule);
}
if (!rulesListModel.isEmpty()) {
rulesList.setSelectedIndex(0);
}
}
private void populateRuleDetailsComponents() {
/**
* Fills in the .exe file path label if a rule is selected.
*/
private void populateExePath() {
ExternalViewerRule rule = rulesList.getSelectedValue();
exePathLabel.setText(rule.getExePath());
if (rule != null) {
exePathNameLabel.setText(rule.getExePath());
}
enableButtons();
}
private void clearRuleDetailsComponents() {
/**
* Clears the .exe file path label.
*/
private void clearExePath() {
rulesList.clearSelection();
exePathLabel.setText("");
exePathNameLabel.setText("");
enableButtons();
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton deleteRuleButton;
private javax.swing.JButton editRuleButton;
private javax.swing.JPanel exePanel;
private javax.swing.JLabel exePathLabel;
private javax.swing.JLabel exePathNameLabel;
private javax.swing.JLabel externalViewerTitleLabel;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JSplitPane jSplitPane1;
private javax.swing.JButton newRuleButton;
private javax.swing.JLabel ruleListLabel;
private javax.swing.JList<ExternalViewerRule> rulesList;
private javax.swing.JPanel rulesPanel;
private javax.swing.JScrollPane rulesScrollPane;
// End of variables declaration//GEN-END:variables
}

View File

@ -2,7 +2,7 @@
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
*/
package org.sleuthkit.autopsy.directorytree;
import java.beans.PropertyChangeEvent;
@ -13,6 +13,9 @@ import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.HelpCtx;
import org.openide.util.Lookup;
/**
* Controller for the ExternalViewerGlobalSettingsPanel
*/
@OptionsPanelController.TopLevelRegistration(
categoryName = "#OptionsCategory_Name_ExternalViewer",
iconBase = "org/sleuthkit/autopsy/modules/filetypeid/user-defined-file-types-settings.png",
@ -77,12 +80,9 @@ public final class ExternalViewerOptionsPanelController extends OptionsPanelCont
private ExternalViewerGlobalSettingsPanel getPanel() {
if (panel == null) {
panel = new ExternalViewerGlobalSettingsPanel();
panel.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(OptionsPanelController.PROP_CHANGED)) {
changed();
}
panel.addPropertyChangeListener((PropertyChangeEvent evt) -> {
if (evt.getPropertyName().equals(OptionsPanelController.PROP_CHANGED)) {
changed();
}
});
}

View File

@ -5,14 +5,23 @@
*/
package org.sleuthkit.autopsy.directorytree;
import java.util.Objects;
/**
*
* @author smori
* An object that represents an association between a MIME type or extension
* name to a user defined executable.
*/
class ExternalViewerRule {
private String name;
private String exePath;
private final String name;
private final String exePath;
/**
* Creates a new ExternalViewerRule
*
* @param name MIME type or extension
* @param exePath Absolute path of the exe file
*/
ExternalViewerRule(String name, String exePath) {
this.name = name;
this.exePath = exePath;
@ -26,12 +35,30 @@ class ExternalViewerRule {
return exePath;
}
void editName(String newName) {
name = newName;
@Override
public String toString() {
return name;
}
void editExePath(String newExePath) {
exePath = newExePath;
/**
* Only one association is allowed per MIME type or extension, so rules are
* equal if the names are the same.
*/
@Override
public boolean equals(Object other) {
if (other != null && other instanceof ExternalViewerRule) {
ExternalViewerRule that = (ExternalViewerRule) other;
if (this.getName().equals(that.getName())) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
int hash = 7;
hash = 67 * hash + Objects.hashCode(this.name);
return hash;
}
}

View File

@ -5,10 +5,91 @@
*/
package org.sleuthkit.autopsy.directorytree;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
/**
*
* @author smori
* Manager for user's external viewer rules, used by the options panel and the
* ExternalViewerAction. Reads from and writes to a preferences file.
*/
public class ExternalViewerRulesManager {
private static final String RULES_SETTINGS_NAME = "ExternalViewerRules"; //NON-NLS
private static final String RULES_SETTINGS_KEY = "Rules"; //NON-NLS
private static ExternalViewerRulesManager instance;
private List<ExternalViewerRule> userRules = new ArrayList<>();
/**
* Gets the singleton manager of the external viewer rules defined by users.
*
* @return The external viewer rules manager singleton.
*/
synchronized static ExternalViewerRulesManager getInstance() {
if (instance == null) {
instance = new ExternalViewerRulesManager();
instance.loadUserDefinedRules();
}
return instance;
}
private ExternalViewerRulesManager() {
}
/**
* Loads user defined rules from the configuration settings file.
*/
private void loadUserDefinedRules() {
String setting = ModuleSettings.getConfigSetting(RULES_SETTINGS_NAME, RULES_SETTINGS_KEY);
if (setting != null && !setting.isEmpty()) {
List<String> ruleTuples = Arrays.asList(setting.split("\\|"));
for (String ruleTuple : ruleTuples) {
String[] ruleParts = ruleTuple.split(">");
userRules.add(new ExternalViewerRule(ruleParts[0], ruleParts[1]));
}
}
}
/**
* Writes a list of ExternalViewerRule objects to a configuration settings
* file.
*
* @param rules to be written and saved.
*/
synchronized void setUserRules(List<ExternalViewerRule> rules) {
StringBuilder setting = new StringBuilder();
for (ExternalViewerRule rule : rules) {
if (setting.length() != 0) {
setting.append("|");
}
setting.append(rule.getName()).append(">");
setting.append(rule.getExePath());
}
ModuleSettings.setConfigSetting(RULES_SETTINGS_NAME, RULES_SETTINGS_KEY, setting.toString());
userRules = new ArrayList<>(rules);
}
/**
* @return a list of the user's rules as ExternalViewerRule objects
*/
synchronized List<ExternalViewerRule> getUserRules() {
// ExternalViewerRule objects are immutable
return new ArrayList<>(userRules);
}
/**
* Finds the executable path associated with a rule name (MIME type or
* extension). Returns an empty string if the rule name is not found.
* @param name of MIME type or extension.
* @return the associated executable absolute path.
*/
synchronized String getExePathForName(String name) {
for (ExternalViewerRule rule : userRules) {
if (rule.getName().equals(name)) {
return rule.getExePath();
}
}
return "";
}
}

View File

@ -372,7 +372,7 @@ final class CustomFileTypesManager {
return filesSetsSettings.getUserDefinedFileTypes();
}
} catch (IOException | ClassNotFoundException ex) {
throw new CustomFileTypesException(String.format("Failed to read ssettings from %s", filePath), ex); //NON-NLS
throw new CustomFileTypesException(String.format("Failed to read settings from %s", filePath), ex); //NON-NLS
}
}