Autopsy  4.19.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.datasourcesummary.uiutils;
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;
56 
61 
74  List<BarChartSeries> categories, String keyColumnHeader, String chartTitle) {
75 
76  // get the row keys by finding the series with the largest set of bar items
77  // (they should all be equal, but just in case)
78  List<? extends Object> rowKeys = categories.stream()
79  .filter(cat -> cat != null && cat.getItems() != null)
80  .map(cat -> cat.getItems())
81  .max((items1, items2) -> Integer.compare(items1.size(), items2.size()))
82  .orElse(Collections.emptyList())
83  .stream()
84  .map((barChartItem) -> barChartItem.getKey())
85  .collect(Collectors.toList());
86 
87  // map of (bar chart category index, bar chart item index) -> value
88  Map<Pair<Integer, Integer>, Double> valueMap = IntStream.range(0, categories.size())
89  .mapToObj(idx -> Pair.of(idx, categories.get(idx)))
90  .filter(pair -> pair.getValue() != null && pair.getValue().getItems() != null)
91  .flatMap(categoryPair -> {
92  return IntStream.range(0, categoryPair.getValue().getItems().size())
93  .mapToObj(idx -> Pair.of(idx, categoryPair.getValue().getItems().get(idx)))
94  .map(itemPair -> Pair.of(
95  Pair.of(categoryPair.getKey(), itemPair.getKey()),
96  itemPair.getValue() == null ? null : itemPair.getValue().getValue()));
97  })
98  .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue(), (v1, v2) -> v1));
99 
100  // Create rows of data to be displayed where each row is a tuple of the bar chart item
101  // key and the list of values in category order.
102  List<Pair<Object, List<Double>>> values = IntStream.range(0, rowKeys.size())
103  .mapToObj(idx -> Pair.of(idx, rowKeys.get(idx)))
104  .map((rowPair) -> {
105  List<Double> items = IntStream.range(0, categories.size())
106  .mapToObj(idx -> valueMap.get(Pair.of(idx, rowPair.getKey())))
107  .collect(Collectors.toList());
108 
109  return Pair.of(rowPair.getValue(), items);
110  })
111  .collect(Collectors.toList());
112 
113  // Create the model for the category column
115  = new ColumnModel<>(keyColumnHeader, (row) -> new DefaultCellModel<>(row.getKey()));
116 
117  // create the models for each category of data to be displayed
118  Stream<ColumnModel<Pair<Object, List<Double>>, DefaultCellModel<?>>> dataColumns = IntStream.range(0, categories.size())
119  .mapToObj(idx -> new ColumnModel<>(
120  categories.get(idx).getKey().toString(),
121  (row) -> new DefaultCellModel<>(row.getValue().get(idx))));
122 
123  // create table
125  chartTitle,
126  Stream.concat(Stream.of(categoryColumn), dataColumns)
127  .collect(Collectors.toList()),
128  values
129  );
130  }
131 
132  private static final int DEFAULT_ROW_SIZE = 15;
133  private static final int DEFAULT_COL_SIZE = 10;
134  private static final int DEFAULT_ROW_PADDING = 1;
135  private static final int DEFAULT_COL_OFFSET = 1;
136 
138  private final int colOffset;
139  private final int rowPadding;
140  private final int colSize;
141  private final int rowSize;
142  private final String chartTitle;
143  private final String sheetName;
144  private final List<BarChartSeries> categories;
145  private final String keyColumnHeader;
146 
157  public BarChartExport(String keyColumnHeader,
158  String valueFormatString,
159  String chartTitle,
160  List<BarChartSeries> categories) {
161  this(keyColumnHeader, valueFormatString, chartTitle, chartTitle, categories,
163  }
164 
180  public BarChartExport(String keyColumnHeader, String valueFormatString,
181  String chartTitle, String sheetName,
182  List<BarChartSeries> categories,
183  int colOffset, int rowPadding, int colSize, int rowSize) {
184 
185  this.keyColumnHeader = keyColumnHeader;
186  this.tableExport = getTableModel(categories, keyColumnHeader, chartTitle);
187  this.colOffset = colOffset;
188  this.rowPadding = rowPadding;
189  this.colSize = colSize;
190  this.rowSize = rowSize;
191  this.chartTitle = chartTitle;
192  this.sheetName = sheetName;
193  this.categories = categories;
194  }
195 
196  @Override
197  public String getSheetName() {
198  return sheetName;
199  }
200 
201  @Override
202  public void renderSheet(Sheet sheet, ExcelExport.WorksheetEnv env) throws ExcelExport.ExcelExportException {
203  write(sheet, 0, 0, env);
204  }
205 
206  @Override
207  public ItemDimensions write(Sheet sheet, int rowStart, int colStart, ExcelExport.WorksheetEnv env) throws ExcelExportException {
208  if (!(sheet instanceof XSSFSheet)) {
209  throw new ExcelExportException("Sheet must be an XSSFSheet in order to write.");
210  }
211 
212  XSSFSheet xssfSheet = (XSSFSheet) sheet;
213 
214  // write pie chart table data
215  ItemDimensions tableDimensions = tableExport.write(xssfSheet, rowStart + rowPadding, colStart, env);
216 
217  XSSFDrawing drawing = xssfSheet.createDrawingPatriarch();
218 
219  int chartColStart = colStart + categories.size() + 1 + colOffset;
220 
221  //createAnchor has arguments of (int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2);
222  XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, chartColStart, rowStart + rowPadding, chartColStart + colSize + 1, rowStart + rowSize + 1);
223 
224  XSSFChart chart = drawing.createChart(anchor);
225  chart.setTitleText(chartTitle);
226  chart.setTitleOverlay(false);
227  XDDFChartLegend legend = chart.getOrAddLegend();
228  legend.setPosition(LegendPosition.BOTTOM);
229 
230  // Use a category axis for the bottom axis.
231  XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
232  bottomAxis.setTitle(keyColumnHeader);
233  XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
234  leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
235  leftAxis.setVisible(false);
236 
237  XDDFBarChartData data = (XDDFBarChartData) chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
238  data.setBarGrouping(BarGrouping.STACKED);
239 
240  XDDFDataSource<String> headerSource = XDDFDataSourcesFactory.fromStringCellRange(xssfSheet,
241  new CellRangeAddress(tableDimensions.getRowStart() + 1, tableDimensions.getRowEnd(),
242  tableDimensions.getColStart(), tableDimensions.getColStart()));
243 
244  data.setBarDirection(BarDirection.COL);
245 
246  // set data for each series and set color if applicable
247  for (int i = 0; i < categories.size(); i++) {
248  XDDFChartData.Series series = data.addSeries(headerSource,
249  XDDFDataSourcesFactory.fromNumericCellRange(xssfSheet,
250  new CellRangeAddress(tableDimensions.getRowStart() + 1, tableDimensions.getRowEnd(),
251  tableDimensions.getColStart() + 1 + i, tableDimensions.getColStart() + 1 + i)));
252 
253  series.setTitle(categories.size() > i && categories.get(i).getKey() != null ? categories.get(i).getKey().toString() : "", null);
254  if (categories.get(i).getColor() != null) {
255  Color color = categories.get(i).getColor();
256  byte[] colorArrARGB = ByteBuffer.allocate(4).putInt(color.getRGB()).array();
257  byte[] colorArrRGB = new byte[]{colorArrARGB[1], colorArrARGB[2], colorArrARGB[3]};
258  XDDFSolidFillProperties fill = new XDDFSolidFillProperties(XDDFColor.from(colorArrRGB));
259  XDDFShapeProperties properties = series.getShapeProperties();
260  if (properties == null) {
261  properties = new XDDFShapeProperties();
262  }
263  properties.setFillProperties(fill);
264  series.setShapeProperties(properties);
265  }
266  }
267 
268  chart.plot(data);
269 
270  return new ItemDimensions(rowStart, colStart, Math.max(tableDimensions.getRowEnd(), rowStart + rowSize) + rowPadding, chartColStart + colSize);
271  }
272 
273 }
BarChartExport(String keyColumnHeader, String valueFormatString, String chartTitle, List< BarChartSeries > categories)
static ExcelTableExport< Pair< Object, List< Double > >,?extends ExcelCellModel > getTableModel(List< BarChartSeries > categories, String keyColumnHeader, String chartTitle)
BarChartExport(String keyColumnHeader, String valueFormatString, String chartTitle, String sheetName, List< BarChartSeries > categories, int colOffset, int rowPadding, int colSize, int rowSize)
ItemDimensions write(Sheet sheet, int rowStart, int colStart, ExcelExport.WorksheetEnv env)
final ExcelTableExport< Pair< Object, List< Double > >,?extends ExcelCellModel > tableExport
void renderSheet(Sheet sheet, ExcelExport.WorksheetEnv env)
ItemDimensions write(Sheet sheet, int rowStart, int colStart, ExcelExport.WorksheetEnv env)

Copyright © 2012-2021 Basis Technology. Generated on: Fri Aug 6 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.