Autopsy  4.20.0
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  */
19 package org.sleuthkit.autopsy.report.modules.datasourcesummaryexport;
20 
21 import java.awt.Color;
22 import java.nio.ByteBuffer;
23 import java.util.Collections;
24 import java.util.List;
25 import java.util.Map;
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;
57 
61 class 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-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.