Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
BarChartExport.java
Go to the documentation of this file.
1/*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2021 Basis Technology Corp.
5 * Contact: carrier <at> sleuthkit <dot> org
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19package org.sleuthkit.autopsy.report.modules.datasourcesummaryexport;
20
21import java.awt.Color;
22import java.nio.ByteBuffer;
23import java.util.Collections;
24import java.util.List;
25import java.util.Map;
26import java.util.stream.Collectors;
27import java.util.stream.IntStream;
28import java.util.stream.Stream;
29import org.apache.commons.lang3.tuple.Pair;
30import org.apache.poi.ss.usermodel.Sheet;
31import org.apache.poi.ss.util.CellRangeAddress;
32import org.apache.poi.xddf.usermodel.XDDFColor;
33import org.apache.poi.xddf.usermodel.XDDFShapeProperties;
34import org.apache.poi.xddf.usermodel.XDDFSolidFillProperties;
35import org.apache.poi.xddf.usermodel.chart.AxisCrosses;
36import org.apache.poi.xddf.usermodel.chart.AxisPosition;
37import org.apache.poi.xddf.usermodel.chart.BarDirection;
38import org.apache.poi.xddf.usermodel.chart.BarGrouping;
39import org.apache.poi.xddf.usermodel.chart.ChartTypes;
40import org.apache.poi.xddf.usermodel.chart.LegendPosition;
41import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData;
42import org.apache.poi.xddf.usermodel.chart.XDDFCategoryAxis;
43import org.apache.poi.xddf.usermodel.chart.XDDFChartData;
44import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend;
45import org.apache.poi.xddf.usermodel.chart.XDDFDataSource;
46import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory;
47import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis;
48import org.apache.poi.xssf.usermodel.XSSFChart;
49import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
50import org.apache.poi.xssf.usermodel.XSSFDrawing;
51import org.apache.poi.xssf.usermodel.XSSFSheet;
52import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries;
53import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExport.ExcelExportException;
54import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExport.ExcelSheetExport;
55import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelSpecialFormatExport.ExcelItemExportable;
56import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelSpecialFormatExport.ItemDimensions;
57
61class BarChartExport implements ExcelItemExportable, ExcelSheetExport {
62
74 private static ExcelTableExport<Pair<Object, List<Double>>, ? extends CellModel> getTableModel(
75 List<BarChartSeries> categories, String keyColumnHeader, String chartTitle) {
76
77 // get the row keys by finding the series with the largest set of bar items
78 // (they should all be equal, but just in case)
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())
84 .stream()
85 .map((barChartItem) -> barChartItem.getKey())
86 .collect(Collectors.toList());
87
88 // map of (bar chart category index, bar chart item index) -> value
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()));
98 })
99 .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue(), (v1, v2) -> v1));
100
101 // Create rows of data to be displayed where each row is a tuple of the bar chart item
102 // key and the list of values in category order.
103 List<Pair<Object, List<Double>>> values = IntStream.range(0, rowKeys.size())
104 .mapToObj(idx -> Pair.of(idx, rowKeys.get(idx)))
105 .map((rowPair) -> {
106 List<Double> items = IntStream.range(0, categories.size())
107 .mapToObj(idx -> valueMap.get(Pair.of(idx, rowPair.getKey())))
108 .collect(Collectors.toList());
109
110 return Pair.of(rowPair.getValue(), items);
111 })
112 .collect(Collectors.toList());
113
114 // Create the model for the category column
115 ColumnModel<Pair<Object, List<Double>>, DefaultCellModel<?>> categoryColumn
116 = new ColumnModel<>(keyColumnHeader, (row) -> new DefaultCellModel<>(row.getKey()));
117
118 // create the models for each category of data to be displayed
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))));
123
124 // create table
125 return new ExcelTableExport<Pair<Object, List<Double>>, DefaultCellModel<?>>(
126 chartTitle,
127 Stream.concat(Stream.of(categoryColumn), dataColumns)
128 .collect(Collectors.toList()),
129 values
130 );
131 }
132
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;
137
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;
147
158 BarChartExport(String keyColumnHeader,
159 String valueFormatString,
160 String chartTitle,
161 List<BarChartSeries> categories) {
162 this(keyColumnHeader, valueFormatString, chartTitle, chartTitle, categories,
163 DEFAULT_COL_OFFSET, DEFAULT_ROW_PADDING, DEFAULT_COL_SIZE, DEFAULT_ROW_SIZE);
164 }
165
181 BarChartExport(String keyColumnHeader, String valueFormatString,
182 String chartTitle, String sheetName,
183 List<BarChartSeries> categories,
184 int colOffset, int rowPadding, int colSize, int rowSize) {
185
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;
195 }
196
197 @Override
198 public String getSheetName() {
199 return sheetName;
200 }
201
202 @Override
203 public void renderSheet(Sheet sheet, ExcelExport.WorksheetEnv env) throws ExcelExport.ExcelExportException {
204 write(sheet, 0, 0, env);
205 }
206
207 @Override
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.");
211 }
212
213 XSSFSheet xssfSheet = (XSSFSheet) sheet;
214
215 // write pie chart table data
216 ItemDimensions tableDimensions = tableExport.write(xssfSheet, rowStart + rowPadding, colStart, env);
217
218 XSSFDrawing drawing = xssfSheet.createDrawingPatriarch();
219
220 int chartColStart = colStart + categories.size() + 1 + colOffset;
221
222 //createAnchor has arguments of (int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2);
223 XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, chartColStart, rowStart + rowPadding, chartColStart + colSize + 1, rowStart + rowSize + 1);
224
225 XSSFChart chart = drawing.createChart(anchor);
226 chart.setTitleText(chartTitle);
227 chart.setTitleOverlay(false);
228 XDDFChartLegend legend = chart.getOrAddLegend();
229 legend.setPosition(LegendPosition.BOTTOM);
230
231 // Use a category axis for the bottom axis.
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);
237
238 XDDFBarChartData data = (XDDFBarChartData) chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
239 data.setBarGrouping(BarGrouping.STACKED);
240
241 XDDFDataSource<String> headerSource = XDDFDataSourcesFactory.fromStringCellRange(xssfSheet,
242 new CellRangeAddress(tableDimensions.getRowStart() + 1, tableDimensions.getRowEnd(),
243 tableDimensions.getColStart(), tableDimensions.getColStart()));
244
245 data.setBarDirection(BarDirection.COL);
246
247 // set data for each series and set color if applicable
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)));
253
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();
263 }
264 properties.setFillProperties(fill);
265 series.setShapeProperties(properties);
266 }
267 }
268
269 chart.plot(data);
270
271 return new ItemDimensions(rowStart, colStart, Math.max(tableDimensions.getRowEnd(), rowStart + rowSize) + rowPadding, chartColStart + colSize);
272 }
273
274}

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.