19 package org.sleuthkit.autopsy.directorytree;
21 import com.fasterxml.jackson.databind.ObjectWriter;
22 import com.fasterxml.jackson.databind.SequenceWriter;
23 import com.fasterxml.jackson.dataformat.csv.CsvMapper;
24 import com.fasterxml.jackson.dataformat.csv.CsvSchema;
25 import java.awt.Component;
26 import java.awt.event.ActionEvent;
28 import java.lang.reflect.InvocationTargetException;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Calendar;
32 import java.util.Collection;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.Iterator;
36 import java.util.List;
39 import java.util.concurrent.ExecutionException;
40 import java.util.logging.Level;
41 import javax.swing.AbstractAction;
42 import javax.swing.JFileChooser;
43 import javax.swing.JOptionPane;
44 import javax.swing.SwingWorker;
45 import javax.swing.filechooser.FileNameExtensionFilter;
46 import org.netbeans.api.progress.ProgressHandle;
47 import org.openide.util.Cancellable;
48 import org.openide.util.NbBundle;
49 import org.openide.util.Utilities;
55 import org.openide.nodes.Node;
56 import org.openide.nodes.Node.PropertySet;
57 import org.openide.nodes.Node.Property;
87 if (null == instance) {
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()) {
136 JFileChooser fileChooser = chooserHelper.
getChooser();
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);
198 return DEFAULT_FILENAME +
" " + dateStr;
211 if (userDefinedExportPath == null) {
212 return caseExportPath;
215 File file =
new File(userDefinedExportPath);
216 if (file.exists() ==
false || file.isDirectory() ==
false) {
217 return caseExportPath;
234 userDefinedExportPath = null;
236 userDefinedExportPath = exportPath;
244 private static class CSVWriter extends SwingWorker<Object, Void> {
257 CSVWriter(Collection<? extends Node> nodesToExport, File outputFile) {
262 @NbBundle.Messages({
"CSVWriter.progress.extracting=Exporting to CSV file",
263 "CSVWriter.progress.cancelling=Cancelling"})
266 if (nodesToExport.isEmpty()) {
271 final String displayName = Bundle.CSVWriter_progress_extracting();
272 progress = ProgressHandle.createHandle(displayName,
new Cancellable() {
274 public boolean cancel() {
275 if (progress != null) {
276 progress.setDisplayName(Bundle.CSVWriter_progress_cancelling());
282 progress.switchToIndeterminate();
284 if (this.isCancelled()) {
288 Set<String> columnHeaderStrs =
new HashSet<>();
289 List<CsvSchema.Column> columnHeaders =
new ArrayList<>();
290 int remainingRowsToSample = 0;
292 for (Node nd: nodesToExport) {
294 if (remainingRowsToSample >= COLUMN_SAMPLING_ROW_NUM) {
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)) {
324 Iterator<?> nodeIterator = nodesToExport.iterator();
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()) {
334 if (!columnsToSkip.contains(prop.getName())) {
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 ExportCSVAction instance
static final JFileChooserFactory chooserHelper
static String getDefaultOutputFileName(Node parent)
static final int COLUMN_SAMPLING_ROW_NUM
static final String DEFAULT_FILENAME
static final Logger logger
static synchronized ExportCSVAction getInstance()
final Collection<?extends Node > nodesToExport
static void updateExportDirectory(String exportPath, Case openCase)
static String userDefinedExportPath
static String getExportDirectory(Case openCase)
JFileChooser getChooser()
String escapeQuotes(String original)
void actionPerformed(ActionEvent e)
static final List< String > columnsToSkip
synchronized static Logger getLogger(String name)
static Case getCurrentCaseThrows()
String getExportDirectory()
String listToCSV(List< String > values)
static void info(String message)
static void saveNodesToCSV(Collection<?extends Node > nodesToExport, Component component)