19 package org.sleuthkit.autopsy.report.modules.datasourcesummaryexport;
21 import java.awt.Color;
22 import java.nio.ByteBuffer;
23 import java.util.Collections;
24 import java.util.List;
26 import java.util.stream.Collectors;
27 import java.util.stream.IntStream;
28 import java.util.stream.Stream;
29 import org.apache.commons.lang3.tuple.Pair;
30 import org.apache.poi.ss.usermodel.Sheet;
31 import org.apache.poi.ss.util.CellRangeAddress;
32 import org.apache.poi.xddf.usermodel.XDDFColor;
33 import org.apache.poi.xddf.usermodel.XDDFShapeProperties;
34 import org.apache.poi.xddf.usermodel.XDDFSolidFillProperties;
35 import org.apache.poi.xddf.usermodel.chart.AxisCrosses;
36 import org.apache.poi.xddf.usermodel.chart.AxisPosition;
37 import org.apache.poi.xddf.usermodel.chart.BarDirection;
38 import org.apache.poi.xddf.usermodel.chart.BarGrouping;
39 import org.apache.poi.xddf.usermodel.chart.ChartTypes;
40 import org.apache.poi.xddf.usermodel.chart.LegendPosition;
41 import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData;
42 import org.apache.poi.xddf.usermodel.chart.XDDFCategoryAxis;
43 import org.apache.poi.xddf.usermodel.chart.XDDFChartData;
44 import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend;
45 import org.apache.poi.xddf.usermodel.chart.XDDFDataSource;
46 import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory;
47 import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis;
48 import org.apache.poi.xssf.usermodel.XSSFChart;
49 import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
50 import org.apache.poi.xssf.usermodel.XSSFDrawing;
51 import org.apache.poi.xssf.usermodel.XSSFSheet;
61 class BarChartExport
implements ExcelItemExportable, ExcelSheetExport {
74 private static ExcelTableExport<Pair<Object, List<Double>>, ? extends CellModel> getTableModel(
75 List<BarChartSeries> categories, String keyColumnHeader, String chartTitle) {
79 List<? extends Object> rowKeys = categories.stream()
80 .filter(cat -> cat != null && cat.getItems() != null)
81 .map(cat -> cat.getItems())
82 .max((items1, items2) -> Integer.compare(items1.size(), items2.size()))
83 .orElse(Collections.emptyList())
85 .map((barChartItem) -> barChartItem.getKey())
86 .collect(Collectors.toList());
89 Map<Pair<Integer, Integer>, Double> valueMap = IntStream.range(0, categories.size())
90 .mapToObj(idx -> Pair.of(idx, categories.get(idx)))
91 .filter(pair -> pair.getValue() != null && pair.getValue().getItems() != null)
92 .flatMap(categoryPair -> {
93 return IntStream.range(0, categoryPair.getValue().getItems().size())
94 .mapToObj(idx -> Pair.of(idx, categoryPair.getValue().getItems().get(idx)))
95 .map(itemPair -> Pair.of(
96 Pair.of(categoryPair.getKey(), itemPair.getKey()),
97 itemPair.getValue() == null ? null : itemPair.getValue().getValue()));
99 .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue(), (v1, v2) -> v1));
103 List<Pair<Object, List<Double>>> values = IntStream.range(0, rowKeys.size())
104 .mapToObj(idx -> Pair.of(idx, rowKeys.get(idx)))
106 List<Double> items = IntStream.range(0, categories.size())
107 .mapToObj(idx -> valueMap.get(Pair.of(idx, rowPair.getKey())))
108 .collect(Collectors.toList());
110 return Pair.of(rowPair.getValue(), items);
112 .collect(Collectors.toList());
115 ColumnModel<Pair<Object, List<Double>>, DefaultCellModel<?>> categoryColumn
116 =
new ColumnModel<>(keyColumnHeader, (row) ->
new DefaultCellModel<>(row.getKey()));
119 Stream<ColumnModel<Pair<Object, List<Double>>, DefaultCellModel<?>>> dataColumns = IntStream.range(0, categories.size())
120 .mapToObj(idx ->
new ColumnModel<>(
121 categories.get(idx).getKey().toString(),
122 (row) ->
new DefaultCellModel<>(row.getValue().get(idx))));
125 return new ExcelTableExport<Pair<Object, List<Double>>, DefaultCellModel<?>>(
127 Stream.concat(Stream.of(categoryColumn), dataColumns)
128 .collect(Collectors.toList()),
133 private static final int DEFAULT_ROW_SIZE = 15;
134 private static final int DEFAULT_COL_SIZE = 10;
135 private static final int DEFAULT_ROW_PADDING = 1;
136 private static final int DEFAULT_COL_OFFSET = 1;
138 private final ExcelTableExport<Pair<Object, List<Double>>, ? extends CellModel> tableExport;
139 private final int colOffset;
140 private final int rowPadding;
141 private final int colSize;
142 private final int rowSize;
143 private final String chartTitle;
144 private final String sheetName;
145 private final List<BarChartSeries> categories;
146 private final String keyColumnHeader;
158 BarChartExport(String keyColumnHeader,
159 String valueFormatString,
161 List<BarChartSeries> categories) {
162 this(keyColumnHeader, valueFormatString, chartTitle, chartTitle, categories,
163 DEFAULT_COL_OFFSET, DEFAULT_ROW_PADDING, DEFAULT_COL_SIZE, DEFAULT_ROW_SIZE);
181 BarChartExport(String keyColumnHeader, String valueFormatString,
182 String chartTitle, String sheetName,
183 List<BarChartSeries> categories,
184 int colOffset,
int rowPadding,
int colSize,
int rowSize) {
186 this.keyColumnHeader = keyColumnHeader;
187 this.tableExport = getTableModel(categories, keyColumnHeader, chartTitle);
188 this.colOffset = colOffset;
189 this.rowPadding = rowPadding;
190 this.colSize = colSize;
191 this.rowSize = rowSize;
192 this.chartTitle = chartTitle;
193 this.sheetName = sheetName;
194 this.categories = categories;
198 public String getSheetName() {
203 public void renderSheet(Sheet sheet, ExcelExport.WorksheetEnv env)
throws ExcelExport.ExcelExportException {
204 write(sheet, 0, 0, env);
208 public ItemDimensions write(Sheet sheet,
int rowStart,
int colStart, ExcelExport.WorksheetEnv env)
throws ExcelExportException {
209 if (!(sheet instanceof XSSFSheet)) {
210 throw new ExcelExportException(
"Sheet must be an XSSFSheet in order to write.");
213 XSSFSheet xssfSheet = (XSSFSheet) sheet;
216 ItemDimensions tableDimensions = tableExport.write(xssfSheet, rowStart + rowPadding, colStart, env);
218 XSSFDrawing drawing = xssfSheet.createDrawingPatriarch();
220 int chartColStart = colStart + categories.size() + 1 + colOffset;
223 XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, chartColStart, rowStart + rowPadding, chartColStart + colSize + 1, rowStart + rowSize + 1);
225 XSSFChart chart = drawing.createChart(anchor);
226 chart.setTitleText(chartTitle);
227 chart.setTitleOverlay(
false);
228 XDDFChartLegend legend = chart.getOrAddLegend();
229 legend.setPosition(LegendPosition.BOTTOM);
232 XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
233 bottomAxis.setTitle(keyColumnHeader);
234 XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
235 leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
236 leftAxis.setVisible(
false);
238 XDDFBarChartData data = (XDDFBarChartData) chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
239 data.setBarGrouping(BarGrouping.STACKED);
241 XDDFDataSource<String> headerSource = XDDFDataSourcesFactory.fromStringCellRange(xssfSheet,
242 new CellRangeAddress(tableDimensions.getRowStart() + 1, tableDimensions.getRowEnd(),
243 tableDimensions.getColStart(), tableDimensions.getColStart()));
245 data.setBarDirection(BarDirection.COL);
248 for (
int i = 0; i < categories.size(); i++) {
249 XDDFChartData.Series series = data.addSeries(headerSource,
250 XDDFDataSourcesFactory.fromNumericCellRange(xssfSheet,
251 new CellRangeAddress(tableDimensions.getRowStart() + 1, tableDimensions.getRowEnd(),
252 tableDimensions.getColStart() + 1 + i, tableDimensions.getColStart() + 1 + i)));
254 series.setTitle(categories.size() > i && categories.get(i).getKey() != null ? categories.get(i).getKey().toString() :
"", null);
255 if (categories.get(i).getColor() != null) {
256 Color color = categories.get(i).getColor();
257 byte[] colorArrARGB = ByteBuffer.allocate(4).putInt(color.getRGB()).array();
258 byte[] colorArrRGB =
new byte[]{colorArrARGB[1], colorArrARGB[2], colorArrARGB[3]};
259 XDDFSolidFillProperties fill =
new XDDFSolidFillProperties(XDDFColor.from(colorArrRGB));
260 XDDFShapeProperties properties = series.getShapeProperties();
261 if (properties == null) {
262 properties =
new XDDFShapeProperties();
264 properties.setFillProperties(fill);
265 series.setShapeProperties(properties);
271 return new ItemDimensions(rowStart, colStart, Math.max(tableDimensions.getRowEnd(), rowStart + rowSize) + rowPadding, chartColStart + colSize);