mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-17 10:17:41 +00:00
Merge pull request #6795 from gdicristofaro/7368-dssSpecialFormat
7368 dss special format
This commit is contained in:
commit
5edbf07d7a
@ -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;
|
||||
|
@ -6,7 +6,21 @@ AnalysisPanel_keywordHits_tabName=Keyword Hits
|
||||
AnalysisPanel_keywordSearchModuleName=Keyword Search
|
||||
BaseDataSourceSummaryPanel_goToArtifact=View Source Result
|
||||
BaseDataSourceSummaryPanel_goToFile=View Source File in Directory
|
||||
ContainerPanel_export_acquisitionDetails=Acquisition Details:
|
||||
ContainerPanel_export_deviceId=Device ID:
|
||||
ContainerPanel_export_displayName=Display Name:
|
||||
ContainerPanel_export_filePaths=File Paths:
|
||||
ContainerPanel_export_imageType=Image Type:
|
||||
ContainerPanel_export_md5=MD5:
|
||||
ContainerPanel_export_originalName=Name:
|
||||
ContainerPanel_export_sectorSize=Sector Size:
|
||||
ContainerPanel_export_sha1=SHA1:
|
||||
ContainerPanel_export_sha256=SHA256:
|
||||
ContainerPanel_export_size=Size:
|
||||
ContainerPanel_export_timeZone=Time Zone:
|
||||
ContainerPanel_export_unallocatedSize=Unallocated Space:
|
||||
ContainerPanel_setFieldsForNonImageDataSource_na=N/A
|
||||
ContainerPanel_tabName=Container
|
||||
CTL_DataSourceSummaryAction=Data Source Summary
|
||||
DataSourceSummaryDialog.closeButton.text=Close
|
||||
ContainerPanel.displayNameLabel.text=Display Name:
|
||||
@ -73,6 +87,12 @@ GeolocationPanel_mostCommon_tabName=Most Common Cities
|
||||
GeolocationPanel_mostRecent_tabName=Most Recent Cities
|
||||
GeolocationPanel_onNoCrIngest_message=No results will be shown because the GPX Parser was not run.
|
||||
GeolocationPanel_unknownRow_title=Unknown
|
||||
IngestJobExcelExport_endTimeColumn=End Time
|
||||
IngestJobExcelExport_ingestStatusTimeColumn=Ingest Status
|
||||
IngestJobExcelExport_moduleNameTimeColumn=Module Name
|
||||
IngestJobExcelExport_sheetName=Ingest History
|
||||
IngestJobExcelExport_startTimeColumn=Start Time
|
||||
IngestJobExcelExport_versionColumn=Module Version
|
||||
PastCasesPanel_caseColumn_title=Case
|
||||
PastCasesPanel_countColumn_title=Count
|
||||
PastCasesPanel_notableFileTable_tabName=Cases with Common Notable
|
||||
@ -87,18 +107,19 @@ RecentFilesPanel_attachmentsTable_tabName=Recent Attachments
|
||||
RecentFilesPanel_col_head_date=Date
|
||||
RecentFilesPanel_docsTable_tabName=Recently Opened Documents
|
||||
RecentFilesPanel_downloadsTable_tabName=Recently Downloads
|
||||
SizeRepresentationUtil_units_bytes=\ bytes
|
||||
SizeRepresentationUtil_units_gigabytes=\ GB
|
||||
SizeRepresentationUtil_units_kilobytes=\ kB
|
||||
SizeRepresentationUtil_units_megabytes=\ MB
|
||||
SizeRepresentationUtil_units_petabytes=\ PB
|
||||
SizeRepresentationUtil_units_terabytes=\ TB
|
||||
SizeRepresentationUtil_units_bytes=bytes
|
||||
SizeRepresentationUtil_units_gigabytes=GB
|
||||
SizeRepresentationUtil_units_kilobytes=KB
|
||||
SizeRepresentationUtil_units_megabytes=MB
|
||||
SizeRepresentationUtil_units_petabytes=PB
|
||||
SizeRepresentationUtil_units_terabytes=TB
|
||||
TimelinePanel_earliestLabel_title=Earliest
|
||||
TimelinePanel_latestLabel_title=Latest
|
||||
TimlinePanel_last30DaysChart_artifactEvts_title=Result Events
|
||||
TimlinePanel_last30DaysChart_fileEvts_title=File Events
|
||||
TimlinePanel_last30DaysChart_title=Last 30 Days
|
||||
TypesPanel_artifactsTypesPieChart_title=Artifact Types
|
||||
TypesPanel_excelTabName=Types
|
||||
TypesPanel_fileMimeTypesChart_audio_title=Audio
|
||||
TypesPanel_fileMimeTypesChart_documents_title=Documents
|
||||
TypesPanel_fileMimeTypesChart_executables_title=Executables
|
||||
|
@ -19,21 +19,35 @@
|
||||
package org.sleuthkit.autopsy.datasourcesummary.ui;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
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.uiutils.DataFetchResult.ResultType;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
|
||||
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;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.UpdateGovernor;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
@ -42,39 +56,184 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
/**
|
||||
* Panel to display additional details associated with a specific DataSource
|
||||
*/
|
||||
@Messages({
|
||||
"ContainerPanel_tabName=Container"
|
||||
})
|
||||
class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
/**
|
||||
* Data payload for the Container panel.
|
||||
* View model data for data source images.
|
||||
*/
|
||||
private static class ContainerPanelData {
|
||||
private static class ImageViewModel {
|
||||
|
||||
private final DataSource dataSource;
|
||||
private final Long unallocatedFilesSize;
|
||||
private final long unallocatedSize;
|
||||
private final long size;
|
||||
private final long sectorSize;
|
||||
|
||||
private final String timeZone;
|
||||
private final String imageType;
|
||||
|
||||
private final List<String> paths;
|
||||
private final String md5Hash;
|
||||
private final String sha1Hash;
|
||||
private final String sha256Hash;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param dataSource The original datasource.
|
||||
* @param unallocatedFilesSize The unallocated file size.
|
||||
* @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.
|
||||
*/
|
||||
ContainerPanelData(DataSource dataSource, Long unallocatedFilesSize) {
|
||||
this.dataSource = dataSource;
|
||||
this.unallocatedFilesSize = unallocatedFilesSize;
|
||||
ImageViewModel(long unallocatedSize, long size, long sectorSize,
|
||||
String timeZone, String imageType, List<String> paths, String md5Hash,
|
||||
String sha1Hash, String sha256Hash) {
|
||||
this.unallocatedSize = unallocatedSize;
|
||||
this.size = size;
|
||||
this.sectorSize = sectorSize;
|
||||
this.timeZone = timeZone;
|
||||
this.imageType = imageType;
|
||||
this.paths = paths == null ? Collections.emptyList() : new ArrayList<>(paths);
|
||||
this.md5Hash = md5Hash;
|
||||
this.sha1Hash = sha1Hash;
|
||||
this.sha256Hash = sha256Hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The original datasource.
|
||||
* @return Size in bytes of unallocated space.
|
||||
*/
|
||||
DataSource getDataSource() {
|
||||
return dataSource;
|
||||
long getUnallocatedSize() {
|
||||
return unallocatedSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The unallocated file size.
|
||||
* @return Total size in bytes.
|
||||
*/
|
||||
Long getUnallocatedFilesSize() {
|
||||
return unallocatedFilesSize;
|
||||
long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Sector size in bytes.
|
||||
*/
|
||||
long getSectorSize() {
|
||||
return sectorSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The time zone.
|
||||
*/
|
||||
String getTimeZone() {
|
||||
return timeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The type of image.
|
||||
*/
|
||||
String getImageType() {
|
||||
return imageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The source paths for the image.
|
||||
*/
|
||||
List<String> getPaths() {
|
||||
return paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The md5 hash or null.
|
||||
*/
|
||||
String getMd5Hash() {
|
||||
return md5Hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The sha1 hash or null.
|
||||
*/
|
||||
String getSha1Hash() {
|
||||
return sha1Hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The sha256 hash or null.
|
||||
*/
|
||||
String getSha256Hash() {
|
||||
return sha256Hash;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* View model for container data.
|
||||
*/
|
||||
private static class ContainerViewModel {
|
||||
|
||||
private final String displayName;
|
||||
private final String originalName;
|
||||
private final String deviceIdValue;
|
||||
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;
|
||||
this.originalName = originalName;
|
||||
this.deviceIdValue = deviceIdValue;
|
||||
this.acquisitionDetails = acquisitionDetails;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,6 +262,7 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
private static final Logger logger = Logger.getLogger(ContainerPanel.class.getName());
|
||||
|
||||
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
|
||||
private final DataFetcher<DataSource, ContainerViewModel> containerDataFetcher;
|
||||
|
||||
/**
|
||||
* Creates a new form ContainerPanel.
|
||||
@ -117,21 +277,15 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
ContainerPanel(ContainerSummary containerSummary) {
|
||||
super(containerSummary, CONTAINER_UPDATES);
|
||||
|
||||
containerDataFetcher = (dataSource) -> getContainerViewModel(containerSummary, dataSource);
|
||||
|
||||
dataFetchComponents = Arrays.asList(
|
||||
new DataFetchComponents<>(
|
||||
(dataSource) -> {
|
||||
return new ContainerPanelData(
|
||||
dataSource,
|
||||
containerSummary.getSizeOfUnallocatedFiles(dataSource)
|
||||
);
|
||||
},
|
||||
containerDataFetcher,
|
||||
(result) -> {
|
||||
if (result != null && result.getResultType() == ResultType.SUCCESS) {
|
||||
ContainerPanelData data = result.getData();
|
||||
DataSource dataSource = (data == null) ? null : data.getDataSource();
|
||||
Long unallocatedFileSize = (data == null) ? null : data.getUnallocatedFilesSize();
|
||||
|
||||
updateDetailsPanelData(dataSource, unallocatedFileSize);
|
||||
ContainerViewModel data = result.getData();
|
||||
updateDetailsPanelData(data);
|
||||
} else {
|
||||
if (result == null) {
|
||||
logger.log(Level.WARNING, "No data fetch result was provided to the ContainerPanel.");
|
||||
@ -139,8 +293,7 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
logger.log(Level.WARNING, "An exception occurred while attempting to fetch data for the ContainerPanel.",
|
||||
result.getException());
|
||||
}
|
||||
|
||||
updateDetailsPanelData(null, null);
|
||||
updateDetailsPanelData(null);
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -161,33 +314,113 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update which DataSource this panel should display details about
|
||||
*
|
||||
* @param selectedDataSource the DataSource to display details about.
|
||||
* A means of retrieving data that could potentially throw an exception.
|
||||
*/
|
||||
private void updateDetailsPanelData(DataSource selectedDataSource, Long unallocatedFilesSize) {
|
||||
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();
|
||||
} catch (TskCoreException | SleuthkitCaseProviderException | SQLException ex) {
|
||||
logger.log(Level.WARNING, "Error while retrieving data.", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
return new ContainerViewModel(
|
||||
ds.getName(),
|
||||
ds.getName(),
|
||||
ds.getDeviceId(),
|
||||
retrieve(() -> ds.getAcquisitionDetails()),
|
||||
ds instanceof Image ? getImageViewModel(containerSummary, (Image) ds) : null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
Long unallocSize = retrieve(() -> containerSummary.getSizeOfUnallocatedFiles(image));
|
||||
String imageType = image.getType().getName();
|
||||
Long size = image.getSize();
|
||||
Long sectorSize = image.getSsize();
|
||||
String timeZone = image.getTimeZone();
|
||||
List<String> paths = image.getPaths() == null ? Collections.emptyList() : Arrays.asList(image.getPaths());
|
||||
String md5 = retrieve(() -> image.getMd5());
|
||||
String sha1 = retrieve(() -> image.getSha1());
|
||||
String sha256 = retrieve(() -> image.getSha256());
|
||||
|
||||
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 (selectedDataSource != null) {
|
||||
displayNameValue.setText(selectedDataSource.getName());
|
||||
originalNameValue.setText(selectedDataSource.getName());
|
||||
deviceIdValue.setText(selectedDataSource.getDeviceId());
|
||||
if (viewModel == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
acquisitionDetailsTextArea.setText(selectedDataSource.getAcquisitionDetails());
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get acquisition details for selected data source", ex);
|
||||
}
|
||||
displayNameValue.setText(viewModel.getDisplayName());
|
||||
originalNameValue.setText(viewModel.getOriginalName());
|
||||
deviceIdValue.setText(viewModel.getDeviceId());
|
||||
acquisitionDetailsTextArea.setText(viewModel.getAcquisitionDetails());
|
||||
|
||||
if (selectedDataSource instanceof Image) {
|
||||
setFieldsForImage((Image) selectedDataSource, unallocatedFilesSize);
|
||||
} else {
|
||||
setFieldsForNonImageDataSource();
|
||||
}
|
||||
if (viewModel.getImageViewModel() != null) {
|
||||
setFieldsForImage(viewModel.getImageViewModel());
|
||||
} else {
|
||||
setFieldsForNonImageDataSource();
|
||||
}
|
||||
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets image-only fields to N/A.
|
||||
*/
|
||||
@Messages({
|
||||
"ContainerPanel_setFieldsForNonImageDataSource_na=N/A"
|
||||
})
|
||||
@ -208,54 +441,24 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets text fields for an image. This should be called after
|
||||
* clearTableValues and before updateFieldVisibility to ensure the proper
|
||||
* rendering.
|
||||
* Sets fields for images.
|
||||
*
|
||||
* @param selectedImage The selected image.
|
||||
* @param unallocatedFilesSize Unallocated file size in bytes.
|
||||
* @param viewModel The image view model data.
|
||||
*/
|
||||
private void setFieldsForImage(Image selectedImage, Long unallocatedFilesSize) {
|
||||
unallocatedSizeValue.setText(SizeRepresentationUtil.getSizeString(unallocatedFilesSize));
|
||||
imageTypeValue.setText(selectedImage.getType().getName());
|
||||
sizeValue.setText(SizeRepresentationUtil.getSizeString(selectedImage.getSize()));
|
||||
sectorSizeValue.setText(SizeRepresentationUtil.getSizeString(selectedImage.getSsize()));
|
||||
timeZoneValue.setText(selectedImage.getTimeZone());
|
||||
private void setFieldsForImage(ImageViewModel viewModel) {
|
||||
unallocatedSizeValue.setText(SizeRepresentationUtil.getSizeString(viewModel.getUnallocatedSize()));
|
||||
imageTypeValue.setText(viewModel.getImageType());
|
||||
sizeValue.setText(SizeRepresentationUtil.getSizeString(viewModel.getSize()));
|
||||
sectorSizeValue.setText(SizeRepresentationUtil.getSizeString(viewModel.getSectorSize()));
|
||||
timeZoneValue.setText(viewModel.getTimeZone());
|
||||
|
||||
for (String path : selectedImage.getPaths()) {
|
||||
for (String path : viewModel.getPaths()) {
|
||||
((DefaultTableModel) filePathsTable.getModel()).addRow(new Object[]{path});
|
||||
}
|
||||
|
||||
try {
|
||||
//older databases may have null as the hash values
|
||||
String md5String = selectedImage.getMd5();
|
||||
if (md5String == null) {
|
||||
md5String = "";
|
||||
}
|
||||
md5HashValue.setText(md5String);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get MD5 for selected data source", ex);
|
||||
}
|
||||
|
||||
try {
|
||||
String sha1String = selectedImage.getSha1();
|
||||
if (sha1String == null) {
|
||||
sha1String = "";
|
||||
}
|
||||
sha1HashValue.setText(sha1String);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get SHA1 for selected data source", ex);
|
||||
}
|
||||
|
||||
try {
|
||||
String sha256String = selectedImage.getSha256();
|
||||
if (sha256String == null) {
|
||||
sha256String = "";
|
||||
}
|
||||
sha256HashValue.setText(sha256String);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get SHA256 for selected data source", ex);
|
||||
}
|
||||
md5HashValue.setText(viewModel.getMd5Hash());
|
||||
sha1HashValue.setText(viewModel.getSha1Hash());
|
||||
sha256HashValue.setText(viewModel.getSha256Hash());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -277,9 +480,82 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
|
||||
((DefaultTableModel) filePathsTable.getModel()).setRowCount(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) -> (StringUtils.isBlank(line)) ? null : new SingleCellExportable(line))
|
||||
.filter(item -> item != null)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
List<ExcelExport.ExcelSheetExport> getExports(DataSource ds) {
|
||||
return Collections.emptyList();
|
||||
@Messages({
|
||||
"ContainerPanel_export_displayName=Display Name:",
|
||||
"ContainerPanel_export_originalName=Name:",
|
||||
"ContainerPanel_export_deviceId=Device ID:",
|
||||
"ContainerPanel_export_timeZone=Time Zone:",
|
||||
"ContainerPanel_export_acquisitionDetails=Acquisition Details:",
|
||||
"ContainerPanel_export_imageType=Image Type:",
|
||||
"ContainerPanel_export_size=Size:",
|
||||
"ContainerPanel_export_sectorSize=Sector Size:",
|
||||
"ContainerPanel_export_md5=MD5:",
|
||||
"ContainerPanel_export_sha1=SHA1:",
|
||||
"ContainerPanel_export_sha256=SHA256:",
|
||||
"ContainerPanel_export_unallocatedSize=Unallocated Space:",
|
||||
"ContainerPanel_export_filePaths=File Paths:",})
|
||||
protected List<ExcelSheetExport> getExports(DataSource ds) {
|
||||
ContainerViewModel result = getFetchResult(containerDataFetcher, "Container sheets", ds);
|
||||
if (ds == null || result == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
String NA = Bundle.ContainerPanel_setFieldsForNonImageDataSource_na();
|
||||
DefaultCellModel<?> NACell = new DefaultCellModel<>(NA);
|
||||
|
||||
ImageViewModel imageModel = result.getImageViewModel();
|
||||
boolean hasImage = imageModel != null;
|
||||
|
||||
DefaultCellModel<?> timeZone = hasImage ? new DefaultCellModel<>(imageModel.getTimeZone()) : NACell;
|
||||
DefaultCellModel<?> imageType = hasImage ? new DefaultCellModel<>(imageModel.getImageType()) : NACell;
|
||||
DefaultCellModel<?> size = hasImage ? SizeRepresentationUtil.getBytesCell(imageModel.getSize()) : NACell;
|
||||
DefaultCellModel<?> sectorSize = hasImage ? SizeRepresentationUtil.getBytesCell(imageModel.getSectorSize()) : NACell;
|
||||
DefaultCellModel<?> md5 = hasImage ? new DefaultCellModel<>(imageModel.getMd5Hash()) : NACell;
|
||||
DefaultCellModel<?> sha1 = hasImage ? new DefaultCellModel<>(imageModel.getSha1Hash()) : NACell;
|
||||
DefaultCellModel<?> sha256 = hasImage ? new DefaultCellModel<>(imageModel.getSha256Hash()) : NACell;
|
||||
DefaultCellModel<?> unallocatedSize = hasImage ? SizeRepresentationUtil.getBytesCell(imageModel.getUnallocatedSize()) : NACell;
|
||||
List<String> paths = result.getImageViewModel() == null ? Collections.singletonList(NA) : result.getImageViewModel().getPaths();
|
||||
List<SingleCellExportable> cellPaths = paths.stream()
|
||||
.map(SingleCellExportable::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Arrays.asList(
|
||||
new ExcelSpecialFormatExport(Bundle.ContainerPanel_tabName(), Arrays.asList(
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_displayName(), new DefaultCellModel<>(result.getDisplayName())),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_originalName(), new DefaultCellModel<>(result.getOriginalName())),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_deviceId(), new DefaultCellModel<>(result.getDeviceId())),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_timeZone(), timeZone),
|
||||
new TitledExportable(Bundle.ContainerPanel_export_acquisitionDetails(), getAcquisitionDetails(result.getAcquisitionDetails())),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_imageType(), imageType),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_size(), size),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_sectorSize(), sectorSize),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_md5(), md5),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_sha1(), sha1),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_sha256(), sha256),
|
||||
new KeyValueItemExportable(Bundle.ContainerPanel_export_unallocatedSize(), unallocatedSize),
|
||||
new TitledExportable(Bundle.ContainerPanel_export_filePaths(), cellPaths)
|
||||
)));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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()),
|
||||
@ -155,7 +154,7 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
|
||||
Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(),
|
||||
ingestHistoryPanel,
|
||||
ingestHistoryPanel::setDataSource,
|
||||
null,
|
||||
IngestJobExcelExport::getExports,
|
||||
null),
|
||||
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new ContainerPanel()),
|
||||
new DataSourceTab(
|
||||
@ -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
|
||||
|
@ -0,0 +1,253 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.IngestJobInfo;
|
||||
import org.sleuthkit.datamodel.IngestModuleInfo;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Class that handles exporting information in IngestJobInfoPanel to excel.
|
||||
*/
|
||||
@Messages({
|
||||
"IngestJobExcelExport_startTimeColumn=Start Time",
|
||||
"IngestJobExcelExport_endTimeColumn=End Time",
|
||||
"IngestJobExcelExport_ingestStatusTimeColumn=Ingest Status",
|
||||
"IngestJobExcelExport_moduleNameTimeColumn=Module Name",
|
||||
"IngestJobExcelExport_versionColumn=Module Version",
|
||||
"IngestJobExcelExport_sheetName=Ingest History"
|
||||
})
|
||||
class IngestJobExcelExport {
|
||||
|
||||
/**
|
||||
* An entry to display in an excel export.
|
||||
*/
|
||||
private static class IngestJobEntry {
|
||||
|
||||
private final Date startTime;
|
||||
private final Date endTime;
|
||||
private final String status;
|
||||
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;
|
||||
this.status = status;
|
||||
this.ingestModule = ingestModule;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Logger logger = Logger.getLogger(IngestJobExcelExport.class.getName());
|
||||
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(),
|
||||
(entry) -> getDateCell(entry.getStartTime())),
|
||||
new ColumnModel<>(
|
||||
Bundle.IngestJobExcelExport_endTimeColumn(),
|
||||
(entry) -> getDateCell(entry.getEndTime())),
|
||||
new ColumnModel<>(
|
||||
Bundle.IngestJobExcelExport_ingestStatusTimeColumn(),
|
||||
(entry) -> new DefaultCellModel<>(entry.getStatus())),
|
||||
new ColumnModel<>(
|
||||
Bundle.IngestJobExcelExport_moduleNameTimeColumn(),
|
||||
(entry) -> new DefaultCellModel<>(entry.getIngestModule())),
|
||||
new ColumnModel<>(
|
||||
Bundle.IngestJobExcelExport_versionColumn(),
|
||||
(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) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
Date startTime = job.getStartDateTime();
|
||||
Date endTime = job.getEndDateTime();
|
||||
String status = job.getStatus().getDisplayName();
|
||||
|
||||
return infoList.stream()
|
||||
.filter(info -> info != null)
|
||||
.map(info -> new IngestJobEntry(startTime, endTime, status, info.getDisplayName(), info.getVersion()))
|
||||
.sorted((a, b) -> {
|
||||
boolean aIsNull = a == null || a.getIngestModule() == null;
|
||||
boolean bIsNull = b == null || b.getIngestModule() == null;
|
||||
if (aIsNull || bIsNull) {
|
||||
return Boolean.compare(aIsNull, bIsNull);
|
||||
} else {
|
||||
return a.getIngestModule().compareTo(b.getIngestModule());
|
||||
}
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 -> {
|
||||
IngestJobEntry entry = list.get(idx);
|
||||
if (entry == null || idx == 0) {
|
||||
return entry;
|
||||
} else {
|
||||
return new IngestJobEntry(null, null, null, entry.getIngestModule(), entry.getIngestModuleVersion());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
List<IngestJobInfo> info = null;
|
||||
try {
|
||||
info = Case.getCurrentCaseThrows().getSleuthkitCase().getIngestJobs();
|
||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "There was an error fetching ingest jobs", ex);
|
||||
}
|
||||
|
||||
if (info == null) {
|
||||
info = Collections.emptyList();
|
||||
}
|
||||
|
||||
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) {
|
||||
return Boolean.compare(aIsNull, bIsNull);
|
||||
} else {
|
||||
return a.getStartDateTime().compareTo(b.getStartDateTime());
|
||||
}
|
||||
})
|
||||
.map((job) -> getEntries(job))
|
||||
.filter(lst -> lst != null)
|
||||
.flatMap((lst) -> showFirstRowOnly(lst))
|
||||
.filter(item -> item != null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Arrays.asList(new ExcelTableExport<>(Bundle.IngestJobExcelExport_sheetName(), COLUMNS, toDisplay));
|
||||
}
|
||||
|
||||
private IngestJobExcelExport() {
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ import java.text.DecimalFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
|
||||
|
||||
/**
|
||||
* This class provides utilities for representing storage size in most relevant
|
||||
@ -32,14 +33,64 @@ public final class SizeRepresentationUtil {
|
||||
private static final int SIZE_CONVERSION_CONSTANT = 1000;
|
||||
private static final DecimalFormat APPROXIMATE_SIZE_FORMAT = new DecimalFormat("#.##");
|
||||
|
||||
private static List<String> UNITS = Arrays.asList(
|
||||
Bundle.SizeRepresentationUtil_units_bytes(),
|
||||
Bundle.SizeRepresentationUtil_units_kilobytes(),
|
||||
Bundle.SizeRepresentationUtil_units_megabytes(),
|
||||
Bundle.SizeRepresentationUtil_units_gigabytes(),
|
||||
Bundle.SizeRepresentationUtil_units_terabytes(),
|
||||
Bundle.SizeRepresentationUtil_units_petabytes()
|
||||
);
|
||||
/**
|
||||
* A size unit corresponding to orders of magnitude of bytes (kilobyte, gigabytes, etc.).
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"SizeRepresentationUtil_units_bytes=bytes",
|
||||
"SizeRepresentationUtil_units_kilobytes=KB",
|
||||
"SizeRepresentationUtil_units_megabytes=MB",
|
||||
"SizeRepresentationUtil_units_gigabytes=GB",
|
||||
"SizeRepresentationUtil_units_terabytes=TB",
|
||||
"SizeRepresentationUtil_units_petabytes=PB"
|
||||
})
|
||||
enum SizeUnit {
|
||||
BYTES(Bundle.SizeRepresentationUtil_units_bytes(), "#", 0),
|
||||
KB(Bundle.SizeRepresentationUtil_units_kilobytes(), "#,##0.00,", 1),
|
||||
MB(Bundle.SizeRepresentationUtil_units_megabytes(), "#,##0.00,,", 2),
|
||||
GB(Bundle.SizeRepresentationUtil_units_gigabytes(), "#,##0.00,,,", 3),
|
||||
TB(Bundle.SizeRepresentationUtil_units_terabytes(), "#,##0.00,,,,", 4),
|
||||
PB(Bundle.SizeRepresentationUtil_units_petabytes(), "#,##0.00,,,,,", 5);
|
||||
|
||||
private final String suffix;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a long size in bytes as a string formated to be read by users.
|
||||
@ -47,49 +98,59 @@ public final class SizeRepresentationUtil {
|
||||
* @param size Long value representing a size in bytes.
|
||||
*
|
||||
* @return Return a string formated with a user friendly version of the size
|
||||
* as a string, returns empty String when provided empty size.
|
||||
* as a string, returns empty String when provided empty size.
|
||||
*/
|
||||
public static String getSizeString(Long size) {
|
||||
static String getSizeString(Long size) {
|
||||
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];
|
||||
}
|
||||
|
||||
for (int unitsIndex = 0; unitsIndex < SizeUnit.values().length; unitsIndex++) {
|
||||
SizeUnit unit = SizeUnit.values()[unitsIndex];
|
||||
long result = size / unit.getDivisor();
|
||||
if (result < SIZE_CONVERSION_CONSTANT) {
|
||||
return unit;
|
||||
}
|
||||
}
|
||||
|
||||
return SizeUnit.values()[SizeUnit.values().length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a long size in bytes as a string formated to be read by users.
|
||||
*
|
||||
* @param size Long value representing a size in byte.s
|
||||
* @param format The means of formatting the number.
|
||||
* @param size Long value representing a size in byte.s
|
||||
* @param format The means of formatting the number.
|
||||
* @param showFullSize Optionally show the number of bytes in the
|
||||
* datasource.
|
||||
* datasource.
|
||||
*
|
||||
* @return Return a string formated with a user friendly version of the size
|
||||
* as a string, returns empty String when provided empty size.
|
||||
* as a string, returns empty String when provided empty size.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"SizeRepresentationUtil_units_bytes= bytes",
|
||||
"SizeRepresentationUtil_units_kilobytes= kB",
|
||||
"SizeRepresentationUtil_units_megabytes= MB",
|
||||
"SizeRepresentationUtil_units_gigabytes= GB",
|
||||
"SizeRepresentationUtil_units_terabytes= TB",
|
||||
"SizeRepresentationUtil_units_petabytes= PB"
|
||||
})
|
||||
public static String getSizeString(Long size, DecimalFormat format, boolean showFullSize) {
|
||||
static String getSizeString(Long size, DecimalFormat format, boolean showFullSize) {
|
||||
if (size == null) {
|
||||
return "";
|
||||
}
|
||||
double approximateSize = size;
|
||||
int unitsIndex = 0;
|
||||
for (; unitsIndex < UNITS.size(); unitsIndex++) {
|
||||
if (approximateSize < SIZE_CONVERSION_CONSTANT) {
|
||||
break;
|
||||
} else {
|
||||
approximateSize /= SIZE_CONVERSION_CONSTANT;
|
||||
}
|
||||
|
||||
SizeUnit sizeUnit = getSizeUnit(size);
|
||||
if (sizeUnit == null) {
|
||||
sizeUnit = SizeUnit.BYTES;
|
||||
}
|
||||
|
||||
String fullSize = size + UNITS.get(0);
|
||||
String closestUnitSize = format.format(approximateSize) + UNITS.get(unitsIndex);
|
||||
|
||||
if (unitsIndex == 0) {
|
||||
|
||||
String closestUnitSize = String.format("%s %s",
|
||||
format.format(((double) size) / sizeUnit.getDivisor()), sizeUnit.getSuffix());
|
||||
|
||||
String fullSize = String.format("%d %s", size, SizeUnit.BYTES.getSuffix());
|
||||
if (sizeUnit.equals(SizeUnit.BYTES)) {
|
||||
return fullSize;
|
||||
} else if (showFullSize) {
|
||||
return String.format("%s (%s)", closestUnitSize, fullSize);
|
||||
@ -97,6 +158,24 @@ public final class SizeRepresentationUtil {
|
||||
return closestUnitSize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<>("");
|
||||
} else {
|
||||
SizeUnit unit = SizeRepresentationUtil.getSizeUnit(bytes);
|
||||
if (unit == null) {
|
||||
unit = SizeUnit.BYTES;
|
||||
}
|
||||
|
||||
return new DefaultCellModel<Long>(bytes, SizeRepresentationUtil::getSizeString, unit.getExcelFormatString());
|
||||
}
|
||||
}
|
||||
|
||||
private SizeRepresentationUtil() {
|
||||
}
|
||||
|
@ -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;
|
||||
@ -40,13 +39,16 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.KeyValueItemExportable;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
|
||||
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;
|
||||
@ -72,7 +74,8 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
"TypesPanel_fileMimeTypesChart_notAnalyzed_title=Not Analyzed",
|
||||
"TypesPanel_usageLabel_title=Usage",
|
||||
"TypesPanel_osLabel_title=OS",
|
||||
"TypesPanel_sizeLabel_title=Size"})
|
||||
"TypesPanel_sizeLabel_title=Size",
|
||||
"TypesPanel_excelTabName=Types"})
|
||||
class TypesPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
/**
|
||||
@ -167,10 +170,9 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final DecimalFormat INTEGER_SIZE_FORMAT = new DecimalFormat("#");
|
||||
private static final DecimalFormat COMMA_FORMATTER = new DecimalFormat("#,###");
|
||||
private static final String FILE_TYPE_FACTORY = FileTypeIdModuleFactory.class.getCanonicalName();
|
||||
private static final String FILE_TYPE_MODULE_NAME = FileTypeIdModuleFactory.getModuleName();
|
||||
private static final Logger logger = Logger.getLogger(TypesPanel.class.getName());
|
||||
private static final String COMMA_FORMAT_STR = "#,###";
|
||||
|
||||
private static final DecimalFormat COMMA_FORMATTER = new DecimalFormat(COMMA_FORMAT_STR);
|
||||
|
||||
private static final Color IMAGES_COLOR = new Color(156, 39, 176);
|
||||
private static final Color VIDEOS_COLOR = Color.YELLOW;
|
||||
@ -191,6 +193,15 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
|
||||
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_unknown_title(), new HashSet<>(Arrays.asList("application/octet-stream")), UNKNOWN_COLOR)
|
||||
);
|
||||
|
||||
private final DataFetcher<DataSource, String> usageFetcher;
|
||||
private final DataFetcher<DataSource, String> osFetcher;
|
||||
private final DataFetcher<DataSource, Long> sizeFetcher;
|
||||
|
||||
private final DataFetcher<DataSource, Long> allocatedFetcher;
|
||||
private final DataFetcher<DataSource, Long> unallocatedFetcher;
|
||||
private final DataFetcher<DataSource, Long> slackFetcher;
|
||||
private final DataFetcher<DataSource, Long> directoriesFetcher;
|
||||
|
||||
private final LoadableLabel usageLabel = new LoadableLabel(Bundle.TypesPanel_usageLabel_title());
|
||||
private final LoadableLabel osLabel = new LoadableLabel(Bundle.TypesPanel_osLabel_title());
|
||||
private final LoadableLabel sizeLabel = new LoadableLabel(Bundle.TypesPanel_sizeLabel_title());
|
||||
@ -246,42 +257,34 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
|
||||
|
||||
super(mimeTypeData, typeData, containerData);
|
||||
|
||||
this.usageFetcher = containerData::getDataSourceType;
|
||||
this.osFetcher = containerData::getOperatingSystems;
|
||||
|
||||
this.sizeFetcher = (dataSource) -> dataSource == null ? null : dataSource.getSize();
|
||||
|
||||
this.allocatedFetcher = (dataSource) -> typeData.getCountOfAllocatedFiles(dataSource);
|
||||
this.unallocatedFetcher = (dataSource) -> typeData.getCountOfUnallocatedFiles(dataSource);
|
||||
this.slackFetcher = (dataSource) -> typeData.getCountOfSlackFiles(dataSource);
|
||||
this.directoriesFetcher = (dataSource) -> typeData.getCountOfDirectories(dataSource);
|
||||
|
||||
this.dataFetchComponents = Arrays.asList(
|
||||
// usage label worker
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
containerData::getDataSourceType,
|
||||
(result) -> usageLabel.showDataFetchResult(result)),
|
||||
// os label worker
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
containerData::getOperatingSystems,
|
||||
(result) -> osLabel.showDataFetchResult(result)),
|
||||
// size label worker
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> {
|
||||
Long size = dataSource == null ? null : dataSource.getSize();
|
||||
return SizeRepresentationUtil.getSizeString(size, INTEGER_SIZE_FORMAT, false);
|
||||
},
|
||||
sizeLabel::showDataFetchResult),
|
||||
// file types worker
|
||||
new DataFetchWorker.DataFetchComponents<>(usageFetcher, usageLabel::showDataFetchResult),
|
||||
new DataFetchWorker.DataFetchComponents<>(osFetcher, osLabel::showDataFetchResult),
|
||||
new DataFetchWorker.DataFetchComponents<>(sizeFetcher,
|
||||
(sizeResult) -> sizeLabel.showDataFetchResult(
|
||||
DataFetchResult.getSubResult(sizeResult,
|
||||
size -> SizeRepresentationUtil.getSizeString(size, INTEGER_SIZE_FORMAT, false)))),
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> getMimeTypeCategoriesModel(mimeTypeData, dataSource),
|
||||
this::showMimeTypeCategories),
|
||||
// allocated files worker
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> getStringOrZero(typeData.getCountOfAllocatedFiles(dataSource)),
|
||||
allocatedLabel::showDataFetchResult),
|
||||
// unallocated files worker
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> getStringOrZero(typeData.getCountOfUnallocatedFiles(dataSource)),
|
||||
unallocatedLabel::showDataFetchResult),
|
||||
// slack files worker
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> getStringOrZero(typeData.getCountOfSlackFiles(dataSource)),
|
||||
slackLabel::showDataFetchResult),
|
||||
// directories worker
|
||||
new DataFetchWorker.DataFetchComponents<>(
|
||||
(dataSource) -> getStringOrZero(typeData.getCountOfDirectories(dataSource)),
|
||||
directoriesLabel::showDataFetchResult)
|
||||
new DataFetchWorker.DataFetchComponents<>(allocatedFetcher,
|
||||
countRes -> allocatedLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count)))),
|
||||
new DataFetchWorker.DataFetchComponents<>(unallocatedFetcher,
|
||||
countRes -> unallocatedLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count)))),
|
||||
new DataFetchWorker.DataFetchComponents<>(slackFetcher,
|
||||
countRes -> slackLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count)))),
|
||||
new DataFetchWorker.DataFetchComponents<>(directoriesFetcher,
|
||||
countRes -> directoriesLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count))))
|
||||
);
|
||||
|
||||
initComponents();
|
||||
@ -406,9 +409,53 @@ 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,
|
||||
new DefaultCellModel<Long>(count, COMMA_FORMATTER::format, COMMA_FORMAT_STR));
|
||||
}
|
||||
|
||||
@Override
|
||||
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
|
||||
return Collections.emptyList();
|
||||
if (dataSource == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Arrays.asList(new ExcelSpecialFormatExport(Bundle.TypesPanel_excelTabName(),
|
||||
Stream.of(
|
||||
getStrExportable(usageFetcher, Bundle.TypesPanel_usageLabel_title(), dataSource),
|
||||
getStrExportable(osFetcher, Bundle.TypesPanel_osLabel_title(), dataSource),
|
||||
new KeyValueItemExportable(Bundle.TypesPanel_sizeLabel_title(),
|
||||
SizeRepresentationUtil.getBytesCell(getFetchResult(sizeFetcher, "Types", dataSource))),
|
||||
getCountExportable(allocatedFetcher, Bundle.TypesPanel_filesByCategoryTable_allocatedRow_title(), dataSource),
|
||||
getCountExportable(unallocatedFetcher, Bundle.TypesPanel_filesByCategoryTable_unallocatedRow_title(), dataSource),
|
||||
getCountExportable(slackFetcher, Bundle.TypesPanel_filesByCategoryTable_slackRow_title(), dataSource),
|
||||
getCountExportable(directoriesFetcher, Bundle.TypesPanel_filesByCategoryTable_directoryRow_title(), dataSource))
|
||||
.filter(sheet -> sheet != null)
|
||||
.collect(Collectors.toList())
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
@ -76,6 +76,7 @@ public class DefaultCellModel<T> implements GuiCellModel, ExcelCellModel {
|
||||
this.data = data;
|
||||
this.stringConverter = stringConverter;
|
||||
this.excelFormatString = excelFormatString;
|
||||
this.tooltip = getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
}
|
@ -21,13 +21,23 @@ package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
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.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.Font;
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
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;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModel.HorizontalAlign;
|
||||
|
||||
/**
|
||||
* Class for handling Excel exporting.
|
||||
@ -59,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.
|
||||
*/
|
||||
@ -66,18 +157,47 @@ 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<CellStyleKey, CellStyle> cellStyleCache = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param headerStyle The cell style to use for headers.
|
||||
* @param defaultStyle The cell style to use as a default.
|
||||
* @param parentWorkbook The parent workbook.
|
||||
*/
|
||||
WorksheetEnv(CellStyle headerStyle, Workbook parentWorkbook) {
|
||||
WorksheetEnv(CellStyle headerStyle, CellStyle defaultStyle, Workbook parentWorkbook) {
|
||||
this.headerStyle = headerStyle;
|
||||
this.defaultStyle = defaultStyle;
|
||||
this.parentWorkbook = parentWorkbook;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(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.
|
||||
*
|
||||
@ -87,6 +207,15 @@ public class ExcelExport {
|
||||
return headerStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cell style for default items.
|
||||
*
|
||||
* @return The cell style for default items.
|
||||
*/
|
||||
public CellStyle getDefaultCellStyle() {
|
||||
return defaultStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent workbook.
|
||||
*
|
||||
@ -125,6 +254,7 @@ public class ExcelExport {
|
||||
|
||||
/**
|
||||
* Retrieves a singleton instance of this class.
|
||||
*
|
||||
* @return The instance.
|
||||
*/
|
||||
public static ExcelExport getInstance() {
|
||||
@ -141,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",
|
||||
@ -160,10 +291,15 @@ public class ExcelExport {
|
||||
//headerFont.setFontHeightInPoints((short) 14);
|
||||
|
||||
// Create a CellStyle with the font
|
||||
HorizontalAlignment alignment = HorizontalAlignment.LEFT;
|
||||
CellStyle headerCellStyle = workbook.createCellStyle();
|
||||
headerCellStyle.setFont(headerFont);
|
||||
headerCellStyle.setAlignment(alignment);
|
||||
|
||||
WorksheetEnv env = new WorksheetEnv(headerCellStyle, workbook);
|
||||
CellStyle defaultCellStyle = workbook.createCellStyle();
|
||||
defaultCellStyle.setAlignment(alignment);
|
||||
|
||||
WorksheetEnv env = new WorksheetEnv(headerCellStyle, defaultCellStyle, workbook);
|
||||
|
||||
if (exports != null) {
|
||||
for (int i = 0; i < exports.size(); i++) {
|
||||
@ -190,4 +326,46 @@ public class ExcelExport {
|
||||
// Closing the workbook
|
||||
workbook.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an excel cell given the model.
|
||||
*
|
||||
* @param env The work sheet environment including the workbook.
|
||||
* @param row The row in the excel document.
|
||||
* @param colNum The column number (not zero-indexed).
|
||||
* @param cellModel The model for the cell.
|
||||
* @param cellStyle The style to use.
|
||||
* @return The created cell.
|
||||
*/
|
||||
static Cell createCell(WorksheetEnv env, Row row, int colNum, ExcelCellModel cellModel, Optional<CellStyle> cellStyle) {
|
||||
CellStyle cellStyleToUse = cellStyle.orElse(env.getDefaultCellStyle());
|
||||
|
||||
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) {
|
||||
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 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());
|
||||
}
|
||||
cell.setCellStyle(cellStyleToUse);
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,265 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
private final int rowEnd;
|
||||
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;
|
||||
this.rowEnd = rowEnd;
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemDimensions write(Sheet sheet, int rowStart, int colStart, ExcelExport.WorksheetEnv env) throws ExcelExportException {
|
||||
Row row = sheet.createRow(rowStart);
|
||||
ExcelExport.createCell(env, row, colStart, item, Optional.empty());
|
||||
return new ItemDimensions(rowStart, colStart, rowStart, colStart);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemDimensions write(Sheet sheet, int rowStart, int colStart, ExcelExport.WorksheetEnv env) throws ExcelExportException {
|
||||
Row row = sheet.createRow(rowStart);
|
||||
ExcelExport.createCell(env, row, colStart, key, Optional.of(env.getHeaderStyle()));
|
||||
ExcelExport.createCell(env, row, colStart + 1, value, Optional.empty());
|
||||
return new ItemDimensions(rowStart, colStart, rowStart, colStart + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemDimensions write(Sheet sheet, int rowStart, int colStart, ExcelExport.WorksheetEnv env) throws ExcelExportException {
|
||||
ExcelExport.createCell(env, sheet.createRow(rowStart), colStart, new DefaultCellModel<>(title), Optional.of(env.getHeaderStyle()));
|
||||
int curRow = rowStart + 1;
|
||||
int maxCol = colStart;
|
||||
for (ExcelItemExportable export : children) {
|
||||
if (export == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ItemDimensions thisItemDim = export.write(sheet, curRow, colStart + DEFAULT_INDENT, env);
|
||||
curRow = thisItemDim.getRowEnd() + 1;
|
||||
maxCol = Math.max(thisItemDim.getColEnd(), maxCol);
|
||||
}
|
||||
|
||||
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
|
||||
public String getSheetName() {
|
||||
return sheetName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderSheet(Sheet sheet, ExcelExport.WorksheetEnv env) throws ExcelExportException {
|
||||
int rowStart = 0;
|
||||
int maxCol = 0;
|
||||
for (ExcelItemExportable export : exports) {
|
||||
if (export == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ItemDimensions dimensions = export.write(sheet, rowStart, 0, env);
|
||||
rowStart = dimensions.getRowEnd() + 1;
|
||||
maxCol = Math.max(maxCol, dimensions.getColEnd());
|
||||
}
|
||||
|
||||
// Resize all columns to fit the content size
|
||||
for (int i = 0; i <= maxCol; i++) {
|
||||
sheet.autoSizeColumn(i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -18,40 +18,26 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
|
||||
|
||||
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 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.apache.poi.ss.usermodel.Workbook;
|
||||
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport.ExcelCellModel;
|
||||
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;
|
||||
|
||||
/**
|
||||
* An excel sheet export of table data.
|
||||
*/
|
||||
public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelExport.ExcelSheetExport {
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelSheetExport, ExcelItemExportable {
|
||||
|
||||
private final String sheetName;
|
||||
private final List<ColumnModel<T, C>> columns;
|
||||
private final List<T> data;
|
||||
private final int columnIndent;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
@ -62,9 +48,23 @@ public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelExpor
|
||||
* @param data The data to export.
|
||||
*/
|
||||
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;
|
||||
this.data = data;
|
||||
this.columnIndent = columnIndent;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -74,11 +74,20 @@ public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelExpor
|
||||
|
||||
@Override
|
||||
public void renderSheet(Sheet sheet, ExcelExport.WorksheetEnv style) throws ExcelExport.ExcelExportException {
|
||||
renderSheet(sheet, style, columns, data);
|
||||
renderSheet(sheet, style, 0, columnIndent, columns, data);
|
||||
|
||||
// Resize all columns to fit the content size
|
||||
for (int i = 0; i < columns.size(); i++) {
|
||||
sheet.autoSizeColumn(i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemDimensions write(Sheet sheet, int rowStart, int colStart, ExcelExport.WorksheetEnv env) throws ExcelExportException {
|
||||
int columnStart = columnIndent + colStart;
|
||||
int rowsWritten = renderSheet(sheet, env, rowStart, columnStart, columns, data);
|
||||
return new ItemDimensions(rowStart, columnStart, rowStart + rowsWritten - 1, this.columns == null ? columnStart : columnStart + this.columns.size());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,88 +95,44 @@ public class ExcelTableExport<T, C extends ExcelCellModel> implements ExcelExpor
|
||||
*
|
||||
* @param sheet The sheet.
|
||||
* @param worksheetEnv The worksheet environment and preferences.
|
||||
* @param rowStart The row to start in.
|
||||
* @param colStart The column to start in.
|
||||
* @param columns The columns.
|
||||
* @param data The data.
|
||||
* @throws ExcelExportException
|
||||
* @return The number of rows (including the header) written.
|
||||
*/
|
||||
private static <T, C extends ExcelCellModel> void renderSheet(
|
||||
Sheet sheet, ExcelExport.WorksheetEnv worksheetEnv, List<ColumnModel<T, C>> columns, List<T> data)
|
||||
private static <T, C extends ExcelCellModel> int renderSheet(
|
||||
Sheet sheet,
|
||||
ExcelExport.WorksheetEnv worksheetEnv,
|
||||
int rowStart,
|
||||
int colStart,
|
||||
List<ColumnModel<T, C>> columns, List<T> data)
|
||||
throws ExcelExport.ExcelExportException {
|
||||
|
||||
List<T> safeData = data == null ? Collections.emptyList() : data;
|
||||
// Create a header row
|
||||
Row headerRow = sheet.createRow(0);
|
||||
Row headerRow = sheet.createRow(rowStart);
|
||||
// Create header cells
|
||||
for (int i = 0; i < columns.size(); i++) {
|
||||
Cell cell = headerRow.createCell(i);
|
||||
Cell cell = headerRow.createCell(i + colStart);
|
||||
cell.setCellValue(columns.get(i).getHeaderTitle());
|
||||
cell.setCellStyle(worksheetEnv.getHeaderStyle());
|
||||
}
|
||||
// freeze header row
|
||||
sheet.createFreezePane(0, 1);
|
||||
// Create Cell Style for each column (if one is needed)
|
||||
Map<String, CellStyle> cellStyles = new HashMap<>();
|
||||
|
||||
for (int rowNum = 0; rowNum < safeData.size(); rowNum++) {
|
||||
T rowData = safeData.get(rowNum);
|
||||
Row row = sheet.createRow(rowNum + 1);
|
||||
Row row = sheet.createRow(rowNum + rowStart + 1);
|
||||
for (int colNum = 0; colNum < columns.size(); colNum++) {
|
||||
ColumnModel<T, ? extends ExcelCellModel> colModel = columns.get(colNum);
|
||||
ExcelCellModel cellModel = colModel.getCellRenderer().apply(rowData);
|
||||
String formatString = cellModel.getExcelFormatString();
|
||||
Optional<CellStyle> cellStyle = (formatString == null)
|
||||
? Optional.empty()
|
||||
: Optional.of(cellStyles.computeIfAbsent(formatString, k -> createCellStyle(worksheetEnv.getParentWorkbook(), formatString)));
|
||||
createCell(row, colNum, cellModel, cellStyle);
|
||||
ExcelExport.createCell(worksheetEnv, row, colNum + colStart, cellModel, Optional.empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a cell style in the workbook with the given format string.
|
||||
*
|
||||
* @param workbook The workbook.
|
||||
* @param formatString The format string.
|
||||
* @return The cell style.
|
||||
*/
|
||||
private static <T> CellStyle createCellStyle(Workbook workbook, String formatString) {
|
||||
CellStyle cellStyle = workbook.createCellStyle();
|
||||
cellStyle.setDataFormat(workbook.getCreationHelper().createDataFormat().getFormat(formatString));
|
||||
return cellStyle;
|
||||
return safeData.size() + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an excel cell given the model.
|
||||
*
|
||||
* @param row The row in the excel document.
|
||||
* @param colNum The column number (not zero-indexed).
|
||||
* @param cellModel The model for the cell.
|
||||
* @param cellStyle The style to use.
|
||||
* @return The created cell.
|
||||
*/
|
||||
private static Cell createCell(Row row, int colNum, ExcelCellModel cellModel, Optional<CellStyle> 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 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user