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)