diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED index 4a47bcb092..3f82fcdd3f 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED @@ -128,6 +128,7 @@ TypesPanel_fileMimeTypesChart_notAnalyzed_title=Not Analyzed TypesPanel_fileMimeTypesChart_other_title=Other TypesPanel_fileMimeTypesChart_title=File Types TypesPanel_fileMimeTypesChart_unknown_title=Unknown +TypesPanel_fileMimeTypesChart_valueLabel=Count TypesPanel_fileMimeTypesChart_videos_title=Videos TypesPanel_filesByCategoryTable_allocatedRow_title=Allocated Files TypesPanel_filesByCategoryTable_directoryRow_title=Directories diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java index ff30a72331..030181c04b 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TimelinePanel.java @@ -40,9 +40,9 @@ import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.DailyActivityAmount; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.TimelineSummaryData; import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.BarChartItem; -import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.BarChartSeries; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries; import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.OrderedKey; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries.BarChartItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java index de9a23df8e..e2fe113b9f 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java @@ -65,6 +65,7 @@ import org.sleuthkit.datamodel.TskCoreException; "TypesPanel_filesByCategoryTable_slackRow_title=Slack Files", "TypesPanel_filesByCategoryTable_directoryRow_title=Directories", "TypesPanel_fileMimeTypesChart_title=File Types", + "TypesPanel_fileMimeTypesChart_valueLabel=Count", "TypesPanel_fileMimeTypesChart_audio_title=Audio", "TypesPanel_fileMimeTypesChart_documents_title=Documents", "TypesPanel_fileMimeTypesChart_executables_title=Executables", @@ -198,6 +199,8 @@ class TypesPanel extends BaseDataSourceSummaryPanel { private final DataFetcher osFetcher; private final DataFetcher sizeFetcher; + private final DataFetcher typesFetcher; + private final DataFetcher allocatedFetcher; private final DataFetcher unallocatedFetcher; private final DataFetcher slackFetcher; @@ -262,6 +265,8 @@ class TypesPanel extends BaseDataSourceSummaryPanel { this.osFetcher = containerData::getOperatingSystems; this.sizeFetcher = (dataSource) -> dataSource == null ? null : dataSource.getSize(); + + this.typesFetcher = (dataSource) -> getMimeTypeCategoriesModel(mimeTypeData, dataSource); this.allocatedFetcher = (dataSource) -> typeData.getCountOfAllocatedFiles(dataSource); this.unallocatedFetcher = (dataSource) -> typeData.getCountOfUnallocatedFiles(dataSource); @@ -275,9 +280,7 @@ class TypesPanel extends BaseDataSourceSummaryPanel { (sizeResult) -> sizeLabel.showDataFetchResult( DataFetchResult.getSubResult(sizeResult, size -> SizeRepresentationUtil.getSizeString(size, INTEGER_SIZE_FORMAT, false)))), - new DataFetchWorker.DataFetchComponents<>( - (dataSource) -> getMimeTypeCategoriesModel(mimeTypeData, dataSource), - this::showMimeTypeCategories), + new DataFetchWorker.DataFetchComponents<>(typesFetcher, this::showMimeTypeCategories), new DataFetchWorker.DataFetchComponents<>(allocatedFetcher, countRes -> allocatedLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count)))), new DataFetchWorker.DataFetchComponents<>(unallocatedFetcher, @@ -443,6 +446,15 @@ class TypesPanel extends BaseDataSourceSummaryPanel { if (dataSource == null) { return Collections.emptyList(); } + + TypesPieChartData typesData = this.getFetchResult(typesFetcher, "Types", dataSource); + PieChartExport typesChart = (typesData == null || !typesData.isUsefulContent()) ? null : + new PieChartExport( + Bundle.TypesPanel_fileMimeTypesChart_title(), + Bundle.TypesPanel_fileMimeTypesChart_valueLabel(), + "#,###", + Bundle.TypesPanel_fileMimeTypesChart_title(), + typesData.getPieSlices()); return Arrays.asList(new ExcelSpecialFormatExport(Bundle.TypesPanel_excelTabName(), Stream.of( @@ -450,10 +462,7 @@ class TypesPanel extends BaseDataSourceSummaryPanel { getStrExportable(osFetcher, Bundle.TypesPanel_osLabel_title(), dataSource), new KeyValueItemExportable(Bundle.TypesPanel_sizeLabel_title(), SizeRepresentationUtil.getBytesCell(getFetchResult(sizeFetcher, "Types", dataSource))), - new PieChartExport(String keyColumnHeader, - String valueColumnHeader, String valueFormatString, - String chartTitle, - List slices), + typesChart, getCountExportable(allocatedFetcher, Bundle.TypesPanel_filesByCategoryTable_allocatedRow_title(), dataSource), getCountExportable(unallocatedFetcher, Bundle.TypesPanel_filesByCategoryTable_unallocatedRow_title(), dataSource), getCountExportable(slackFetcher, Bundle.TypesPanel_filesByCategoryTable_slackRow_title(), dataSource), diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartExport.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartExport.java new file mode 100644 index 0000000000..032912742b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartExport.java @@ -0,0 +1,187 @@ +/* + * 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. + */ +package org.sleuthkit.autopsy.datasourcesummary.uiutils; + +import com.google.cloud.Tuple; +import java.awt.Color; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xddf.usermodel.XDDFColor; +import org.apache.poi.xddf.usermodel.XDDFShapeProperties; +import org.apache.poi.xddf.usermodel.XDDFSolidFillProperties; +import org.apache.poi.xddf.usermodel.chart.AxisCrosses; +import org.apache.poi.xddf.usermodel.chart.AxisPosition; +import org.apache.poi.xddf.usermodel.chart.BarDirection; +import org.apache.poi.xddf.usermodel.chart.BarGrouping; +import org.apache.poi.xddf.usermodel.chart.ChartTypes; +import org.apache.poi.xddf.usermodel.chart.LegendPosition; +import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFCategoryAxis; +import org.apache.poi.xddf.usermodel.chart.XDDFChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory; +import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFPieChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis; +import org.apache.poi.xssf.usermodel.XSSFChart; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelExportException; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ExcelItemExportable; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ItemDimensions; + +/** + * + * @author gregd + */ +public class BarChartExport implements ExcelItemExportable, ExcelSheetExport { + private final ExcelTableExport 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; + + List categoryKeys = categories.stream() + .filter(cat -> cat != null && cat.getKey() != null) + .sorted((c1, c2) -> c1.getKey().compareTo(c2.getKey())) + .collect(Collectors.toList()); + + List> rowKeys = categories.stream() + .filter(cat -> cat != null && cat.getItems() != null) + .flatMap(cat -> cat.getItems().stream().map(item -> item.getKey())) + .filter(i -> i != null) + .distinct() + .sorted() + .collect(Collectors.toList()); + + Map, Comparable>, Double> valueMap = categories.stream() + .flatMap((cat) -> cat.getItems().stream().map((item) -> Tuple.of(Tuple.of(cat.getKey(), item.getKey()), item.getValue()))) + .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue(), (v1, v2) -> v1)); + + List, List>> values = rowKeys.stream() + .map((rowValue)) + + this.tableExport = new ExcelTableExport<>(chartTitle, + Stream.concat(Stream.of(new ColumnModel<>(keyColumnHeader, (category) -> new DefaultCellModel<>(category.getLabel()))), ) + Arrays.asList( + , + new ColumnModel<>(valueColumnHeader, (category) -> new DefaultCellModel<>(category.getValue(), null, valueFormatString)) + ), + categories); + this.colOffset = colOffset; + this.rowPadding = rowPadding; + this.colSize = colSize; + this.rowSize = rowSize; + this.chartTitle = chartTitle; + this.sheetName = sheetName; + this.categories = categories; + } + + + + @Override + public String getSheetName() { + return sheetName; + } + + @Override + public void renderSheet(Sheet sheet, ExcelExport.WorksheetEnv env) throws ExcelExport.ExcelExportException { + write(sheet, 0, 0, env); + } + + @Override + public ItemDimensions write(Sheet sheet, int rowStart, int colStart, ExcelExport.WorksheetEnv env) throws ExcelExportException { + if (!(sheet instanceof XSSFSheet)) { + throw new ExcelExportException("Sheet must be an XSSFSheet in order to write."); + } + + XSSFSheet xssfSheet = (XSSFSheet) sheet; + + // write pie chart table data + ItemDimensions tableDimensions = tableExport.write(xssfSheet, rowStart + rowPadding, colStart, env); + + XSSFDrawing drawing = xssfSheet.createDrawingPatriarch(); + + int chartColStart = colStart + 2 + colOffset; + + //createAnchor(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); + chart.setTitleText(chartTitle); + chart.setTitleOverlay(false); + XDDFChartLegend legend = chart.getOrAddLegend(); + legend.setPosition(LegendPosition.BOTTOM); + + + // Use a category axis for the bottom axis. + XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM); + bottomAxis.setTitle(keyColumnHeader); + XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT); + // leftAxis.setTitle(valHeader); + leftAxis.setCrosses(AxisCrosses.AUTO_ZERO); + leftAxis.setVisible(false); + + XDDFBarChartData data = (XDDFBarChartData) chart.createData(ChartTypes.BAR, bottomAxis, leftAxis); + data.setBarGrouping(BarGrouping.STACKED); + + XDDFDataSource headerSource = XDDFDataSourcesFactory.fromStringCellRange(sheet, new CellRangeAddress(1, keyVals.size(), 0, 0)); + data.setBarDirection(BarDirection.COL); + + for (int i = 0; i < categories.size(); i++) { + XDDFChartData.Series series = data.addSeries(headerSource, + XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(1, keyVals.size(), i + 1, i + 1))); + series.setTitle(categories.size() > i && categories.get(i).getIdentifier() != null ? categories.get(i).getIdentifier() : "", null); + if (categories.get(i).getColor() != null) { + Color color = categories.get(i).getColor(); + byte[] colorArrARGB = ByteBuffer.allocate(4).putInt(color.getRGB()).array(); + byte[] colorArrRGB = new byte[]{colorArrARGB[1], colorArrARGB[2], colorArrARGB[3]}; + XDDFSolidFillProperties fill = new XDDFSolidFillProperties(XDDFColor.from(colorArrRGB)); // XDDFColor.from(color.getRed(), color.getGreen(), color.getBlue())); + XDDFShapeProperties properties = series.getShapeProperties(); + if (properties == null) { + properties = new XDDFShapeProperties(); + } + properties.setFillProperties(fill); + series.setShapeProperties(properties); + } + } + + chart.plot(data); + + return new ItemDimensions(rowStart, colStart, Math.max(tableDimensions.getRowEnd(), rowStart + rowSize) + rowPadding, chartColStart + colSize); + } + + + +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java index 09f5ade505..d1c87364fe 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartPanel.java @@ -19,9 +19,7 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils; import java.awt.BorderLayout; -import java.awt.Color; import java.awt.Font; -import java.util.Collections; import java.util.List; import javax.swing.JLabel; import org.apache.commons.collections4.CollectionUtils; @@ -34,90 +32,13 @@ import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.category.BarRenderer; import org.jfree.chart.renderer.category.StandardBarPainter; import org.jfree.data.category.DefaultCategoryDataset; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries.BarChartItem; /** * A bar chart panel. */ -public class BarChartPanel extends AbstractLoadableComponent> { +public class BarChartPanel extends AbstractLoadableComponent> { - /** - * Represents a series in a bar chart where all items pertain to one - * category. - */ - public static class BarChartSeries { - - private final Comparable key; - private final Color color; - private final List items; - - /** - * Main constructor. - * - * @param key The key. - * @param color The color for this series. - * @param items The bars to be displayed for this series. - */ - public BarChartSeries(Comparable key, Color color, List items) { - this.key = key; - this.color = color; - this.items = (items == null) ? Collections.emptyList() : Collections.unmodifiableList(items); - } - - /** - * @return The color for this series. - */ - public Color getColor() { - return color; - } - - /** - * @return The bars to be displayed for this series. - */ - public List getItems() { - return items; - } - - /** - * @return The key for this item. - */ - public Comparable getKey() { - return key; - } - } - - /** - * An individual bar to be displayed in the bar chart. - */ - public static class BarChartItem { - - private final Comparable key; - private final double value; - - /** - * Main constructor. - * - * @param key The key. - * @param value The value for this item. - */ - public BarChartItem(Comparable key, double value) { - this.key = key; - this.value = value; - } - - /** - * @return The key for this item. - */ - public Comparable getKey() { - return key; - } - - /** - * @return The value for this item. - */ - public double getValue() { - return value; - } - } /** * JFreeChart bar charts don't preserve the order of bars provided to the @@ -285,12 +206,12 @@ public class BarChartPanel extends AbstractLoadableComponent data) { + protected void setResults(List data) { this.dataset.clear(); if (CollectionUtils.isNotEmpty(data)) { for (int s = 0; s < data.size(); s++) { - BarChartPanel.BarChartSeries series = data.get(s); + BarChartSeries series = data.get(s); if (series != null && CollectionUtils.isNotEmpty(series.getItems())) { if (series.getColor() != null) { this.plot.getRenderer().setSeriesPaint(s, series.getColor()); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartSeries.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartSeries.java new file mode 100644 index 0000000000..818c21e0d5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BarChartSeries.java @@ -0,0 +1,102 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 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; + +import java.awt.Color; +import java.util.Collections; +import java.util.List; + +/** + * Represents a series in a bar chart where all items pertain to one + * category. + */ +public class BarChartSeries { + + /** + * An individual bar to be displayed in the bar chart. + */ + public static class BarChartItem { + + private final Comparable key; + private final double value; + + /** + * Main constructor. + * + * @param key The key. + * @param value The value for this item. + */ + public BarChartItem(Comparable key, double value) { + this.key = key; + this.value = value; + } + + /** + * @return The key for this item. + */ + public Comparable getKey() { + return key; + } + + /** + * @return The value for this item. + */ + public double getValue() { + return value; + } + } + private final Comparable key; + private final Color color; + private final List items; + + /** + * Main constructor. + * + * @param key The key. + * @param color The color for this series. + * @param items The bars to be displayed for this series. + */ + public BarChartSeries(Comparable key, Color color, List items) { + this.key = key; + this.color = color; + this.items = (items == null) ? Collections.emptyList() : Collections.unmodifiableList(items); + } + + /** + * @return The color for this series. + */ + public Color getColor() { + return color; + } + + /** + * @return The bars to be displayed for this series. + */ + public List getItems() { + return items; + } + + /** + * @return The key for this item. + */ + 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 5592344e2c..5d583f1f68 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelTableExport.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelTableExport.java @@ -81,6 +81,8 @@ public class ExcelTableExport implements ExcelSheet sheet.autoSizeColumn(i); } + // freeze header row + sheet.createFreezePane(0, 1); } @Override @@ -119,8 +121,7 @@ public class ExcelTableExport implements ExcelSheet cell.setCellValue(columns.get(i).getHeaderTitle()); cell.setCellStyle(worksheetEnv.getHeaderStyle()); } - // freeze header row - sheet.createFreezePane(0, 1); + // Create Cell Style for each column (if one is needed) for (int rowNum = 0; rowNum < safeData.size(); rowNum++) { diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartExport.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartExport.java index 1a454b6ba3..2b8aba50f5 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartExport.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartExport.java @@ -9,11 +9,16 @@ import java.util.Arrays; import java.util.List; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xddf.usermodel.chart.AxisPosition; import org.apache.poi.xddf.usermodel.chart.ChartTypes; +import org.apache.poi.xddf.usermodel.chart.LegendPosition; +import org.apache.poi.xddf.usermodel.chart.XDDFCategoryAxis; +import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend; import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory; import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; import org.apache.poi.xddf.usermodel.chart.XDDFPieChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis; import org.apache.poi.xssf.usermodel.XSSFChart; import org.apache.poi.xssf.usermodel.XSSFClientAnchor; import org.apache.poi.xssf.usermodel.XSSFDrawing; @@ -28,6 +33,7 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport. * @author gregd */ public class PieChartExport implements ExcelItemExportable, ExcelSheetExport { + private final ExcelTableExport tableExport; private final int colOffset; private final int rowPadding; @@ -35,26 +41,25 @@ public class PieChartExport implements ExcelItemExportable, ExcelSheetExport { private final int rowSize; private final String chartTitle; private final String sheetName; - private final List slices; - - public PieChartExport(String keyColumnHeader, + + public PieChartExport(String keyColumnHeader, String valueColumnHeader, String valueFormatString, String chartTitle, List slices) { - this(keyColumnHeader, valueColumnHeader, valueFormatString, chartTitle, chartTitle, slices, 1, 1, 8, 10); + this(keyColumnHeader, valueColumnHeader, valueFormatString, chartTitle, chartTitle, slices, 1, 1, 8, 14); } - public PieChartExport(String keyColumnHeader, + public PieChartExport(String keyColumnHeader, String valueColumnHeader, String valueFormatString, - String chartTitle, String sheetName, - List slices, + String chartTitle, String sheetName, + List slices, int colOffset, int rowPadding, int colSize, int rowSize) { - + this.tableExport = new ExcelTableExport<>(chartTitle, Arrays.asList( new ColumnModel<>(keyColumnHeader, (slice) -> new DefaultCellModel<>(slice.getLabel())), new ColumnModel<>(valueColumnHeader, (slice) -> new DefaultCellModel<>(slice.getValue(), null, valueFormatString)) - ), + ), slices); this.colOffset = colOffset; this.rowPadding = rowPadding; @@ -62,11 +67,8 @@ public class PieChartExport implements ExcelItemExportable, ExcelSheetExport { this.rowSize = rowSize; this.chartTitle = chartTitle; this.sheetName = sheetName; - this.slices = slices; } - - - + @Override public String getSheetName() { return sheetName; @@ -82,35 +84,39 @@ public class PieChartExport implements ExcelItemExportable, ExcelSheetExport { if (!(sheet instanceof XSSFSheet)) { throw new ExcelExportException("Sheet must be an XSSFSheet in order to write."); } - + XSSFSheet xssfSheet = (XSSFSheet) sheet; - + // write pie chart table data ItemDimensions tableDimensions = tableExport.write(xssfSheet, rowStart + rowPadding, colStart, env); XSSFDrawing drawing = xssfSheet.createDrawingPatriarch(); - + int chartColStart = colStart + 2 + colOffset; - + //createAnchor(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); chart.setTitleText(chartTitle); chart.setTitleOverlay(false); -// XDDFChartLegend legend = chart.getOrAddLegend(); -// legend.setPosition(LegendPosition.BOTTOM); + XDDFChartLegend legend = chart.getOrAddLegend(); + legend.setPosition(LegendPosition.RIGHT); // (int firstRow, int lastRow, int firstCol, int lastCol) XDDFDataSource cat = XDDFDataSourcesFactory.fromStringCellRange(xssfSheet, - new CellRangeAddress(tableDimensions.getRowStart(), tableDimensions.getRowEnd(), + new CellRangeAddress(tableDimensions.getRowStart() + 1, tableDimensions.getRowEnd(), tableDimensions.getColStart(), tableDimensions.getColStart())); - + XDDFNumericalDataSource val = XDDFDataSourcesFactory.fromNumericCellRange(xssfSheet, - new CellRangeAddress(tableDimensions.getRowStart(), tableDimensions.getRowEnd(), + new CellRangeAddress(tableDimensions.getRowStart() + 1, tableDimensions.getRowEnd(), tableDimensions.getColStart() + 1, tableDimensions.getColStart() + 1)); - XDDFPieChartData data = (XDDFPieChartData) chart.createData(ChartTypes.PIE, null, null); + // NOTE: these can be null parameters to chart.createData in poi >= 4.1.1 + XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM); + XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT); + + XDDFPieChartData data = (XDDFPieChartData) chart.createData(ChartTypes.PIE, bottomAxis, leftAxis); data.setVaryColors(true); data.addSeries(cat, val); @@ -124,13 +130,11 @@ public class PieChartExport implements ExcelItemExportable, ExcelSheetExport { chart.getCTChart().getPlotArea().getPieChartArray(0).getSerArray(0).getDLbls().addNewShowSerName().setVal(false); chart.getCTChart().getPlotArea().getPieChartArray(0).getSerArray(0).getDLbls().addNewShowCatName().setVal(true); chart.getCTChart().getPlotArea().getPieChartArray(0).getSerArray(0).getDLbls().addNewShowPercent().setVal(true); - //chart.getCTChart().getPlotArea().getPieChartArray(0).getSerArray(0).getDLbls().addNewShowLegendKey().setVal(false); + chart.getCTChart().getPlotArea().getPieChartArray(0).getSerArray(0).getDLbls().addNewShowLegendKey().setVal(false); chart.plot(data); - + return new ItemDimensions(rowStart, colStart, Math.max(tableDimensions.getRowEnd(), rowStart + rowSize) + rowPadding, chartColStart + colSize); } - - }