From 72c1e5a224bc6cb482eb20339b6b0ebf2379c73a Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 17 Mar 2021 11:50:02 -0400 Subject: [PATCH] commenting --- .../ui/BaseDataSourceSummaryPanel.java | 2 +- .../datasourcesummary/ui/ContainerPanel.java | 169 +++++++++++++++--- .../ui/DataSourceSummaryTabbedPane.java | 12 +- .../ui/IngestJobExcelExport.java | 81 ++++++++- .../ui/SizeRepresentationUtil.java | 32 +++- .../datasourcesummary/ui/TypesPanel.java | 24 ++- .../datasourcesummary/uiutils/CellModel.java | 19 +- .../uiutils/DefaultCellModel.java | 8 +- .../uiutils/ExcelExport.java | 137 +++++++++++--- .../uiutils/ExcelSpecialFormatExport.java | 127 +++++++++++-- .../uiutils/ExcelTableExport.java | 29 ++- 11 files changed, 530 insertions(+), 110 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java index 818dc85cdd..35d65db932 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/BaseDataSourceSummaryPanel.java @@ -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; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ContainerPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ContainerPanel.java index affba43a61..a8e5fef64e 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ContainerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/ContainerPanel.java @@ -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 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 getPaths() { + /** + * @return The source paths for the image. + */ + List 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 { + /** + * 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 retrieve(Retriever 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 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 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:", diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java index c65e5e39cb..4a77a8a485 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java @@ -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 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 diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/IngestJobExcelExport.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/IngestJobExcelExport.java index 51e0407286..3ad757e7be 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/IngestJobExcelExport.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/IngestJobExcelExport.java @@ -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 sleuthkit 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>> 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 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 getEntries(IngestJobInfo job) { List 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 showFirstRowOnly(List 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 getExports(DataSource dataSource) { if (dataSource == null) { return Collections.emptyList(); @@ -167,6 +230,7 @@ class IngestJobExcelExport { List 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() { + } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/SizeRepresentationUtil.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/SizeRepresentationUtil.java index ef30e790bb..74b9be06a8 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/SizeRepresentationUtil.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/SizeRepresentationUtil.java @@ -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<>(""); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java index c7e90e5072..97fc63fc51 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java @@ -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 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 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(count, COMMA_FORMATTER::format, COMMA_FORMAT_STR)); } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModel.java index 38afc21e2f..60d7e27112 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModel.java @@ -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; + } } /** diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultCellModel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultCellModel.java index 009405bf4b..06165a3023 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultCellModel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/DefaultCellModel.java @@ -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 implements GuiCellModel, ExcelCellModel { - private final T data; - private final Function stringConverter; + final T data; + final Function stringConverter; String tooltip; CellModel.HorizontalAlign horizontalAlignment; Insets insets; List popupMenu; Supplier> menuItemSupplier; - private final String excelFormatString; + final String excelFormatString; /** * Main constructor. diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelExport.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelExport.java index 7472bdada7..fab6558c4a 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelExport.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelExport.java @@ -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, CellStyle> cellStyleCache = new HashMap<>(); - + private final Map 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) { + static Cell createCell(WorksheetEnv env, Row row, int colNum, ExcelCellModel cellModel, Optional 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) { diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelSpecialFormatExport.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelSpecialFormatExport.java index dce61185fe..f9c46fa5e2 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelSpecialFormatExport.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelSpecialFormatExport.java @@ -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 sleuthkit 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 exports; - - public ExcelSpecialFormatExport(String sheetName, List 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. + *
+     * title
+     *      item 1
+     *      item 2
+     * 
+ */ 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 children; + /** + * Main constructor. + * + * @param title The title for the export. + * @param children The children to be indented and enumerated. + */ public TitledExportable(String title, List 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 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 exports) { + this.sheetName = sheetName; + this.exports = exports == null ? Collections.emptyList() : exports; } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelTableExport.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelTableExport.java index 9a10a1541c..5592344e2c 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelTableExport.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/ExcelTableExport.java @@ -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 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> columns; private final List data; @@ -66,7 +50,16 @@ public class ExcelTableExport implements ExcelSheet public ExcelTableExport(String sheetName, List> columns, List 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> columns, List data, int columnIndent) { this.sheetName = sheetName; this.columns = columns; @@ -129,7 +122,7 @@ public class ExcelTableExport 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);