From fd0838f4b4fcb45a83036a67e78ab1eeaa1aacac Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 24 Feb 2021 15:44:41 -0500 Subject: [PATCH] beginnings of excel export --- .../datasourcesummary/uiutils/CellModel.java | 66 ++++++++ .../uiutils/ColumnModel.java | 71 ++++++++ .../uiutils/DefaultCellModel.java | 159 ++++++++++++++++++ .../uiutils/ExcelCellModel.java | 18 ++ .../uiutils/ExcelExport.java | 156 +++++++++++++++++ .../uiutils/GuiCellModel.java | 73 ++++++++ .../uiutils/TableTemplate.java | 31 ++++ 7 files changed, 574 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModel.java create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ColumnModel.java create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultCellModel.java create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelCellModel.java create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelExport.java create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/GuiCellModel.java create mode 100644 Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/TableTemplate.java diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModel.java new file mode 100644 index 0000000000..c5af9ed5cb --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModel.java @@ -0,0 +1,66 @@ +/* + * 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 javax.swing.JLabel; + +/** + * Basic interface for a cell model. + */ +public interface CellModel { + + /** + * Describes the horizontal alignment. + */ + public enum HorizontalAlign { + LEFT(JLabel.LEFT), + CENTER(JLabel.CENTER), + RIGHT(JLabel.RIGHT); + + private final int jlabelAlignment; + + /** + * Constructor for a HorizontalAlign enum. + * + * @param jlabelAlignment The corresponding JLabel horizontal alignment + * number. + */ + HorizontalAlign(int jlabelAlignment) { + this.jlabelAlignment = jlabelAlignment; + } + + /** + * @return The corresponding JLabel horizontal alignment (i.e. + * JLabel.LEFT). + */ + int getJLabelAlignment() { + return this.jlabelAlignment; + } + } + + /** + * @return The root data object. + */ + Object getData(); + + /** + * @return The text to be shown in the cell. + */ + default String getText() { + Object data = getData(); + return (data == null) ? null : data.toString(); + } + + /** + * @return The tooltip (if any) to be displayed in the cell. + */ + String getTooltip(); + + /** + * @return The horizontal alignment for the text in the cell. + */ + HorizontalAlign getHorizontalAlignment(); +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ColumnModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ColumnModel.java new file mode 100644 index 0000000000..e4fb8ca267 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ColumnModel.java @@ -0,0 +1,71 @@ +/* + * 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 java.util.function.Function; + +/** + * + * @author gregd + */ +/** + * Describes aspects of a column which can be used with getTableModel or + * getJTablePanel. 'T' represents the object that will represent rows in the + * table. + */ +public class ColumnModel { + + private final String headerTitle; + private final Function cellRenderer; + private final Integer width; + + /** + * Constructor for a DataResultColumnModel. + * + * @param headerTitle The title for the column. + * @param cellRenderer The method that generates a CellModel for the column + * based on the data. + */ + public ColumnModel(String headerTitle, Function cellRenderer) { + this(headerTitle, cellRenderer, null); + } + + /** + * Constructor for a DataResultColumnModel. + * + * @param headerTitle The title for the column. + * @param cellRenderer The method that generates a CellModel for the column + * based on the data. + * @param width The preferred width of the column. + */ + public ColumnModel(String headerTitle, Function cellRenderer, Integer width) { + this.headerTitle = headerTitle; + this.cellRenderer = cellRenderer; + this.width = width; + } + + /** + * @return The title for the column. + */ + public String getHeaderTitle() { + return headerTitle; + } + + /** + * @return The method that generates a CellModel for the column based on the + * data. + */ + public Function getCellRenderer() { + return cellRenderer; + } + + /** + * @return The preferred width of the column (can be null). + */ + public Integer getWidth() { + return width; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultCellModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultCellModel.java new file mode 100644 index 0000000000..1af3d25b99 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultCellModel.java @@ -0,0 +1,159 @@ +/* + * 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 java.awt.Insets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * + * @author gregd + */ +/** + * The default cell model. + */ +public class DefaultCellModel implements GuiCellModel, ExcelCellModel { + private final T data; + private final Function stringConverter; + String tooltip; + CellModel.HorizontalAlign horizontalAlignment; + Insets insets; + List popupMenu; + Supplier> menuItemSupplier; + private final String excelFormatString; + + + /** + * Main constructor. + * + * @param text The text to be displayed in the cell. + */ + public DefaultCellModel(T data) { + this(data, null, null); + } + + public DefaultCellModel(T data, Function stringConverter, String excelFormatString) { + this.data = data; + this.stringConverter = stringConverter; + this.excelFormatString = excelFormatString; + } + + + + @Override + public T getData() { + return this.data; + } + + @Override + public String getExcelFormatString() { + return this.excelFormatString; + } + + @Override + public String getText() { + if (this.stringConverter == null) { + return this.data == null ? "" : this.data.toString(); + } else { + return this.stringConverter.apply(this.data); + } + } + + @Override + public String getTooltip() { + return tooltip; + } + + /** + * Sets the tooltip for this cell model. + * + * @param tooltip The tooltip for the cell model. + * + * @return As a utility, returns this. + */ + public DefaultCellModel setTooltip(String tooltip) { + this.tooltip = tooltip; + return this; + } + + @Override + public HorizontalAlign getHorizontalAlignment() { + return horizontalAlignment; + } + + /** + * Sets the horizontal alignment for this cell model. + * + * @param alignment The horizontal alignment for the cell model. + * + * @return As a utility, returns this. + */ + public DefaultCellModel setHorizontalAlignment(CellModel.HorizontalAlign alignment) { + this.horizontalAlignment = alignment; + return this; + } + + @Override + public Insets getInsets() { + return insets; + } + + /** + * Sets the insets for the text within the cell + * + * @param insets The insets. + * + * @return As a utility, returns this. + */ + public DefaultCellModel setInsets(Insets insets) { + this.insets = insets; + return this; + } + + @Override + public List getPopupMenu() { + if (popupMenu != null) { + return Collections.unmodifiableList(popupMenu); + } + + if (menuItemSupplier != null) { + return this.menuItemSupplier.get(); + } + + return null; + } + + /** + * Sets a function to lazy load the popup menu items. + * + * @param menuItemSupplier The lazy load function for popup items. + * @return + */ + public DefaultCellModel setPopupMenuRetriever(Supplier> menuItemSupplier) { + this.menuItemSupplier = menuItemSupplier; + return this; + } + + /** + * Sets the list of items for a popup menu + * + * @param popupMenu + * @return As a utility, returns this. + */ + public DefaultCellModel setPopupMenu(List popupMenu) { + this.popupMenu = popupMenu == null ? null : new ArrayList<>(popupMenu); + return this; + } + + @Override + public String toString() { + return getText(); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelCellModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelCellModel.java new file mode 100644 index 0000000000..befab77f69 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelCellModel.java @@ -0,0 +1,18 @@ +/* + * 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; + +/** + * Basic interface for a cell model. + */ +public interface ExcelCellModel extends CellModel { + + /** + * @return The format string to be used with Apache POI during excel export + * or null if none necessary. + */ + String getExcelFormatString(); +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelExport.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelExport.java new file mode 100644 index 0000000000..365412e71e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelExport.java @@ -0,0 +1,156 @@ +/* + * 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 java.io.FileOutputStream; +import java.io.IOException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.logging.Logger; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +/** + * + * @author gregd + */ +public class ExcelExport { + + private static final Logger logger = Logger.getLogger(ExcelExport.class.getName()); + + public static void main(String[] args) throws IOException { + // Create a Workbook + Workbook workbook = new XSSFWorkbook(); // new HSSFWorkbook() for generating `.xls` file + + // Create a Font for styling header cells + Font headerFont = workbook.createFont(); + headerFont.setBold(true); + //headerFont.setFontHeightInPoints((short) 14); + + // Create a CellStyle with the font + CellStyle headerCellStyle = workbook.createCellStyle(); + headerCellStyle.setFont(headerFont); + + createSheet(workbook, headerCellStyle, GeolocationDTO.TEMPLATE_1, dtos1); + createSheet(workbook, headerCellStyle, GeolocationDTO.TEMPLATE_2, dtos2); + + // Write the output to a file + FileOutputStream fileOut = new FileOutputStream("C:\\Users\\gregd\\Desktop\\datasourcesummary-export.xlsx"); + workbook.write(fileOut); + fileOut.close(); + + // Closing the workbook + workbook.close(); + } + + public static Sheet createSheet( + Workbook workbook, CellStyle headerCellStyle, TableTemplate tableTemplate, List data) + throws IllegalArgumentException { + + if (workbook == null || tableTemplate == null) { + throw new IllegalArgumentException("workbook and tableTemplate parameters cannot be null"); + } + + List> columns = tableTemplate.getColumns() != null + ? tableTemplate.getColumns() + : Collections.emptyList(); + + List safeData = data == null ? Collections.emptyList() : data; + + Sheet sheet = workbook.createSheet(tableTemplate.getTabName()); + + // Create a header row + Row headerRow = sheet.createRow(0); + + // Create header cells + for (int i = 0; i < columns.size(); i++) { + Cell cell = headerRow.createCell(i); + cell.setCellValue(columns.get(i).getHeaderTitle()); + cell.setCellStyle(headerCellStyle); + } + + // freeze header row + sheet.createFreezePane(0, 1); + + // Create Cell Style for each column (if one is needed) + Map cellStyles = new HashMap<>(); + + for (int rowNum = 0; rowNum < safeData.size(); rowNum++) { + T rowData = safeData.get(rowNum); + Row row = sheet.createRow(rowNum + 1); + + for (int colNum = 0; colNum < columns.size(); colNum++) { + ColumnModel colModel = columns.get(colNum); + ExcelCellModel cellModel = colModel.getCellRenderer().apply(rowData); + String formatString = cellModel.getExcelFormatString(); + + Optional cellStyle = (formatString == null) + ? Optional.empty() + : Optional.of(cellStyles.computeIfAbsent(formatString, (k) -> getCellStyles(workbook, formatString))); + + createCell(row, colNum, cellModel, cellStyle); + } + } + + // Resize all columns to fit the content size + for (int i = 0; i < columns.size(); i++) { + sheet.autoSizeColumn(i); + } + + return sheet; + } + + private static Cell createCell(Row row, int colNum, ExcelCellModel cellModel, Optional cellStyle) { + Object cellData = cellModel.getData(); + + Cell cell = row.createCell(colNum); + if (cellData instanceof Calendar) { + cell.setCellValue((Calendar) cellData); + } else if (cellData instanceof Date) { + cell.setCellValue((Date) cellData); + } else if (cellData instanceof Double) { + cell.setCellValue((Double) cellData); + } else if (cellData instanceof LocalDate) { + cell.setCellValue((LocalDate) cellData); + } else if (cellData instanceof LocalDateTime) { + cell.setCellValue((LocalDateTime) cellData); + } else if (cellData instanceof String) { + cell.setCellValue((String) cellData); + } else if (cellData instanceof Short) { + cell.setCellValue((Short) cellData); + } else if (cellData instanceof Integer) { + cell.setCellValue((Integer) cellData); + } else if (cellData instanceof Long) { + cell.setCellValue((Long) cellData); + } else if (cellData instanceof Float) { + cell.setCellValue((Float) cellData); + } else { + cell.setCellValue(cellModel.getText()); + } + + cellStyle.ifPresent((cs) -> cell.setCellStyle(cs)); + return cell; + } + + private static CellStyle getCellStyles(Workbook workbook, String formatString) { + CellStyle cellStyle = workbook.createCellStyle(); + cellStyle.setDataFormat(workbook.getCreationHelper().createDataFormat().getFormat(formatString)); + return cellStyle; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/GuiCellModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/GuiCellModel.java new file mode 100644 index 0000000000..f6d2e0b870 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/GuiCellModel.java @@ -0,0 +1,73 @@ +/* + * 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 java.awt.Insets; +import java.util.List; + +/** + * Basic interface for a cell model. + */ +public interface GuiCellModel extends CellModel { + + /** + * A menu item to be used within a popup menu. + */ + public interface MenuItem { + + /** + * @return The title for that popup menu item. + */ + String getTitle(); + + /** + * @return The action if that popup menu item is clicked. + */ + Runnable getAction(); + } + + /** + * Default implementation of a menu item. + */ + public static class DefaultMenuItem implements MenuItem { + + private final String title; + private final Runnable action; + + /** + * Main constructor. + * + * @param title The title for the menu item. + * @param action The action should the menu item be clicked. + */ + public DefaultMenuItem(String title, Runnable action) { + this.title = title; + this.action = action; + } + + @Override + public String getTitle() { + return title; + } + + @Override + public Runnable getAction() { + return action; + } + } + + /** + * @return The insets for the cell text. + */ + Insets getInsets(); + + /** + * @return The popup menu associated with this cell or null if no popup menu + * should be shown for this cell. + */ + List getPopupMenu(); + +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/TableTemplate.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/TableTemplate.java new file mode 100644 index 0000000000..0432e99c62 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/TableTemplate.java @@ -0,0 +1,31 @@ +/* + * 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 java.util.List; + +/** + * + * @author gregd + */ +public class TableTemplate { + + private final List> columns; + private final String tabName; + + public TableTemplate(List> columns, String tabName) { + this.columns = columns; + this.tabName = tabName; + } + + public List> getColumns() { + return columns; + } + + public String getTabName() { + return tabName; + } +}