commenting

This commit is contained in:
Greg DiCristofaro 2021-03-17 11:50:02 -04:00
parent 26091c4c58
commit 72c1e5a224
11 changed files with 530 additions and 110 deletions

View File

@ -47,7 +47,7 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.EventUpdateHandler;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelExportException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport.ExcelCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.DefaultMenuItem;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;

View File

@ -37,7 +37,6 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.ui.SizeRepresentationUtil.SizeUnit;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
@ -45,6 +44,7 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ExcelItemExportable;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.KeyValueItemExportable;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.SingleCellExportable;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.TitledExportable;
@ -61,6 +61,9 @@ import org.sleuthkit.datamodel.TskCoreException;
})
class ContainerPanel extends BaseDataSourceSummaryPanel {
/**
* View model data for data source images.
*/
private static class ImageViewModel {
private final long unallocatedSize;
@ -75,7 +78,20 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
private final String sha1Hash;
private final String sha256Hash;
public ImageViewModel(long unallocatedSize, long size, long sectorSize,
/**
* Main constructor.
*
* @param unallocatedSize Size in bytes of unallocated space.
* @param size Total size in bytes.
* @param sectorSize Sector size in bytes.
* @param timeZone The time zone.
* @param imageType The type of image.
* @param paths The source paths for the image.
* @param md5Hash The md5 hash or null.
* @param sha1Hash The sha1 hash or null.
* @param sha256Hash The sha256 hash or null.
*/
ImageViewModel(long unallocatedSize, long size, long sectorSize,
String timeZone, String imageType, List<String> paths, String md5Hash,
String sha1Hash, String sha256Hash) {
this.unallocatedSize = unallocatedSize;
@ -89,44 +105,73 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
this.sha256Hash = sha256Hash;
}
public long getUnallocatedSize() {
/**
* @return Size in bytes of unallocated space.
*/
long getUnallocatedSize() {
return unallocatedSize;
}
public long getSize() {
/**
* @return Total size in bytes.
*/
long getSize() {
return size;
}
public long getSectorSize() {
/**
* @return Sector size in bytes.
*/
long getSectorSize() {
return sectorSize;
}
public String getTimeZone() {
/**
* @return The time zone.
*/
String getTimeZone() {
return timeZone;
}
public String getImageType() {
/**
* @return The type of image.
*/
String getImageType() {
return imageType;
}
public List<String> getPaths() {
/**
* @return The source paths for the image.
*/
List<String> getPaths() {
return paths;
}
public String getMd5Hash() {
/**
* @return The md5 hash or null.
*/
String getMd5Hash() {
return md5Hash;
}
public String getSha1Hash() {
/**
* @return The sha1 hash or null.
*/
String getSha1Hash() {
return sha1Hash;
}
public String getSha256Hash() {
/**
* @return The sha256 hash or null.
*/
String getSha256Hash() {
return sha256Hash;
}
}
/**
* View model for container data.
*/
private static class ContainerViewModel {
private final String displayName;
@ -135,6 +180,17 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
private final String acquisitionDetails;
private final ImageViewModel imageViewModel;
/**
* Main constructor.
*
* @param displayName The display name for this data source.
* @param originalName The original name for this data source.
* @param deviceIdValue The device id value for this data source.
* @param acquisitionDetails The acquisition details for this data
* source or null.
* @param imageViewModel If the data source is an image, the image view
* model for this data source or null if non-image.
*/
ContainerViewModel(String displayName, String originalName, String deviceIdValue,
String acquisitionDetails, ImageViewModel imageViewModel) {
this.displayName = displayName;
@ -144,22 +200,38 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
this.imageViewModel = imageViewModel;
}
/**
* @return The display name for this data source.
*/
String getDisplayName() {
return displayName;
}
/**
* @return The original name for this data source.
*/
String getOriginalName() {
return originalName;
}
/**
* @return The device id value for this data source.
*/
String getDeviceId() {
return deviceIdValue;
}
/**
* @return The acquisition details for this data source or null.
*/
String getAcquisitionDetails() {
return acquisitionDetails;
}
/**
* @return If the data source is an image, the image view model for this
* data source or null if non-image.
*/
ImageViewModel getImageViewModel() {
return imageViewModel;
}
@ -241,11 +313,29 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
fetchInformation(dataFetchComponents, dataSource);
}
/**
* A means of retrieving data that could potentially throw an exception.
*/
private interface Retriever<O> {
/**
* Retrieves data of a certain type and possibly throws an exception.
*
* @return The data type.
* @throws TskCoreException
* @throws SleuthkitCaseProviderException
* @throws SQLException
*/
O retrieve() throws TskCoreException, SleuthkitCaseProviderException, SQLException;
}
/**
* Retrieves data of a particular type and handles any exceptions that may
* be thrown by logging.
*
* @param retriever The retrieving function.
* @return The retrieved data.
*/
private static <O> O retrieve(Retriever<O> retriever) {
try {
return retriever.retrieve();
@ -255,6 +345,14 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
}
}
/**
* Generates a container view model object containing data to display about
* the data source.
*
* @param containerSummary The service providing data about the data source.
* @param ds The data source.
* @return The generated view model.
*/
private static ContainerViewModel getContainerViewModel(ContainerSummary containerSummary, DataSource ds) {
if (ds == null) {
return null;
@ -269,6 +367,14 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
);
}
/**
* Generates an image view model object containing data to display about the
* image.
*
* @param containerSummary The service providing data about the image.
* @param image The image.
* @return The generated view model.
*/
private static ImageViewModel getImageViewModel(ContainerSummary containerSummary, Image image) {
if (image == null) {
return null;
@ -287,6 +393,11 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
return new ImageViewModel(unallocSize, size, sectorSize, timeZone, imageType, paths, md5, sha1, sha256);
}
/**
* Update the swing components with fetched data.
*
* @param viewModel The data source view model data.
*/
private void updateDetailsPanelData(ContainerViewModel viewModel) {
clearTableValues();
if (viewModel == null) {
@ -307,6 +418,9 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
this.repaint();
}
/**
* Sets image-only fields to N/A.
*/
@Messages({
"ContainerPanel_setFieldsForNonImageDataSource_na=N/A"
})
@ -326,6 +440,11 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
sha256HashValue.setText(NA);
}
/**
* Sets fields for images.
*
* @param viewModel The image view model data.
*/
private void setFieldsForImage(ImageViewModel viewModel) {
unallocatedSizeValue.setText(SizeRepresentationUtil.getSizeString(viewModel.getUnallocatedSize()));
imageTypeValue.setText(viewModel.getImageType());
@ -361,29 +480,25 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
((DefaultTableModel) filePathsTable.getModel()).setRowCount(0);
}
private static List<KeyValueItemExportable> getAcquisitionDetails(String acquisitionDetails) {
/**
* Divides acquisition details into key/value pairs to be displayed in
* separate cells in an excel export.
*
* @param acquisitionDetails The acquisition details.
* @return The list of key value pairs that can be incorporated into the
* excel export.
*/
private static List<? extends ExcelItemExportable> getAcquisitionDetails(String acquisitionDetails) {
if (StringUtils.isBlank(acquisitionDetails)) {
return Collections.emptyList();
} else {
return Stream.of(acquisitionDetails.split("\\r?\\n"))
.map((line) -> {
if (StringUtils.isBlank(line)) {
return null;
} else {
int colonIdx = line.indexOf(':');
if (colonIdx >= 0) {
return new KeyValueItemExportable(new DefaultCellModel<>(line.substring(0, colonIdx + 1).trim()),
new DefaultCellModel<>(line.substring(colonIdx + 1, line.length()).trim()));
} else {
return new KeyValueItemExportable(new DefaultCellModel<>(""), new DefaultCellModel<>(line));
}
}
})
.map((line) -> (StringUtils.isBlank(line)) ? null : new SingleCellExportable(line))
.filter(item -> item != null)
.collect(Collectors.toList());
}
}
@Override
@Messages({
"ContainerPanel_export_displayName=Display Name:",

View File

@ -68,7 +68,6 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
*
* @param tabTitle The title of the tab.
* @param panel The component to be displayed in the tab.
* @param notifyParentClose Notifies parent to trigger a close.
*/
DataSourceTab(String tabTitle, BaseDataSourceSummaryPanel panel) {
this(tabTitle, panel, panel::setDataSource, panel::getExports, panel::close);
@ -138,10 +137,10 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
private Runnable notifyParentClose = null;
private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel();
// create an export panel whose button triggers the export to XLSX action
private final ExportPanel exportPanel = new ExportPanel();
private final List<DataSourceTab> tabs = Arrays.asList(
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_typesTab_title(), new TypesPanel()),
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), new UserActivityPanel()),
@ -165,10 +164,10 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
null,
null)
);
// the action that does the export
private final ExcelExportAction exportAction = new ExcelExportAction(tabs);
private DataSource dataSource = null;
private CardLayout cardLayout;
@ -222,7 +221,7 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
// set this to no datasource initially
cardLayout.show(this, NO_DATASOURCE_PANE);
// set action for when user requests xlsx export
exportPanel.setXlsxExportAction(() -> exportAction.accept(getDataSource()));
}
@ -270,7 +269,6 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), caseEventsListener);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always

View File

@ -1,7 +1,20 @@
/*
* 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.
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datasourcesummary.ui;
@ -17,8 +30,6 @@ import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -33,8 +44,7 @@ import org.sleuthkit.datamodel.IngestModuleInfo;
import org.sleuthkit.datamodel.TskCoreException;
/**
*
* @author gregd
* Class that handles exporting information in IngestJobInfoPanel to excel.
*/
@Messages({
"IngestJobExcelExport_startTimeColumn=Start Time",
@ -46,6 +56,9 @@ import org.sleuthkit.datamodel.TskCoreException;
})
class IngestJobExcelExport {
/**
* An entry to display in an excel export.
*/
private static class IngestJobEntry {
private final Date startTime;
@ -54,6 +67,15 @@ class IngestJobExcelExport {
private final String ingestModule;
private final String ingestModuleVersion;
/**
* Main constructor.
*
* @param startTime The ingest start time.
* @param endTime The ingest stop time.
* @param status The ingest status.
* @param ingestModule The ingest module.
* @param ingestModuleVersion The ingest module version.
*/
IngestJobEntry(Date startTime, Date endTime, String status, String ingestModule, String ingestModuleVersion) {
this.startTime = startTime;
this.endTime = endTime;
@ -62,22 +84,37 @@ class IngestJobExcelExport {
this.ingestModuleVersion = ingestModuleVersion;
}
/**
* @return The ingest start time.
*/
Date getStartTime() {
return startTime;
}
/**
* @return The ingest stop time.
*/
Date getEndTime() {
return endTime;
}
/**
* @return The ingest status.
*/
String getStatus() {
return status;
}
/**
* @return The ingest module.
*/
String getIngestModule() {
return ingestModule;
}
/**
* @return The ingest module version.
*/
String getIngestModuleVersion() {
return ingestModuleVersion;
}
@ -87,6 +124,7 @@ class IngestJobExcelExport {
private static final String DATETIME_FORMAT_STR = "yyyy/MM/dd HH:mm:ss";
private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat(DATETIME_FORMAT_STR, Locale.getDefault());
// columns in the excel export table to be created.
private static final List<ColumnModel<IngestJobEntry, DefaultCellModel<?>>> COLUMNS = Arrays.asList(
new ColumnModel<>(
Bundle.IngestJobExcelExport_startTimeColumn(),
@ -105,11 +143,23 @@ class IngestJobExcelExport {
(entry) -> new DefaultCellModel<>(entry.getIngestModuleVersion()))
);
/**
* Retrieves data for a date cell.
*
* @param date The date.
* @return The data cell to be used in the excel export.
*/
private static DefaultCellModel<?> getDateCell(Date date) {
Function<Date, String> dateParser = (dt) -> dt == null ? "" : DATETIME_FORMAT.format(dt);
return new DefaultCellModel<>(date, dateParser, DATETIME_FORMAT_STR);
}
/**
* Retrieves all the ingest job modules and versions for a job.
*
* @param job The ingest job.
* @return All of the corresponding entries sorted by module name.
*/
private static List<IngestJobEntry> getEntries(IngestJobInfo job) {
List<IngestModuleInfo> infoList = job.getIngestModuleInfo();
if (infoList == null) {
@ -135,6 +185,13 @@ class IngestJobExcelExport {
}
}
/**
* For output, show ingest job details in first row present. Otherwise, set
* to null.
*
* @param list The list of entries for an ingest job.
* @return The stream of entries to be displayed.
*/
private static Stream<IngestJobEntry> showFirstRowOnly(List<IngestJobEntry> list) {
return IntStream.range(0, list.size())
.mapToObj(idx -> {
@ -148,6 +205,12 @@ class IngestJobExcelExport {
}
/**
* Returns a list of sheets to be exported for the Ingest History tab.
*
* @param dataSource The data source.
* @return The list of sheets to be included in an export.
*/
static List<ExcelSheetExport> getExports(DataSource dataSource) {
if (dataSource == null) {
return Collections.emptyList();
@ -167,6 +230,7 @@ class IngestJobExcelExport {
List<IngestJobEntry> toDisplay = info.stream()
.filter(job -> job != null && dataSource.getId() == job.getObjectId())
.sorted((a, b) -> {
// sort ingest jobs by time.
boolean aIsNull = a.getStartDateTime() == null;
boolean bIsNull = b.getStartDateTime() == null;
if (aIsNull || bIsNull) {
@ -183,4 +247,7 @@ class IngestJobExcelExport {
return Arrays.asList(new ExcelTableExport<>(Bundle.IngestJobExcelExport_sheetName(), COLUMNS, toDisplay));
}
private IngestJobExcelExport() {
}
}

View File

@ -33,7 +33,9 @@ public final class SizeRepresentationUtil {
private static final int SIZE_CONVERSION_CONSTANT = 1000;
private static final DecimalFormat APPROXIMATE_SIZE_FORMAT = new DecimalFormat("#.##");
// based on https://www.mrexcel.com/board/threads/how-do-i-format-cells-to-show-gb-mb-kb.140135/
/**
* A size unit corresponding to orders of magnitude of bytes (kilobyte, gigabytes, etc.).
*/
@NbBundle.Messages({
"SizeRepresentationUtil_units_bytes=bytes",
"SizeRepresentationUtil_units_kilobytes=KB",
@ -54,20 +56,37 @@ public final class SizeRepresentationUtil {
private final String excelFormatString;
private final long divisor;
/**
* Main constructor.
* @param suffix The string suffix to use for size unit.
* @param excelFormatString The excel format string to use for this size unit.
* @param power The power of 1000 of bytes for this size unit.
*/
SizeUnit(String suffix, String excelFormatString, int power) {
this.suffix = suffix;
// based on https://www.mrexcel.com/board/threads/how-do-i-format-cells-to-show-gb-mb-kb.140135/
this.excelFormatString = String.format("%s \"%s\"", excelFormatString, suffix);
this.divisor = (long) Math.pow(SIZE_CONVERSION_CONSTANT, power);
}
/**
* @return The string suffix to use for size unit.
*/
public String getSuffix() {
return suffix;
}
/**
* @return The excel format string to use for this size unit.
*/
public String getExcelFormatString() {
return excelFormatString;
}
/**
* @return The divisor to convert from bytes to this unit.
*/
public long getDivisor() {
return divisor;
}
@ -85,6 +104,11 @@ public final class SizeRepresentationUtil {
return getSizeString(size, APPROXIMATE_SIZE_FORMAT, true);
}
/**
* Determines the relevant size unit that should be used for a particular size.
* @param size The size in bytes.
* @return The relevant size unit.
*/
static SizeUnit getSizeUnit(Long size) {
if (size == null) {
return SizeUnit.values()[0];
@ -135,7 +159,11 @@ public final class SizeRepresentationUtil {
}
}
/**
* Returns a default cell model using size units.
* @param bytes The number of bytes.
* @return The default cell model.
*/
static DefaultCellModel<?> getBytesCell(Long bytes) {
if (bytes == null) {
return new DefaultCellModel<>("");

View File

@ -31,7 +31,6 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TypesSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.MimeTypeSummary;
@ -50,7 +49,6 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel.PieChartItem;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
@ -173,9 +171,8 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L;
private static final DecimalFormat INTEGER_SIZE_FORMAT = new DecimalFormat("#");
private static final String COMMA_FORMAT_STR = "#,###";
private static final DecimalFormat COMMA_FORMATTER = new DecimalFormat(COMMA_FORMAT_STR);
private static final Logger logger = Logger.getLogger(TypesPanel.class.getName());
private static final Color IMAGES_COLOR = new Color(156, 39, 176);
private static final Color VIDEOS_COLOR = Color.YELLOW;
@ -412,14 +409,31 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
return longVal == null ? "0" : COMMA_FORMATTER.format(longVal);
}
/**
* Returns a key value pair to be exported in a sheet.
*
* @param fetcher The means of fetching the data.
* @param key The key to use.
* @param dataSource The data source containing the data.
* @return The key value pair to be exported.
*/
private static KeyValueItemExportable getStrExportable(DataFetcher<DataSource, String> fetcher, String key, DataSource dataSource) {
String result = getFetchResult(fetcher, "Types", dataSource);
return (result == null) ? null : new KeyValueItemExportable(key, new DefaultCellModel<>(result));
}
/**
* Returns a key value pair to be exported in a sheet formatting the long
* with commas separated by orders of 1000.
*
* @param fetcher The means of fetching the data.
* @param key The string key for this key value pair.
* @param dataSource The data source.
* @return The key value pair.
*/
private static KeyValueItemExportable getCountExportable(DataFetcher<DataSource, Long> fetcher, String key, DataSource dataSource) {
Long count = getFetchResult(fetcher, "Types", dataSource);
return (count == null) ? null : new KeyValueItemExportable(key,
return (count == null) ? null : new KeyValueItemExportable(key,
new DefaultCellModel<Long>(count, COMMA_FORMATTER::format, COMMA_FORMAT_STR));
}

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
import javax.swing.JLabel;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
/**
* Basic interface for a cell model.
@ -29,20 +30,23 @@ public interface CellModel {
* Describes the horizontal alignment.
*/
public enum HorizontalAlign {
LEFT(JLabel.LEFT),
CENTER(JLabel.CENTER),
RIGHT(JLabel.RIGHT);
LEFT(JLabel.LEFT, HorizontalAlignment.LEFT),
CENTER(JLabel.CENTER, HorizontalAlignment.CENTER),
RIGHT(JLabel.RIGHT, HorizontalAlignment.RIGHT);
private final int jlabelAlignment;
private final HorizontalAlignment poiAlignment;
/**
* Constructor for a HorizontalAlign enum.
*
* @param jlabelAlignment The corresponding JLabel horizontal alignment
* number.
* @param poiAlignment Horizontal alignment for Apache POI.
*/
HorizontalAlign(int jlabelAlignment) {
HorizontalAlign(int jlabelAlignment, HorizontalAlignment poiAlignment) {
this.jlabelAlignment = jlabelAlignment;
this.poiAlignment = poiAlignment;
}
/**
@ -52,6 +56,13 @@ public interface CellModel {
int getJLabelAlignment() {
return this.jlabelAlignment;
}
/**
* @return Horizontal alignment for Apache POI.
*/
HorizontalAlignment getPoiAlignment() {
return poiAlignment;
}
}
/**

View File

@ -24,21 +24,21 @@ import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport.ExcelCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelCellModel;
/**
* The default cell model.
*/
public class DefaultCellModel<T> implements GuiCellModel, ExcelCellModel {
private final T data;
private final Function<T, String> stringConverter;
final T data;
final Function<T, String> stringConverter;
String tooltip;
CellModel.HorizontalAlign horizontalAlignment;
Insets insets;
List<MenuItem> popupMenu;
Supplier<List<MenuItem>> menuItemSupplier;
private final String excelFormatString;
final String excelFormatString;
/**
* Main constructor.

View File

@ -26,8 +26,8 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
@ -37,6 +37,7 @@ import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModel.HorizontalAlign;
/**
* Class for handling Excel exporting.
@ -68,6 +69,87 @@ public class ExcelExport {
}
}
/**
* A cell style key that can be used with the WorksheetEnv to generate a
* cell style to be used in a POI excel document.
*/
static class CellStyleKey {
private final String formatString;
private final CellStyle cellStyle;
private final HorizontalAlign alignment;
/**
* Main constructor.
*
* @param formatString The format string or null if no special
* formatting.
* @param cellStyle The base cell style or null if default is to be
* used.
* @param alignment The horizontal alignment or null if default is to be
* used.
*/
CellStyleKey(String formatString, CellStyle cellStyle, HorizontalAlign alignment) {
this.formatString = formatString;
this.cellStyle = cellStyle;
this.alignment = alignment;
}
/**
* @return The format string or null if no special formatting.
*/
String getFormatString() {
return formatString;
}
/**
* @return The base cell style or null if default is to be used.
*/
CellStyle getCellStyle() {
return cellStyle;
}
/**
* @return The horizontal alignment or null if default is to be used.
*/
HorizontalAlign getAlignment() {
return alignment;
}
@Override
public int hashCode() {
int hash = 7;
hash = 29 * hash + Objects.hashCode(this.formatString);
hash = 29 * hash + Objects.hashCode(this.cellStyle);
hash = 29 * hash + Objects.hashCode(this.alignment);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final CellStyleKey other = (CellStyleKey) obj;
if (!Objects.equals(this.formatString, other.formatString)) {
return false;
}
if (!Objects.equals(this.cellStyle, other.cellStyle)) {
return false;
}
if (this.alignment != other.alignment) {
return false;
}
return true;
}
}
/**
* Class detailing aspects of the worksheet.
*/
@ -76,10 +158,10 @@ public class ExcelExport {
private final CellStyle headerStyle;
private final Workbook parentWorkbook;
private final CellStyle defaultStyle;
// maps a data format string / original cell style combination to a created cell style
private final Map<Pair<String, CellStyle>, CellStyle> cellStyleCache = new HashMap<>();
private final Map<CellStyleKey, CellStyle> cellStyleCache = new HashMap<>();
/**
* Main constructor.
*
@ -92,17 +174,29 @@ public class ExcelExport {
this.defaultStyle = defaultStyle;
this.parentWorkbook = parentWorkbook;
}
public CellStyle getCellStyle(CellStyle baseStyle, String dataFormat) {
return cellStyleCache.computeIfAbsent(Pair.of(dataFormat, baseStyle), (pair) -> {
/**
* Returns a cell style signified by the given cell style key. If the
* key is already present, a cached version is returned.
*
* @param cellStyleKey The key.
* @return The cell style representing this key.
*/
public CellStyle getCellStyle(CellStyleKey cellStyleKey) {
return cellStyleCache.computeIfAbsent(cellStyleKey, (pair) -> {
CellStyle computed = this.parentWorkbook.createCellStyle();
computed.cloneStyleFrom(pair.getRight() == null ? defaultStyle : pair.getRight());
computed.setDataFormat(this.parentWorkbook.getCreationHelper().createDataFormat().getFormat(dataFormat));
computed.cloneStyleFrom(cellStyleKey.getCellStyle() == null ? defaultStyle : cellStyleKey.getCellStyle());
if (cellStyleKey.getAlignment() != null) {
computed.setAlignment(cellStyleKey.getAlignment().getPoiAlignment());
}
if (cellStyleKey.getFormatString() != null) {
computed.setDataFormat(this.parentWorkbook.getCreationHelper().createDataFormat().getFormat(cellStyleKey.getFormatString()));
}
return computed;
});
}
/**
* Returns the cell style to use for headers.
@ -115,13 +209,13 @@ public class ExcelExport {
/**
* Returns the cell style for default items.
*
*
* @return The cell style for default items.
*/
public CellStyle getDefaultCellStyle() {
return defaultStyle;
}
/**
* Returns the parent workbook.
*
@ -160,6 +254,7 @@ public class ExcelExport {
/**
* Retrieves a singleton instance of this class.
*
* @return The instance.
*/
public static ExcelExport getInstance() {
@ -176,10 +271,11 @@ public class ExcelExport {
/**
* Writes the exports to a workbook.
*
* @param exports The sheets to export.
* @param path The path to the output file.
* @throws IOException
* @throws ExcelExportException
* @throws ExcelExportException
*/
@Messages({
"# {0} - sheetNumber",
@ -199,7 +295,7 @@ public class ExcelExport {
CellStyle headerCellStyle = workbook.createCellStyle();
headerCellStyle.setFont(headerFont);
headerCellStyle.setAlignment(alignment);
CellStyle defaultCellStyle = workbook.createCellStyle();
defaultCellStyle.setAlignment(alignment);
@ -231,7 +327,6 @@ public class ExcelExport {
workbook.close();
}
/**
* Creates an excel cell given the model.
*
@ -242,13 +337,13 @@ public class ExcelExport {
* @param cellStyle The style to use.
* @return The created cell.
*/
static Cell createCell(WorksheetEnv env, Row row, int colNum, ExcelTableExport.ExcelCellModel cellModel, Optional<CellStyle> cellStyle) {
static Cell createCell(WorksheetEnv env, Row row, int colNum, ExcelCellModel cellModel, Optional<CellStyle> cellStyle) {
CellStyle cellStyleToUse = cellStyle.orElse(env.getDefaultCellStyle());
if (cellModel.getExcelFormatString() != null) {
cellStyleToUse = env.getCellStyle(cellStyleToUse, cellModel.getExcelFormatString());
if (cellModel.getExcelFormatString() != null || cellModel.getHorizontalAlignment() != null) {
cellStyleToUse = env.getCellStyle(new CellStyleKey(cellModel.getExcelFormatString(), cellStyleToUse, cellModel.getHorizontalAlignment()));
}
Object cellData = cellModel.getData();
Cell cell = row.createCell(colNum);
if (cellData instanceof Calendar) {

View File

@ -1,7 +1,20 @@
/*
* 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.
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
@ -11,14 +24,16 @@ import java.util.Optional;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelExportException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport.ExcelCellModel;
/**
*
* @author gregd
* An excel export that has special row-by-row formatting.
*/
public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport {
/**
* The dimensions consumed by an item in an ExcelSpecialFormatExport list of
* items to be rendered.
*/
public static class ItemDimensions {
private final int rowStart;
@ -26,6 +41,14 @@ public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport {
private final int colStart;
private final int colEnd;
/**
* Main constructor.
*
* @param rowStart The starting excel row of the item.
* @param colStart The starting excel column of the item.
* @param rowEnd The last excel row of the the item.
* @param colEnd The last excel column of the item.
*/
public ItemDimensions(int rowStart, int colStart, int rowEnd, int colEnd) {
this.rowStart = rowStart;
this.colStart = colStart;
@ -33,36 +56,74 @@ public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport {
this.colEnd = colEnd;
}
/**
* @return The starting excel row of the item.
*/
public int getRowStart() {
return rowStart;
}
/**
* @return The last excel row of the the item.
*/
public int getRowEnd() {
return rowEnd;
}
/**
* @return The starting excel column of the item.
*/
public int getColStart() {
return colStart;
}
/**
* @return The last excel column of the item.
*/
public int getColEnd() {
return colEnd;
}
}
/**
* An item to be exported in a specially formatted excel export.
*/
public interface ExcelItemExportable {
/**
* Writes the item to the sheet in the special format export sheet.
*
* @param sheet The sheet.
* @param rowStart The starting row to start writing.
* @param colStart The starting column to start writing.
* @param env The excel export context.
* @return The dimensions of what has been written.
* @throws ExcelExportException
*/
ItemDimensions write(Sheet sheet, int rowStart, int colStart, ExcelExport.WorksheetEnv env) throws ExcelExportException;
}
/**
* Writes a string to a single cell in a specially formatted excel export.
*/
public static class SingleCellExportable implements ExcelItemExportable {
private final ExcelCellModel item;
/**
* Main constructor.
*
* @param key The text to be written.
*/
public SingleCellExportable(String key) {
this(new DefaultCellModel<>(key));
}
/**
* Main constructor.
*
* @param item The cell model to be written.
*/
public SingleCellExportable(ExcelCellModel item) {
this.item = item;
}
@ -75,15 +136,31 @@ public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport {
}
}
/**
* Writes a row consisting of first column as a key and second column as a
* value.
*/
public static class KeyValueItemExportable implements ExcelItemExportable {
private final ExcelCellModel key;
private final ExcelCellModel value;
/**
* Main constructor.
*
* @param key The string key to be exported.
* @param value The cell model to be exported.
*/
public KeyValueItemExportable(String key, ExcelCellModel value) {
this(new DefaultCellModel<>(key), value);
}
/**
* Main constructor.
*
* @param key The cell key to be exported.
* @param value The cell model to be exported.
*/
public KeyValueItemExportable(ExcelCellModel key, ExcelCellModel value) {
this.key = key;
this.value = value;
@ -98,14 +175,17 @@ public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport {
}
}
private final String sheetName;
private final List<ExcelItemExportable> exports;
public ExcelSpecialFormatExport(String sheetName, List<ExcelItemExportable> exports) {
this.sheetName = sheetName;
this.exports = exports == null ? Collections.emptyList() : exports;
}
/**
* A special format excel export item that shows a title and a list of items
* indented one column.
*
* i.e.
* <pre>
* title
* item 1
* item 2
* </pre>
*/
public static class TitledExportable implements ExcelItemExportable {
private static final int DEFAULT_INDENT = 1;
@ -113,6 +193,12 @@ public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport {
private final String title;
private final List<? extends ExcelItemExportable> children;
/**
* Main constructor.
*
* @param title The title for the export.
* @param children The children to be indented and enumerated.
*/
public TitledExportable(String title, List<? extends ExcelItemExportable> children) {
this.title = title;
this.children = children;
@ -135,7 +221,20 @@ public class ExcelSpecialFormatExport implements ExcelExport.ExcelSheetExport {
return new ItemDimensions(rowStart, colStart, curRow - 1, maxCol);
}
}
private final String sheetName;
private final List<ExcelItemExportable> exports;
/**
* Main constructor.
*
* @param sheetName The name of the sheet.
* @param exports The row-by-row items to be exported.
*/
public ExcelSpecialFormatExport(String sheetName, List<ExcelItemExportable> exports) {
this.sheetName = sheetName;
this.exports = exports == null ? Collections.emptyList() : exports;
}
@Override

View File

@ -19,37 +19,21 @@
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelExportException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ExcelItemExportable;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ItemDimensions;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport.ExcelCellModel;
/**
* An excel sheet export of table data.
*/
public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelSheetExport, ExcelItemExportable {
/**
* 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();
}
private final String sheetName;
private final List<ColumnModel<T, C>> columns;
private final List<T> data;
@ -66,7 +50,16 @@ public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelSheet
public ExcelTableExport(String sheetName, List<ColumnModel<T, C>> columns, List<T> data) {
this(sheetName, columns, data, 0);
}
/**
* Main constructor.
*
* @param sheetName The name of the sheet. NOTE: There can be no duplicates
* in a workbook.
* @param columns The columns of the table.
* @param data The data to export.
* @param columnIndent The column indent.
*/
public ExcelTableExport(String sheetName, List<ColumnModel<T, C>> columns, List<T> data, int columnIndent) {
this.sheetName = sheetName;
this.columns = columns;
@ -129,7 +122,7 @@ public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelSheet
// freeze header row
sheet.createFreezePane(0, 1);
// Create Cell Style for each column (if one is needed)
for (int rowNum = 0; rowNum < safeData.size(); rowNum++) {
T rowData = safeData.get(rowNum);
Row row = sheet.createRow(rowNum + rowStart + 1);