diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExcelExportAction.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExcelExportAction.java index 3f3ac09ec3..22e06c07ed 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExcelExportAction.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ExcelExportAction.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.datasourcesummary.ui; -import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; @@ -34,6 +33,7 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; import java.util.logging.Level; +import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.openide.util.NbBundle; @@ -186,7 +186,9 @@ class ExcelExportAction implements Consumer { "# {0} - dataSource", "ExcelExportAction_runXLSXExport_progressTitle=Exporting {0} to XLSX", "ExcelExportAction_runXLSXExport_progressCancelTitle=Cancel", - "ExcelExportAction_runXLSXExport_progressCancelActionTitle=Cancelling..." + "ExcelExportAction_runXLSXExport_progressCancelActionTitle=Cancelling...", + "ExcelExportAction_runXLSXExport_errorTitle=Error While Exporting", + "ExcelExportAction_runXLSXExport_errorMessage=There was an error while exporting.", }) private void runXLSXExport(DataSource dataSource, File path) { @@ -213,6 +215,10 @@ class ExcelExportAction implements Consumer { get(); } catch (ExecutionException ex) { logger.log(Level.WARNING, "Error while trying to export data source summary to xlsx.", ex); + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + Bundle.ExcelExportAction_runXLSXExport_errorMessage(), + Bundle.ExcelExportAction_runXLSXExport_errorTitle(), + JOptionPane.ERROR_MESSAGE); } catch (InterruptedException | CancellationException ex) { // no op on cancellation } finally { diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java index d27d4f595d..0697d7b1a6 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java @@ -291,6 +291,13 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel { super.close(); } + /** + * Create a default cell model to be use with excel export in the earliest / + * latest date format. + * + * @param date The date. + * @return The cell model. + */ private static DefaultCellModel getEarliestLatestCell(Date date) { return new DefaultCellModel<>(date, (dt) -> dt == null ? "" : EARLIEST_LATEST_FORMAT.format(dt), EARLIEST_LATEST_FORMAT_STR); } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java index ec67388eee..e11e638459 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java @@ -447,6 +447,7 @@ class TypesPanel extends BaseDataSourceSummaryPanel { return Collections.emptyList(); } + // Retrieve data to create the types pie chart TypesPieChartData typesData = TypesPanel.getFetchResult(typesFetcher, "Types", dataSource); PieChartExport typesChart = (typesData == null || !typesData.isUsefulContent()) ? null : new PieChartExport( diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartExport.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartExport.java index 7b5e56b656..3ab6796173 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartExport.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartExport.java @@ -1,7 +1,20 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2021 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.sleuthkit.autopsy.datasourcesummary.uiutils; @@ -42,35 +55,26 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport. import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ItemDimensions; /** - * - * @author gregd + * Class that creates an excel stacked bar chart along with data table. */ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport { - private final ExcelTableExport>, ? extends ExcelCellModel> tableExport; - private final int colOffset; - private final int rowPadding; - private final int colSize; - private final int rowSize; - private final String chartTitle; - private final String sheetName; - private final List categories; - private final String keyColumnHeader; - - public BarChartExport(String keyColumnHeader, - String valueFormatString, - String chartTitle, - List categories) { - this(keyColumnHeader, valueFormatString, chartTitle, chartTitle, categories, 1, 1, 8, 10); - } - - public BarChartExport(String keyColumnHeader, String valueFormatString, - String chartTitle, String sheetName, - List categories, - int colOffset, int rowPadding, int colSize, int rowSize) { - - this.keyColumnHeader = keyColumnHeader; + /** + * Creates an excel table model to be written to an excel sheet and used as + * a datasource for the chart. + * + * @param categories The categories with their data. + * @param keyColumnHeader The header column name for the table descriptions + * (i.e. types: file types / artifact types). + * @param valueFormatString The excel format string to use for values. + * @return An excel table export to be used as the data source for the chart + * in the excel document. + */ + private static ExcelTableExport>, ? extends ExcelCellModel> getTableModel( + List categories, String keyColumnHeader, String chartTitle) { + // get the row keys by finding the series with the largest set of bar items + // (they should all be equal, but just in case) List rowKeys = categories.stream() .filter(cat -> cat != null && cat.getItems() != null) .map(cat -> cat.getItems()) @@ -80,7 +84,7 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport { .map((barChartItem) -> barChartItem.getKey()) .collect(Collectors.toList()); - // map of (category, item) -> value + // map of (bar chart category index, bar chart item index) -> value Map, Double> valueMap = IntStream.range(0, categories.size()) .mapToObj(idx -> Pair.of(idx, categories.get(idx))) .filter(pair -> pair.getValue() != null && pair.getValue().getItems() != null) @@ -88,11 +92,13 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport { return IntStream.range(0, categoryPair.getValue().getItems().size()) .mapToObj(idx -> Pair.of(idx, categoryPair.getValue().getItems().get(idx))) .map(itemPair -> Pair.of( - Pair.of(categoryPair.getKey(), itemPair.getKey()), - itemPair.getValue() == null ? null : itemPair.getValue().getValue())); + Pair.of(categoryPair.getKey(), itemPair.getKey()), + itemPair.getValue() == null ? null : itemPair.getValue().getValue())); }) .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue(), (v1, v2) -> v1)); + // Create rows of data to be displayed where each row is a tuple of the bar chart item + // key and the list of values in category order. List>> values = IntStream.range(0, rowKeys.size()) .mapToObj(idx -> Pair.of(idx, rowKeys.get(idx))) .map((rowPair) -> { @@ -104,20 +110,80 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport { }) .collect(Collectors.toList()); - ColumnModel>, DefaultCellModel> categoryColumn = new ColumnModel<>(keyColumnHeader, (row) -> new DefaultCellModel<>(row.getKey())); + // Create the model for the category column + ColumnModel>, DefaultCellModel> categoryColumn + = new ColumnModel<>(keyColumnHeader, (row) -> new DefaultCellModel<>(row.getKey())); + // create the models for each category of data to be displayed Stream>, DefaultCellModel>> dataColumns = IntStream.range(0, categories.size()) .mapToObj(idx -> new ColumnModel<>( categories.get(idx).getKey().toString(), (row) -> new DefaultCellModel<>(row.getValue().get(idx)))); - this.tableExport = new ExcelTableExport>, DefaultCellModel>( + // create table + return new ExcelTableExport>, DefaultCellModel>( chartTitle, Stream.concat(Stream.of(categoryColumn), dataColumns) .collect(Collectors.toList()), values ); + } + private static final int DEFAULT_ROW_SIZE = 8; + private static final int DEFAULT_COL_SIZE = 15; + private static final int DEFAULT_ROW_PADDING = 1; + private static final int DEFAULT_COL_OFFSET = 1; + + private final ExcelTableExport>, ? extends ExcelCellModel> tableExport; + private final int colOffset; + private final int rowPadding; + private final int colSize; + private final int rowSize; + private final String chartTitle; + private final String sheetName; + private final List categories; + private final String keyColumnHeader; + + /** + * Main constructor that assumes some defaults (i.e. chart size follows + * defaults and sheet name is chart title). + * + * @param keyColumnHeader The header column name for the table descriptions + * (i.e. types: file types / artifact types). + * @param valueFormatString The excel format string to use for values. + * @param chartTitle The title for the chart. + * @param categories The categories along with data. + */ + public BarChartExport(String keyColumnHeader, + String valueFormatString, + String chartTitle, + List categories) { + this(keyColumnHeader, valueFormatString, chartTitle, chartTitle, categories, + DEFAULT_COL_OFFSET, DEFAULT_ROW_PADDING, DEFAULT_COL_SIZE, DEFAULT_ROW_SIZE); + } + + /** + * Main constructor. + * + * @param keyColumnHeader The header column name for the table descriptions + * (i.e. types: file types / artifact types). + * @param valueFormatString The excel format string to use for values. + * @param chartTitle The title for the chart. + * @param sheetName The sheet name if used as a sheet export. + * @param categories The categories along with data. + * @param colOffset The column spacing between the table and the chart. + * @param rowPadding The padding between this and data above or below (if + * used as an ExcelItemExportable). + * @param colSize The column size of the chart. + * @param rowSize The row size of the chart. + */ + public BarChartExport(String keyColumnHeader, String valueFormatString, + String chartTitle, String sheetName, + List categories, + int colOffset, int rowPadding, int colSize, int rowSize) { + + this.keyColumnHeader = keyColumnHeader; + this.tableExport = getTableModel(categories, keyColumnHeader, chartTitle); this.colOffset = colOffset; this.rowPadding = rowPadding; this.colSize = colSize; @@ -152,7 +218,7 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport { int chartColStart = colStart + categories.size() + 1 + colOffset; - //createAnchor(int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2); + //createAnchor has arguments of (int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2); XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, chartColStart, rowStart + rowPadding, chartColStart + colSize, rowStart + rowSize); XSSFChart chart = drawing.createChart(anchor); @@ -178,6 +244,7 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport { data.setBarDirection(BarDirection.COL); + // set data for each series and set color if applicable for (int i = 0; i < categories.size(); i++) { XDDFChartData.Series series = data.addSeries(headerSource, XDDFDataSourcesFactory.fromNumericCellRange(xssfSheet, diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java index d1c87364fe..21f3bca572 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java @@ -39,7 +39,6 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries.BarChartIt */ public class BarChartPanel extends AbstractLoadableComponent> { - /** * JFreeChart bar charts don't preserve the order of bars provided to the * chart, but instead uses the comparable nature to order items. This diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartSeries.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartSeries.java index 818c21e0d5..c1626f34b2 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartSeries.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartSeries.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2020 Basis Technology Corp. + * Copyright 2021 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,8 +23,7 @@ import java.util.Collections; import java.util.List; /** - * Represents a series in a bar chart where all items pertain to one - * category. + * Represents a series in a bar chart where all items pertain to one category. */ public class BarChartSeries { @@ -98,5 +97,5 @@ public class BarChartSeries { public Comparable getKey() { return key; } - + } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelTableExport.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelTableExport.java index 5d583f1f68..c79cb381aa 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelTableExport.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelTableExport.java @@ -123,7 +123,6 @@ public class ExcelTableExport implements ExcelSheet } // Create Cell Style for each column (if one is needed) - for (int rowNum = 0; rowNum < safeData.size(); rowNum++) { T rowData = safeData.get(rowNum); Row row = sheet.createRow(rowNum + rowStart + 1); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartExport.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartExport.java index 1dacdd3ac0..7b1490a20b 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartExport.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartExport.java @@ -1,7 +1,20 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2021 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.sleuthkit.autopsy.datasourcesummary.uiutils; @@ -29,10 +42,15 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport. /** * - * @author gregd + * Class that creates an excel pie chart along with data table. */ public class PieChartExport implements ExcelItemExportable, ExcelSheetExport { + private static final int DEFAULT_ROW_SIZE = 8; + private static final int DEFAULT_COL_SIZE = 15; + private static final int DEFAULT_ROW_PADDING = 1; + private static final int DEFAULT_COL_OFFSET = 1; + private final ExcelTableExport tableExport; private final int colOffset; private final int rowPadding; @@ -41,13 +59,40 @@ public class PieChartExport implements ExcelItemExportable, ExcelSheetExport { private final String chartTitle; private final String sheetName; + /** + * Main constructor assuming defaults. + * + * @param keyColumnHeader The header column name for the table descriptions + * (i.e. file types). + * @param valueColumnHeader The header column name for the values. + * @param valueFormatString The excel format string to use for values. + * @param chartTitle The title for the chart. + * @param slices The values for the pie slices. + */ public PieChartExport(String keyColumnHeader, String valueColumnHeader, String valueFormatString, String chartTitle, List slices) { - this(keyColumnHeader, valueColumnHeader, valueFormatString, chartTitle, chartTitle, slices, 1, 1, 8, 14); + this(keyColumnHeader, valueColumnHeader, valueFormatString, chartTitle, chartTitle, slices, + DEFAULT_COL_OFFSET, DEFAULT_ROW_PADDING, DEFAULT_COL_SIZE, DEFAULT_ROW_SIZE); } + /** + * Main constructor. + * + * @param keyColumnHeader The header column name for the table descriptions + * (i.e. file types). + * @param valueColumnHeader The header column name for the values. + * @param valueFormatString The excel format string to use for values. + * @param chartTitle The title for the chart. + * @param sheetName The sheet name if used as a sheet export. + * @param slices The values for the pie slices. + * @param colOffset The column spacing between the table and the chart. + * @param rowPadding The padding between this and data above or below (if + * used as an ExcelItemExportable). + * @param colSize The column size of the chart. + * @param rowSize The row size of the chart. + */ public PieChartExport(String keyColumnHeader, String valueColumnHeader, String valueFormatString, String chartTitle, String sheetName, @@ -93,7 +138,7 @@ public class PieChartExport implements ExcelItemExportable, ExcelSheetExport { int chartColStart = colStart + 2 + colOffset; - //createAnchor(int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2); + //createAnchor has arguments of (int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2); XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, chartColStart, rowStart + rowPadding, chartColStart + colSize, rowStart + rowSize); XSSFChart chart = drawing.createChart(anchor); @@ -102,7 +147,7 @@ public class PieChartExport implements ExcelItemExportable, ExcelSheetExport { XDDFChartLegend legend = chart.getOrAddLegend(); legend.setPosition(LegendPosition.RIGHT); - // (int firstRow, int lastRow, int firstCol, int lastCol) + // CellRangeAddress has arguments of (int firstRow, int lastRow, int firstCol, int lastCol) XDDFDataSource cat = XDDFDataSourcesFactory.fromStringCellRange(xssfSheet, new CellRangeAddress(tableDimensions.getRowStart() + 1, tableDimensions.getRowEnd(), tableDimensions.getColStart(), tableDimensions.getColStart())); @@ -111,13 +156,14 @@ public class PieChartExport implements ExcelItemExportable, ExcelSheetExport { new CellRangeAddress(tableDimensions.getRowStart() + 1, tableDimensions.getRowEnd(), tableDimensions.getColStart() + 1, tableDimensions.getColStart() + 1)); - - // XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM); - // XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT); - - // NOTE: these can be null parameters to chart.createData in poi >= 4.1.1 - //XDDFPieChartData data = (XDDFPieChartData) chart.createData(ChartTypes.PIE, bottomAxis, leftAxis); - + // NOTE: There appears to be a classpath issue with POI (a version of 4.0.1 and 4.1.1 simultaneously) + // that is causing conflicts for XDDFPieChartData creation (i.e. the compiler thinks its using 4.1.1 + // and the runtime thinks its using 4.0.1) Reflection is used below to use the 4.0.1 method while + // sidestepping compiler issues. + // XDDFPieChartData creation that can be used in poi >= 4.1.1: + // XDDFPieChartData data = (XDDFPieChartData) chart.createData(ChartTypes.PIE, bottomAxis, leftAxis); + // XDDFPieChartData creation that can be used in 4.0.1: + // XDDFPieChartData data = new XDDFPieChartData(chart.getCTChart().getPlotArea().addNewPieChart()); XDDFPieChartData data; try { Constructor constructor = XDDFPieChartData.class.getConstructor(CTPieChart.class); @@ -126,7 +172,7 @@ public class PieChartExport implements ExcelItemExportable, ExcelSheetExport { } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException | IllegalArgumentException ex) { throw new ExcelExportException("Error while instantiating chart data.", ex); } - + data.setVaryColors(true); data.addSeries(cat, val); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartItem.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartItem.java index f85dfb1dd7..b00a97e996 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartItem.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartItem.java @@ -1,7 +1,20 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2021 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.sleuthkit.autopsy.datasourcesummary.uiutils; @@ -50,5 +63,5 @@ public class PieChartItem { public Color getColor() { return color; } - + } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartPanel.java index 7131b8a9df..1e0ddde838 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2019 Basis Technology Corp. + * Copyright 2020 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,7 +41,6 @@ import org.openide.util.NbBundle.Messages; }) public class PieChartPanel extends AbstractLoadableComponent> { - private static final long serialVersionUID = 1L; private static final Font DEFAULT_FONT = new JLabel().getFont();