Handle empty tables.

Cleanup
This commit is contained in:
Ann Priestman 2019-06-06 10:26:02 -04:00
parent f8d02a3002
commit 3ef9746d26
5 changed files with 59 additions and 30 deletions

View File

@ -32,6 +32,7 @@ DataResultViewerTable.commentRenderer.noComment.toolTip=No comments found
DataResultViewerTable.commentRenderer.tagComment.toolTip=Comment exists on associated tag(s) DataResultViewerTable.commentRenderer.tagComment.toolTip=Comment exists on associated tag(s)
DataResultViewerTable.countRender.name=O DataResultViewerTable.countRender.name=O
DataResultViewerTable.countRender.toolTip=O(ccurrences) indicates the number of data sources containing the item in the Central Repository DataResultViewerTable.countRender.toolTip=O(ccurrences) indicates the number of data sources containing the item in the Central Repository
DataResultViewerTable.exportCSVButtonActionPerformed.empty=No data to export
DataResultViewerTable.firstColLbl=Name DataResultViewerTable.firstColLbl=Name
DataResultViewerTable.goToPageTextField.err=Invalid page number DataResultViewerTable.goToPageTextField.err=Invalid page number
# {0} - totalPages # {0} - totalPages

View File

@ -18,12 +18,11 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="outlineView" pref="904" max="32767" attributes="0"/> <Component id="outlineView" pref="904" max="32767" attributes="0"/>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Component id="exportCSVButton" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="pageLabel" min="-2" max="-2" attributes="0"/> <Component id="pageLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="pageNumLabel" min="-2" pref="53" max="-2" attributes="0"/> <Component id="pageNumLabel" min="-2" pref="53" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace min="-2" pref="14" max="-2" attributes="0"/>
<Component id="pagesLabel" min="-2" max="-2" attributes="0"/> <Component id="pagesLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="pagePrevButton" linkSize="1" min="-2" pref="16" max="-2" attributes="0"/> <Component id="pagePrevButton" linkSize="1" min="-2" pref="16" max="-2" attributes="0"/>
@ -33,7 +32,8 @@
<Component id="gotoPageLabel" min="-2" max="-2" attributes="0"/> <Component id="gotoPageLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="gotoPageTextField" min="-2" pref="33" max="-2" attributes="0"/> <Component id="gotoPageTextField" min="-2" pref="33" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="32767" attributes="0"/>
<Component id="exportCSVButton" min="-2" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>

View File

@ -77,6 +77,7 @@ import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo; import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo;
@ -177,6 +178,13 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
initializePagingSupport(); initializePagingSupport();
/*
* Disable the CSV export button for the common properties results
*/
if (this instanceof org.sleuthkit.autopsy.commonpropertiessearch.CommonAttributesSearchResultsViewerTable) {
exportCSVButton.setEnabled(false);
}
/* /*
* Configure the child OutlineView (explorer view) component. * Configure the child OutlineView (explorer view) component.
*/ */
@ -1352,12 +1360,11 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 904, Short.MAX_VALUE) .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 904, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(exportCSVButton) .addContainerGap()
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(pageLabel) .addComponent(pageLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGap(14, 14, 14)
.addComponent(pagesLabel) .addComponent(pagesLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
@ -1367,7 +1374,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
.addComponent(gotoPageLabel) .addComponent(gotoPageLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap()) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(exportCSVButton))
); );
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {pageNextButton, pagePrevButton}); layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {pageNextButton, pagePrevButton});
@ -1407,8 +1415,15 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
pagingSupport.gotoPage(); pagingSupport.gotoPage();
}//GEN-LAST:event_gotoPageTextFieldActionPerformed }//GEN-LAST:event_gotoPageTextFieldActionPerformed
@NbBundle.Messages({"DataResultViewerTable.exportCSVButtonActionPerformed.empty=No data to export"
})
private void exportCSVButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportCSVButtonActionPerformed private void exportCSVButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportCSVButtonActionPerformed
org.sleuthkit.autopsy.directorytree.ExportCSVAction.saveNodesToCSV(java.util.Arrays.asList(rootNode.getChildren().getNodes()), this); Node currentRoot = this.getExplorerManager().getRootContext();
if (currentRoot != null && currentRoot.getChildren().getNodesCount() > 0) {
org.sleuthkit.autopsy.directorytree.ExportCSVAction.saveNodesToCSV(java.util.Arrays.asList(currentRoot.getChildren().getNodes()), this);
} else {
MessageNotifyUtil.Message.info(Bundle.DataResultViewerTable_exportCSVButtonActionPerformed_empty());
}
}//GEN-LAST:event_exportCSVButtonActionPerformed }//GEN-LAST:event_exportCSVButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables

View File

@ -10,6 +10,7 @@ DirectoryTreeTopComponent.componentOpened.groupDataSources.text=This case contai
DirectoryTreeTopComponent.componentOpened.groupDataSources.title=Group by data source? DirectoryTreeTopComponent.componentOpened.groupDataSources.title=Group by data source?
DirectoryTreeTopComponent.emptyMimeNode.text=Data not available. Run file type identification module. DirectoryTreeTopComponent.emptyMimeNode.text=Data not available. Run file type identification module.
DirectoryTreeTopComponent.resultsView.title=Listing DirectoryTreeTopComponent.resultsView.title=Listing
ExportCSV.saveNodesToCSV.empty=No data to export
# {0} - Output file # {0} - Output file
ExportCSV.saveNodesToCSV.fileExists=File {0} already exists ExportCSV.saveNodesToCSV.fileExists=File {0} already exists
ExportCSV.saveNodesToCSV.noCurrentCase=No open case available ExportCSV.saveNodesToCSV.noCurrentCase=No open case available

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2013-2018 Basis Technology Corp. * Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -22,7 +22,6 @@ import java.awt.Component;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.io.File; import java.io.File;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@ -31,10 +30,8 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
@ -48,14 +45,8 @@ import org.openide.util.NbBundle;
import org.openide.util.Utilities; import org.openide.util.Utilities;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.FileUtil;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.datamodel.ContentUtils.ExtractFscContentVisitor;
import org.sleuthkit.autopsy.datamodel.FileNode;
import org.sleuthkit.datamodel.AbstractFile;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.nodes.Node.PropertySet; import org.openide.nodes.Node.PropertySet;
import org.openide.nodes.Node.Property; import org.openide.nodes.Node.Property;
@ -76,6 +67,12 @@ public final class ExportCSVAction extends AbstractAction {
// node in the array returns a reference to the same action object from Node.getActions(boolean). // node in the array returns a reference to the same action object from Node.getActions(boolean).
private static ExportCSVAction instance; private static ExportCSVAction instance;
/**
* Get an instance of the Action. See above for why
* the class is a singleton.
*
* @return the instance
*/
public static synchronized ExportCSVAction getInstance() { public static synchronized ExportCSVAction getInstance() {
if (null == instance) { if (null == instance) {
instance = new ExportCSVAction(); instance = new ExportCSVAction();
@ -104,20 +101,28 @@ public final class ExportCSVAction extends AbstractAction {
saveNodesToCSV(selectedNodes, (Component)e.getSource()); saveNodesToCSV(selectedNodes, (Component)e.getSource());
} }
/**
* Save the selected nodes to a CSV file
*
* @param nodesToExport the nodes to save
* @param component
*/
@NbBundle.Messages({ @NbBundle.Messages({
"# {0} - Output file", "# {0} - Output file",
"ExportCSV.saveNodesToCSV.fileExists=File {0} already exists", "ExportCSV.saveNodesToCSV.fileExists=File {0} already exists",
"ExportCSV.saveNodesToCSV.noCurrentCase=No open case available"}) "ExportCSV.saveNodesToCSV.noCurrentCase=No open case available",
"ExportCSV.saveNodesToCSV.empty=No data to export"})
public static void saveNodesToCSV(Collection<? extends Node> nodesToExport, Component component) { public static void saveNodesToCSV(Collection<? extends Node> nodesToExport, Component component) {
if (nodesToExport.isEmpty()) { if (nodesToExport.isEmpty()) {
MessageNotifyUtil.Message.info(Bundle.ExportCSV_saveNodesToCSV_empty());
return; return;
} }
try { try {
// Set up the file chooser with a default name and either the Export
// folder or the last used folder.
String fileName = getDefaultOutputFileName(nodesToExport.iterator().next().getParentNode()); String fileName = getDefaultOutputFileName(nodesToExport.iterator().next().getParentNode());
JFileChooser fileChooser = new JFileChooser(); JFileChooser fileChooser = new JFileChooser();
fileChooser.setCurrentDirectory(new File(getExportDirectory(Case.getCurrentCaseThrows()))); fileChooser.setCurrentDirectory(new File(getExportDirectory(Case.getCurrentCaseThrows())));
fileChooser.setSelectedFile(new File(fileName)); fileChooser.setSelectedFile(new File(fileName));
@ -126,15 +131,18 @@ public final class ExportCSVAction extends AbstractAction {
int returnVal = fileChooser.showSaveDialog(component); int returnVal = fileChooser.showSaveDialog(component);
if (returnVal == JFileChooser.APPROVE_OPTION) { if (returnVal == JFileChooser.APPROVE_OPTION) {
// Get the file name, appending .csv if necessary
File selectedFile = fileChooser.getSelectedFile(); File selectedFile = fileChooser.getSelectedFile();
if (!selectedFile.getName().endsWith(".csv")) { // NON-NLS if (!selectedFile.getName().endsWith(".csv")) { // NON-NLS
selectedFile = new File(selectedFile.toString() + ".csv"); // NON-NLS selectedFile = new File(selectedFile.toString() + ".csv"); // NON-NLS
} }
// Save the directory used for next time
updateExportDirectory(selectedFile.getParent(), Case.getCurrentCaseThrows()); updateExportDirectory(selectedFile.getParent(), Case.getCurrentCaseThrows());
if (selectedFile.exists()) { if (selectedFile.exists()) {
logger.log(Level.SEVERE, "File {0} already exists", selectedFile.getAbsolutePath()); //NON-NLS logger.log(Level.SEVERE, "File {0} already exists", selectedFile.getAbsolutePath()); //NON-NLS
MessageNotifyUtil.Message.info(Bundle.ExportCSV_actionPerformed_fileExists(selectedFile)); MessageNotifyUtil.Message.info(Bundle.ExportCSV_saveNodesToCSV_fileExists(selectedFile));
return; return;
} }
@ -142,7 +150,7 @@ public final class ExportCSVAction extends AbstractAction {
writer.execute(); writer.execute();
} }
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
JOptionPane.showMessageDialog(component, Bundle.ExportCSV_actionPerformed_noCurrentCase()); JOptionPane.showMessageDialog(component, Bundle.ExportCSV_saveNodesToCSV_noCurrentCase());
logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
} }
} }
@ -165,13 +173,10 @@ public final class ExportCSVAction extends AbstractAction {
String parentName = prop.getValue().toString(); String parentName = prop.getValue().toString();
// Strip off the count (if present) // Strip off the count (if present)
System.out.println("parentName (raw) : " + parentName);
parentName = parentName.replaceAll("\\([0-9]+\\)$", ""); parentName = parentName.replaceAll("\\([0-9]+\\)$", "");
System.out.println("parentName (after paren regex) : " + parentName);
// Strip out any invalid characters // Strip out any invalid characters
parentName = parentName.replaceAll("[\\\\/:*?\"<>|]", "_"); parentName = parentName.replaceAll("[\\\\/:*?\"<>|]", "_");
System.out.println("parentName (after char regex) : " + parentName);
return parentName + " " + dateStr; return parentName + " " + dateStr;
} catch (IllegalAccessException | InvocationTargetException ex) { } catch (IllegalAccessException | InvocationTargetException ex) {
@ -190,7 +195,7 @@ public final class ExportCSVAction extends AbstractAction {
* *
* @return The export directory path. * @return The export directory path.
*/ */
private static String getExportDirectory(Case openCase) { // TODO sync private static String getExportDirectory(Case openCase) {
String caseExportPath = openCase.getExportDirectory(); String caseExportPath = openCase.getExportDirectory();
if (userDefinedExportPath == null) { if (userDefinedExportPath == null) {
@ -306,6 +311,13 @@ public final class ExportCSVAction extends AbstractAction {
return null; return null;
} }
/**
* Convert list of values to a comma separated string.
*
* @param values Values to convert
*
* @return values as CSV
*/
private String listToCSV(List<String> values) { private String listToCSV(List<String> values) {
return "\"" + String.join("\",\"", values) + "\"\n"; return "\"" + String.join("\",\"", values) + "\"\n";
} }