19package org.sleuthkit.autopsy.directorytree;
21import com.fasterxml.jackson.databind.ObjectWriter;
22import com.fasterxml.jackson.databind.SequenceWriter;
23import com.fasterxml.jackson.dataformat.csv.CsvMapper;
24import com.fasterxml.jackson.dataformat.csv.CsvSchema;
25import java.awt.Component;
26import java.awt.event.ActionEvent;
28import java.lang.reflect.InvocationTargetException;
29import java.util.ArrayList;
30import java.util.Arrays;
31import java.util.Calendar;
32import java.util.Collection;
33import java.util.HashMap;
34import java.util.HashSet;
35import java.util.Iterator;
39import java.util.concurrent.ExecutionException;
40import java.util.logging.Level;
41import javax.swing.AbstractAction;
42import javax.swing.JFileChooser;
43import javax.swing.JOptionPane;
44import javax.swing.SwingWorker;
45import javax.swing.filechooser.FileNameExtensionFilter;
46import org.netbeans.api.progress.ProgressHandle;
47import org.openide.util.Cancellable;
48import org.openide.util.NbBundle;
49import org.openide.util.Utilities;
50import org.sleuthkit.autopsy.casemodule.Case;
51import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
52import org.sleuthkit.autopsy.coreutils.Logger;
53import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
54import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType;
55import org.openide.nodes.Node;
56import org.openide.nodes.Node.PropertySet;
57import org.openide.nodes.Node.Property;
58import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
96 @NbBundle.Messages({
"ExportCSV.title.text=Export Selected Rows to CSV"})
98 super(Bundle.ExportCSV_title_text());
110 Collection<? extends Node> selectedNodes = Utilities.actionsGlobalContext().lookupAll(Node.class);
121 "# {0} - Output file",
122 "ExportCSV.saveNodesToCSV.fileExists=File {0} already exists",
123 "ExportCSV.saveNodesToCSV.noCurrentCase=No open case available",
124 "ExportCSV.saveNodesToCSV.empty=No data to export"})
125 public static void saveNodesToCSV(Collection<? extends Node> nodesToExport, Component component) {
127 if (nodesToExport.isEmpty()) {
138 fileChooser.setSelectedFile(
new File(fileName));
139 fileChooser.setFileFilter(
new FileNameExtensionFilter(
"csv file",
"csv"));
141 int returnVal = fileChooser.showSaveDialog(component);
142 if (returnVal == JFileChooser.APPROVE_OPTION) {
145 File selectedFile = fileChooser.getSelectedFile();
146 if (!selectedFile.getName().endsWith(
".csv")) {
147 selectedFile =
new File(selectedFile.toString() +
".csv");
153 if (selectedFile.exists()) {
154 logger.log(Level.SEVERE,
"File {0} already exists", selectedFile.getAbsolutePath());
163 JOptionPane.showMessageDialog(component, Bundle.ExportCSV_saveNodesToCSV_noCurrentCase());
164 logger.log(Level.INFO,
"Exception while getting open case.", ex);
176 String dateStr = String.format(
"%1$tY%1$tm%1$te%1$tI%1$tM%1$tS", Calendar.getInstance());
178 if (parent !=
null) {
180 for (PropertySet
set : parent.getPropertySets()) {
181 for (Property<?> prop :
set.getProperties()) {
183 String parentName = prop.getValue().toString();
186 parentName = parentName.replaceAll(
"\\([0-9]+\\)$",
"");
189 parentName = parentName.replaceAll(
"[\\\\/:*?\"<>|]",
"_");
191 return parentName +
" " + dateStr;
192 }
catch (IllegalAccessException | InvocationTargetException ex) {
193 logger.log(Level.WARNING,
"Failed to get property set value as string", ex);
212 return caseExportPath;
216 if (file.exists() ==
false || file.isDirectory() ==
false) {
217 return caseExportPath;
244 private static class CSVWriter
extends SwingWorker<Object, Void> {
262 @NbBundle.Messages({
"CSVWriter.progress.extracting=Exporting to CSV file",
263 "CSVWriter.progress.cancelling=Cancelling"})
271 final String displayName = Bundle.CSVWriter_progress_extracting();
272 progress = ProgressHandle.createHandle(displayName,
new Cancellable() {
274 public boolean cancel() {
276 progress.setDisplayName(Bundle.CSVWriter_progress_cancelling());
284 if (this.isCancelled()) {
288 Set<String> columnHeaderStrs =
new HashSet<>();
289 List<CsvSchema.Column> columnHeaders =
new ArrayList<>();
290 int remainingRowsToSample = 0;
297 remainingRowsToSample++;
299 for (PropertySet ps: nd.getPropertySets()) {
300 for (Property prop: ps.getProperties()) {
301 if (!columnHeaderStrs.contains(prop.getDisplayName()) && !
columnsToSkip.contains(prop.getName())) {
302 columnHeaderStrs.add(prop.getDisplayName());
303 columnHeaders.add(
new CsvSchema.Column(columnIdx, prop.getDisplayName()));
310 if (this.isCancelled()) {
314 CsvSchema schema = CsvSchema.builder()
315 .addColumns(columnHeaders)
320 CsvMapper mapper =
new CsvMapper();
321 ObjectWriter writer = mapper.writerFor(Map.class).with(schema);
322 try (SequenceWriter seqWriter = writer.writeValues(
outputFile)) {
325 while (nodeIterator.hasNext()) {
326 if (this.isCancelled()) {
330 Map<String, Object> rowMap =
new HashMap<>();
331 Node node = (Node)nodeIterator.next();
332 for(PropertySet
set : node.getPropertySets()) {
333 for (Property<?> prop :
set.getProperties()) {
335 rowMap.put(prop.getDisplayName(), prop.getValue());
339 seqWriter.write(rowMap);
353 return original.replaceAll(
"\"",
"\\\\\"");
364 return "\"" + String.join(
"\",\"", values) +
"\"\n";
367 @NbBundle.Messages({
"CSVWriter.done.notifyMsg.error=Error exporting to CSV file",
368 "# {0} - Output file",
369 "CSVWriter.done.notifyMsg.success=Wrote to {0}"})
372 boolean msgDisplayed =
false;
375 }
catch (InterruptedException | ExecutionException ex) {
376 logger.log(Level.SEVERE,
"Fatal error during file extraction", ex);
379 }
catch (java.util.concurrent.CancellationException ex) {
383 if (!this.isCancelled() && !msgDisplayed) {
static Case getCurrentCaseThrows()
String getExportDirectory()
synchronized static Logger getLogger(String name)
static void info(String message)
String escapeQuotes(String original)
final Collection<? extends Node > nodesToExport
String listToCSV(List< String > values)
static final Logger logger
static final List< String > columnsToSkip
static ExportCSVAction instance
static final int COLUMN_SAMPLING_ROW_NUM
static void updateExportDirectory(String exportPath, Case openCase)
static void saveNodesToCSV(Collection<? extends Node > nodesToExport, Component component)
void actionPerformed(ActionEvent e)
static String getDefaultOutputFileName(Node parent)
static final Logger logger
static String userDefinedExportPath
static final String DEFAULT_FILENAME
static synchronized ExportCSVAction getInstance()
static final JFileChooserFactory chooserHelper
static String getExportDirectory(Case openCase)