From 3ef9746d2647f76d29d04dea6e21c6c7164f727d Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Thu, 6 Jun 2019 10:26:02 -0400 Subject: [PATCH] Handle empty tables. Cleanup --- .../corecomponents/Bundle.properties-MERGED | 1 + .../corecomponents/DataResultViewerTable.form | 10 ++-- .../corecomponents/DataResultViewerTable.java | 27 +++++++--- .../directorytree/Bundle.properties-MERGED | 1 + .../directorytree/ExportCSVAction.java | 50 ++++++++++++------- 5 files changed, 59 insertions(+), 30 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED index 43ceba39ba..b0e36da986 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED @@ -32,6 +32,7 @@ DataResultViewerTable.commentRenderer.noComment.toolTip=No comments found DataResultViewerTable.commentRenderer.tagComment.toolTip=Comment exists on associated tag(s) DataResultViewerTable.countRender.name=O 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.goToPageTextField.err=Invalid page number # {0} - totalPages diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form index 87e771695b..6aeda07298 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.form @@ -18,12 +18,11 @@ - - + - + - + @@ -33,7 +32,8 @@ - + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index eb5fad97f5..e6c1110038 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -77,6 +77,7 @@ import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo; @@ -176,6 +177,13 @@ public class DataResultViewerTable extends AbstractDataResultViewer { initComponents(); 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. @@ -1352,12 +1360,11 @@ public class DataResultViewerTable extends AbstractDataResultViewer { layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 904, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() - .addComponent(exportCSVButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addContainerGap() .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) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGap(14, 14, 14) .addComponent(pagesLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -1367,7 +1374,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer { .addComponent(gotoPageLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .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}); @@ -1407,8 +1415,15 @@ public class DataResultViewerTable extends AbstractDataResultViewer { pagingSupport.gotoPage(); }//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 - 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 // Variables declaration - do not modify//GEN-BEGIN:variables diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties-MERGED index 6ec6a0e76b..f70a374bc5 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/directorytree/Bundle.properties-MERGED @@ -10,6 +10,7 @@ DirectoryTreeTopComponent.componentOpened.groupDataSources.text=This case contai DirectoryTreeTopComponent.componentOpened.groupDataSources.title=Group by data source? DirectoryTreeTopComponent.emptyMimeNode.text=Data not available. Run file type identification module. DirectoryTreeTopComponent.resultsView.title=Listing +ExportCSV.saveNodesToCSV.empty=No data to export # {0} - Output file ExportCSV.saveNodesToCSV.fileExists=File {0} already exists ExportCSV.saveNodesToCSV.noCurrentCase=No open case available diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java index dce6244084..7fa4560afc 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExportCSVAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2018 Basis Technology Corp. + * Copyright 2019 Basis Technology Corp. * Contact: carrier sleuthkit org * * 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.io.File; import java.io.BufferedWriter; -import java.io.FileWriter; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.lang.reflect.InvocationTargetException; @@ -31,10 +30,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; -import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.AbstractAction; @@ -48,14 +45,8 @@ import org.openide.util.NbBundle; import org.openide.util.Utilities; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.coreutils.FileUtil; import org.sleuthkit.autopsy.coreutils.Logger; 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.PropertySet; 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). 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() { if (null == instance) { instance = new ExportCSVAction(); @@ -104,20 +101,28 @@ public final class ExportCSVAction extends AbstractAction { saveNodesToCSV(selectedNodes, (Component)e.getSource()); } + /** + * Save the selected nodes to a CSV file + * + * @param nodesToExport the nodes to save + * @param component + */ @NbBundle.Messages({ "# {0} - Output file", "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 nodesToExport, Component component) { if (nodesToExport.isEmpty()) { + MessageNotifyUtil.Message.info(Bundle.ExportCSV_saveNodesToCSV_empty()); return; } 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()); - JFileChooser fileChooser = new JFileChooser(); fileChooser.setCurrentDirectory(new File(getExportDirectory(Case.getCurrentCaseThrows()))); fileChooser.setSelectedFile(new File(fileName)); @@ -126,15 +131,18 @@ public final class ExportCSVAction extends AbstractAction { int returnVal = fileChooser.showSaveDialog(component); if (returnVal == JFileChooser.APPROVE_OPTION) { + // Get the file name, appending .csv if necessary File selectedFile = fileChooser.getSelectedFile(); if (!selectedFile.getName().endsWith(".csv")) { // NON-NLS selectedFile = new File(selectedFile.toString() + ".csv"); // NON-NLS } + + // Save the directory used for next time updateExportDirectory(selectedFile.getParent(), Case.getCurrentCaseThrows()); if (selectedFile.exists()) { 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; } @@ -142,7 +150,7 @@ public final class ExportCSVAction extends AbstractAction { writer.execute(); } } 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 } } @@ -165,13 +173,10 @@ public final class ExportCSVAction extends AbstractAction { String parentName = prop.getValue().toString(); // Strip off the count (if present) - System.out.println("parentName (raw) : " + parentName); parentName = parentName.replaceAll("\\([0-9]+\\)$", ""); - System.out.println("parentName (after paren regex) : " + parentName); // Strip out any invalid characters parentName = parentName.replaceAll("[\\\\/:*?\"<>|]", "_"); - System.out.println("parentName (after char regex) : " + parentName); return parentName + " " + dateStr; } catch (IllegalAccessException | InvocationTargetException ex) { @@ -190,7 +195,7 @@ public final class ExportCSVAction extends AbstractAction { * * @return The export directory path. */ - private static String getExportDirectory(Case openCase) { // TODO sync + private static String getExportDirectory(Case openCase) { String caseExportPath = openCase.getExportDirectory(); if (userDefinedExportPath == null) { @@ -306,6 +311,13 @@ public final class ExportCSVAction extends AbstractAction { return null; } + /** + * Convert list of values to a comma separated string. + * + * @param values Values to convert + * + * @return values as CSV + */ private String listToCSV(List values) { return "\"" + String.join("\",\"", values) + "\"\n"; }