diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties-MERGED index 24083965fb..d5c75e81a3 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/Bundle.properties-MERGED @@ -26,7 +26,7 @@ CallLogArtifactViewer_label_from=From CallLogArtifactViewer_label_to=To CallLogArtifactViewer_suffix_local=(Local) CallLogArtifactViewer_value_unknown=Unknown -#{0} - contact name +# {0} - contact name CommunicationArtifactViewerHelper_contact_label=Contact: {0} CommunicationArtifactViewerHelper_contact_label_unknown=Unknown CommunicationArtifactViewerHelper_menuitem_copy=Copy diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/CommunicationArtifactViewerHelper.java b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/CommunicationArtifactViewerHelper.java index 071fc7011b..e65550f88c 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/CommunicationArtifactViewerHelper.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/CommunicationArtifactViewerHelper.java @@ -440,7 +440,7 @@ final class CommunicationArtifactViewerHelper { * @return A JLabel with the contact information. */ @NbBundle.Messages({ - "#{0} - contact name", + "# {0} - contact name", "CommunicationArtifactViewerHelper_contact_label=Contact: {0}", "CommunicationArtifactViewerHelper_contact_label_unknown=Unknown" }) diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceCountsSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceCountsSummary.java index 0f2826636b..54e61e4a87 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceCountsSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceCountsSummary.java @@ -43,7 +43,8 @@ public class DataSourceCountsSummary { * @return The count. */ public static Long getCountOfFiles(DataSource currentDataSource) { - return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource, null, + return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource, + null, "Unable to get count of files, providing empty results"); } @@ -55,7 +56,7 @@ public class DataSourceCountsSummary { * @return The count. */ public static Long getCountOfAllocatedFiles(DataSource currentDataSource) { - return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource, + return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(currentDataSource, DataSourceInfoUtilities.getMetaFlagsContainsStatement(TskData.TSK_FS_META_FLAG_ENUM.ALLOC), "Unable to get counts of unallocated files for datasource, providing empty results"); } @@ -68,9 +69,8 @@ public class DataSourceCountsSummary { * @return The count. */ public static Long getCountOfUnallocatedFiles(DataSource currentDataSource) { - return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource, - DataSourceInfoUtilities.getMetaFlagsContainsStatement(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC) - + " AND type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType(), + return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(currentDataSource, + DataSourceInfoUtilities.getMetaFlagsContainsStatement(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC), "Unable to get counts of unallocated files for datasource, providing empty results"); } @@ -97,8 +97,7 @@ public class DataSourceCountsSummary { */ public static Long getCountOfSlackFiles(DataSource currentDataSource) { return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource, - DataSourceInfoUtilities.getMetaFlagsContainsStatement(TskData.TSK_FS_META_FLAG_ENUM.UNALLOC) - + " AND type=" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType(), + "type=" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType(), "Unable to get count of slack files for datasources, providing empty results"); } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java index 4480b207e1..a5ec1be604 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceInfoUtilities.java @@ -37,8 +37,8 @@ import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; import org.sleuthkit.datamodel.BlackboardAttribute.Type; -import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM; import org.sleuthkit.datamodel.TskData.TSK_FS_META_FLAG_ENUM; import org.sleuthkit.datamodel.TskData.TSK_FS_META_TYPE_ENUM; @@ -51,8 +51,7 @@ final class DataSourceInfoUtilities { private static final Logger logger = Logger.getLogger(DataSourceInfoUtilities.class.getName()); /** - * Gets a count of tsk_files for a particular datasource where dir_type is - * not a virtual directory and has a name. + * Gets a count of tsk_files for a particular datasource. * * @param currentDataSource The datasource. * @param additionalWhere Additional sql where clauses. @@ -66,8 +65,6 @@ final class DataSourceInfoUtilities { SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); return skCase.countFilesWhere( "data_source_obj_id=" + currentDataSource.getId() - + " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() - + " AND name<>''" + (StringUtils.isBlank(additionalWhere) ? "" : (" AND " + additionalWhere))); } catch (TskCoreException | NoCurrentCaseException ex) { logger.log(Level.WARNING, onError, ex); @@ -78,8 +75,7 @@ final class DataSourceInfoUtilities { } /** - * Gets a count of regular files for a particular datasource where the - * dir_type and type are not a virtual directory and has a name. + * Gets a count of regular files for a particular datasource. * * @param currentDataSource The datasource. * @param additionalWhere Additional sql where clauses. @@ -88,8 +84,27 @@ final class DataSourceInfoUtilities { * @return The count of files or null on error. */ static Long getCountOfRegularFiles(DataSource currentDataSource, String additionalWhere, String onError) { + String whereClause = "meta_type=" + TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue(); + + if (StringUtils.isNotBlank(additionalWhere)) { + whereClause += " AND " + additionalWhere; + } + + return getCountOfTskFiles(currentDataSource, whereClause, onError); + } + + /** + * Gets a count of regular non-slack files for a particular datasource. + * + * @param currentDataSource The datasource. + * @param additionalWhere Additional sql where clauses. + * @param onError The message to log on error. + * + * @return The count of files or null on error. + */ + static Long getCountOfRegNonSlackFiles(DataSource currentDataSource, String additionalWhere, String onError) { String whereClause = "meta_type=" + TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue() - + " AND type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType(); + + " AND type<>" + TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType(); if (StringUtils.isNotBlank(additionalWhere)) { whereClause += " AND " + additionalWhere; diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceMimeTypeSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceMimeTypeSummary.java index add28c0364..95dc360d2a 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceMimeTypeSummary.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/DataSourceMimeTypeSummary.java @@ -43,7 +43,7 @@ public class DataSourceMimeTypeSummary { * source, null if no count was retrieved */ public static Long getCountOfFilesForMimeTypes(DataSource currentDataSource, Set setOfMimeTypes) { - return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource, + return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(currentDataSource, "mime_type IN " + getSqlSet(setOfMimeTypes), "Unable to get count of files for specified mime types"); } @@ -61,11 +61,22 @@ public class DataSourceMimeTypeSummary { * have the specific mime type, but do have a mime type. */ public static Long getCountOfFilesNotInMimeTypes(DataSource currentDataSource, Set setOfMimeTypes) { - return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource, + return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(currentDataSource, "mime_type NOT IN " + getSqlSet(setOfMimeTypes) + " AND mime_type IS NOT NULL AND mime_type <> '' ", "Unable to get count of files without specified mime types"); } + + + /** + * Get a count of all regular files in a datasource. + * @param dataSource The datasource. + * @return The count of regular files. + */ + public static Long getCountOfAllRegularFiles(DataSource dataSource) { + return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(dataSource, null, + "Unable to get count of all regular files"); + } /** * Gets the number of files in the data source with no assigned mime type. @@ -77,7 +88,7 @@ public class DataSourceMimeTypeSummary { * */ public static Long getCountOfFilesWithNoMimeType(DataSource currentDataSource) { - return DataSourceInfoUtilities.getCountOfRegularFiles(currentDataSource, + return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(currentDataSource, "(mime_type IS NULL OR mime_type = '') ", "Unable to get count of files without a mime type"); } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties index b88a008e10..c099f7e206 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties @@ -28,8 +28,6 @@ DataSourceSummaryDetailsPanel.acquisitionDetailsTextArea.text= DataSourceSummaryDetailsPanel.acquisitionDetailsLabel.text=Acquisition Details: DataSourceSummaryDetailsPanel.unallocatedSizeLabel.text=Unallocated Space: DataSourceSummaryDetailsPanel.unallocatedSizeValue.text= -DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category -DataSourceSummaryCountsPanel.resultsByTypeLabel.text=Results by Type DataSourceSummaryUserActivityPanel.programsRunLabel.text=Recent Programs DataSourceSummaryUserActivityPanel.recentAccountsLabel.text=Recent Accounts DataSourceSummaryUserActivityPanel.topWebSearchLabel.text=Recent Web Searches diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED index f3325f2e46..0b14791df5 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED @@ -1,31 +1,8 @@ AnalysisPanel_countColumn_title=Count AnalysisPanel_keyColumn_title=Name CTL_DataSourceSummaryAction=Data Source Summary -DataSourceSummaryCountsPanel.ArtifactCountsTableModel.count.header=Count -DataSourceSummaryCountsPanel.ArtifactCountsTableModel.type.header=Result Type -DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type -DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=All -DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=Allocated -DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count -DataSourceSummaryCountsPanel.FilesByCategoryTableModel.directory.row=Directory -DataSourceSummaryCountsPanel.FilesByCategoryTableModel.slack.row=Slack -DataSourceSummaryCountsPanel.FilesByCategoryTableModel.type.header=File Type -DataSourceSummaryCountsPanel.FilesByCategoryTableModel.unallocated.row=Unallocated -DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.audio.row=Audio -DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.documents.row=Documents -DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.executables.row=Executables -DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.images.row=Images -DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.videos.row=Videos -DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_notAnalyzed_label=Not Analyzed -DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_other_label=Other DataSourceSummaryDetailsPanel.getDataSources.error.text=Failed to get the list of datasources for the current case. DataSourceSummaryDetailsPanel.getDataSources.error.title=Load Failure -DataSourceSummaryDetailsPanel.units.bytes=\ bytes -DataSourceSummaryDetailsPanel.units.gigabytes=\ GB -DataSourceSummaryDetailsPanel.units.kilobytes=\ kB -DataSourceSummaryDetailsPanel.units.megabytes=\ MB -DataSourceSummaryDetailsPanel.units.petabytes=\ PB -DataSourceSummaryDetailsPanel.units.terabytes=\ TB DataSourceSummaryDialog.closeButton.text=Close DataSourceSummaryDetailsPanel.displayNameLabel.text=Display Name: DataSourceSummaryDetailsPanel.originalNameLabel.text=Name: @@ -56,8 +33,6 @@ DataSourceSummaryDetailsPanel.acquisitionDetailsTextArea.text= DataSourceSummaryDetailsPanel.acquisitionDetailsLabel.text=Acquisition Details: DataSourceSummaryDetailsPanel.unallocatedSizeLabel.text=Unallocated Space: DataSourceSummaryDetailsPanel.unallocatedSizeValue.text= -DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category -DataSourceSummaryCountsPanel.resultsByTypeLabel.text=Results by Type DataSourceSummaryDialog.window.title=Data Sources Summary DataSourceSummaryNode.column.dataSourceName.header=Data Source Name DataSourceSummaryNode.column.files.header=Files @@ -67,10 +42,10 @@ DataSourceSummaryNode.column.tags.header=Tags DataSourceSummaryNode.column.type.header=Type DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source DataSourceSummaryTabbedPane_analysisTab_title=Analysis -DataSourceSummaryTabbedPane_countsTab_title=Counts DataSourceSummaryTabbedPane_detailsTab_title=Container DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files +DataSourceSummaryTabbedPane_typesTab_title=Types DataSourceSummaryTabbedPane_userActivityTab_title=User Activity DataSourceSummaryUserActivityPanel.programsRunLabel.text=Recent Programs DataSourceSummaryUserActivityPanel.recentAccountsLabel.text=Recent Accounts @@ -105,4 +80,27 @@ RecentFilesPanel.openDocsLabel.text=Recently Opened Documents RecentFilesPanel.downloadLabel.text=Recent Downloads RecentFilesPanel.attachmentLabel.text=Recent Attachements RecentFilesPanel_col_head_date=Date +SizeRepresentationUtil_units_bytes=\ bytes +SizeRepresentationUtil_units_gigabytes=\ GB +SizeRepresentationUtil_units_kilobytes=\ kB +SizeRepresentationUtil_units_megabytes=\ MB +SizeRepresentationUtil_units_petabytes=\ PB +SizeRepresentationUtil_units_terabytes=\ TB +TypesPanel_artifactsTypesPieChart_title=Artifact Types +TypesPanel_fileMimeTypesChart_audio_title=Audio +TypesPanel_fileMimeTypesChart_documents_title=Documents +TypesPanel_fileMimeTypesChart_executables_title=Executables +TypesPanel_fileMimeTypesChart_images_title=Images +TypesPanel_fileMimeTypesChart_notAnalyzed_title=Not Analyzed +TypesPanel_fileMimeTypesChart_other_title=Other +TypesPanel_fileMimeTypesChart_title=File Types +TypesPanel_fileMimeTypesChart_unknown_title=Unknown +TypesPanel_fileMimeTypesChart_videos_title=Videos +TypesPanel_filesByCategoryTable_allocatedRow_title=Allocated Files +TypesPanel_filesByCategoryTable_directoryRow_title=Directories +TypesPanel_filesByCategoryTable_slackRow_title=Slack Files +TypesPanel_filesByCategoryTable_unallocatedRow_title=Unallocated Files +TypesPanel_osLabel_title=OS +TypesPanel_sizeLabel_title=Size +TypesPanel_usageLabel_title=Usage ViewSummaryInformationAction.name.text=View Summary Information diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.form deleted file mode 100644 index c9c1930787..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.form +++ /dev/null @@ -1,222 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.java deleted file mode 100644 index 306a036941..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryCountsPanel.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2019 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; - -import org.sleuthkit.autopsy.datasourcesummary.uiutils.NonEditableTableModel; -import java.util.Map; -import javax.swing.JLabel; -import javax.swing.table.DefaultTableCellRenderer; -import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceCountsSummary; - -import org.sleuthkit.datamodel.DataSource; - -/** - * Panel for displaying summary information on the known files present in the - * specified DataSource - */ -@Messages({ - "DataSourceSummaryCountsPanel.ArtifactCountsTableModel.type.header=Result Type", - "DataSourceSummaryCountsPanel.ArtifactCountsTableModel.count.header=Count", - "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.type.header=File Type", - "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count" -}) -class DataSourceSummaryCountsPanel extends BaseDataSourceSummaryPanel { - - private static final long serialVersionUID = 1L; - - // Result returned for a data model if no data found. - private static final Object[][] EMPTY_PAIRS = new Object[][]{}; - - // column headers for file by category table - private static final Object[] FILE_BY_CATEGORY_COLUMN_HEADERS = new Object[]{ - Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_type_header(), - Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_count_header() - }; - - // column headers for artifact counts table - private static final Object[] ARTIFACT_COUNTS_COLUMN_HEADERS = new Object[]{ - Bundle.DataSourceSummaryCountsPanel_ArtifactCountsTableModel_type_header(), - Bundle.DataSourceSummaryCountsPanel_ArtifactCountsTableModel_count_header() - }; - - private final DefaultTableCellRenderer rightAlignedRenderer = new DefaultTableCellRenderer(); - - private final FileTypePieChart fileTypePieChart = new FileTypePieChart(); - - /** - * Creates new form DataSourceSummaryCountsPanel - */ - DataSourceSummaryCountsPanel() { - rightAlignedRenderer.setHorizontalAlignment(JLabel.RIGHT); - initComponents(); - fileCountsByCategoryTable.getTableHeader().setReorderingAllowed(false); - artifactCountsTable.getTableHeader().setReorderingAllowed(false); - setDataSource(null); - } - - @Override - protected void onNewDataSource(DataSource dataSource) { - if (dataSource == null || !Case.isCaseOpen()) { - updateCountsTableData(EMPTY_PAIRS, EMPTY_PAIRS); - } else { - updateCountsTableData(getFileCategoryModel(dataSource), getArtifactCountsModel(dataSource)); - } - this.fileTypePieChart.setDataSource(dataSource); - } - - /** - * Specify the DataSource to display file information for. - * - * @param fileCategoryDataModel The file category data model. - * @param artifactDataModel The artifact type data model. - */ - private void updateCountsTableData(Object[][] fileCategoryDataModel, Object[][] artifactDataModel) { - fileCountsByCategoryTable.setModel(new NonEditableTableModel(fileCategoryDataModel, FILE_BY_CATEGORY_COLUMN_HEADERS)); - fileCountsByCategoryTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer); - fileCountsByCategoryTable.getColumnModel().getColumn(0).setPreferredWidth(130); - - artifactCountsTable.setModel(new NonEditableTableModel(artifactDataModel, ARTIFACT_COUNTS_COLUMN_HEADERS)); - artifactCountsTable.getColumnModel().getColumn(0).setPreferredWidth(230); - artifactCountsTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer); - - this.repaint(); - } - - /** - * Determines the JTable data model for datasource file categories. - * - * @param dataSource The DataSource. - * - * @return The model to be used with a JTable. - */ - @Messages({ - "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=All", - "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=Allocated", - "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.unallocated.row=Unallocated", - "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.slack.row=Slack", - "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.directory.row=Directory" - }) - private static Object[][] getFileCategoryModel(DataSource selectedDataSource) { - Long fileCount = zeroIfNull(DataSourceCountsSummary.getCountOfFiles(selectedDataSource)); - Long unallocatedFiles = zeroIfNull(DataSourceCountsSummary.getCountOfUnallocatedFiles(selectedDataSource)); - Long allocatedFiles = zeroIfNull(DataSourceCountsSummary.getCountOfAllocatedFiles(selectedDataSource)); - Long slackFiles = zeroIfNull(DataSourceCountsSummary.getCountOfSlackFiles(selectedDataSource)); - Long directories = zeroIfNull(DataSourceCountsSummary.getCountOfDirectories(selectedDataSource)); - - return new Object[][]{ - new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_all_row(), fileCount}, - new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_allocated_row(), allocatedFiles}, - new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_unallocated_row(), unallocatedFiles}, - new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_slack_row(), slackFiles}, - new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_directory_row(), directories} - }; - } - - /** - * Returns 0 if value is null. - * - * @param origValue The original value. - * - * @return The value or 0 if null. - */ - private static Long zeroIfNull(Long origValue) { - return origValue == null ? 0 : origValue; - } - - /** - * The counts of different artifact types found in a DataSource. - * - * @param selectedDataSource The DataSource. - * - * @return The JTable data model of counts of artifact types. - */ - private static Object[][] getArtifactCountsModel(DataSource selectedDataSource) { - Map artifactMapping = DataSourceCountsSummary.getCountsOfArtifactsByType(selectedDataSource); - if (artifactMapping == null) { - return EMPTY_PAIRS; - } - - return artifactMapping.entrySet().stream() - .filter((entrySet) -> entrySet != null && entrySet.getKey() != null) - .sorted((a, b) -> a.getKey().compareTo(b.getKey())) - .map((entrySet) -> new Object[]{entrySet.getKey(), entrySet.getValue()}) - .toArray(Object[][]::new); - } - - /** - * 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 - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - javax.swing.JScrollPane scrollParent = new javax.swing.JScrollPane(); - javax.swing.JPanel parentPanel = new javax.swing.JPanel(); - javax.swing.JScrollPane fileCountsByCategoryScrollPane = new javax.swing.JScrollPane(); - fileCountsByCategoryTable = new javax.swing.JTable(); - javax.swing.JLabel byCategoryLabel = new javax.swing.JLabel(); - javax.swing.JLabel resultsByTypeLabel = new javax.swing.JLabel(); - javax.swing.JScrollPane artifactCountsScrollPane = new javax.swing.JScrollPane(); - artifactCountsTable = new javax.swing.JTable(); - javax.swing.JPanel fileTypePiePanel = fileTypePieChart; - javax.swing.JPanel filesByCatParent = new javax.swing.JPanel(); - javax.swing.JPanel resultsByTypeParent = new javax.swing.JPanel(); - - parentPanel.setMinimumSize(new java.awt.Dimension(840, 320)); - - fileCountsByCategoryScrollPane.setViewportView(fileCountsByCategoryTable); - - org.openide.awt.Mnemonics.setLocalizedText(byCategoryLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.byCategoryLabel.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(resultsByTypeLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.resultsByTypeLabel.text")); // NOI18N - - artifactCountsTable.setAutoCreateRowSorter(true); - artifactCountsScrollPane.setViewportView(artifactCountsTable); - - fileTypePiePanel.setPreferredSize(new java.awt.Dimension(400, 300)); - - javax.swing.GroupLayout filesByCatParentLayout = new javax.swing.GroupLayout(filesByCatParent); - filesByCatParent.setLayout(filesByCatParentLayout); - filesByCatParentLayout.setHorizontalGroup( - filesByCatParentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 0, Short.MAX_VALUE) - ); - filesByCatParentLayout.setVerticalGroup( - filesByCatParentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 0, Short.MAX_VALUE) - ); - - javax.swing.GroupLayout resultsByTypeParentLayout = new javax.swing.GroupLayout(resultsByTypeParent); - resultsByTypeParent.setLayout(resultsByTypeParentLayout); - resultsByTypeParentLayout.setHorizontalGroup( - resultsByTypeParentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 0, Short.MAX_VALUE) - ); - resultsByTypeParentLayout.setVerticalGroup( - resultsByTypeParentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 0, Short.MAX_VALUE) - ); - - javax.swing.GroupLayout parentPanelLayout = new javax.swing.GroupLayout(parentPanel); - parentPanel.setLayout(parentPanelLayout); - parentPanelLayout.setHorizontalGroup( - parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(parentPanelLayout.createSequentialGroup() - .addContainerGap() - .addComponent(fileTypePiePanel, javax.swing.GroupLayout.PREFERRED_SIZE, 400, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18) - .addGroup(parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(fileCountsByCategoryScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(byCategoryLabel) - .addComponent(filesByCatParent, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(18, 18, 18) - .addGroup(parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(resultsByTypeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(parentPanelLayout.createSequentialGroup() - .addComponent(artifactCountsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 244, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(resultsByTypeParent, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - parentPanelLayout.setVerticalGroup( - parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(parentPanelLayout.createSequentialGroup() - .addContainerGap() - .addGroup(parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(parentPanelLayout.createSequentialGroup() - .addComponent(fileTypePiePanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addGroup(parentPanelLayout.createSequentialGroup() - .addGroup(parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(byCategoryLabel) - .addComponent(resultsByTypeLabel)) - .addGroup(parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, parentPanelLayout.createSequentialGroup() - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(resultsByTypeParent, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(148, 148, 148)) - .addGroup(parentPanelLayout.createSequentialGroup() - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(parentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(artifactCountsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) - .addGroup(parentPanelLayout.createSequentialGroup() - .addComponent(fileCountsByCategoryScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(31, 31, 31) - .addComponent(filesByCatParent, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, Short.MAX_VALUE))) - .addContainerGap()))))) - ); - - scrollParent.setViewportView(parentPanel); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(scrollParent) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(scrollParent) - ); - }// //GEN-END:initComponents - - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JTable artifactCountsTable; - private javax.swing.JTable fileCountsByCategoryTable; - // End of variables declaration//GEN-END:variables -} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDetailsPanel.java index c9df74d2b8..3750ec76bd 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryDetailsPanel.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.datasourcesummary.ui; -import java.text.DecimalFormat; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.table.DefaultTableModel; @@ -36,8 +35,6 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel { //Because this panel was made using the gridbaglayout and netbean's Customize Layout tool it will be best to continue to modify it through that private static final long serialVersionUID = 1L; - private static final Integer SIZE_COVERSION_CONSTANT = 1000; - private static final DecimalFormat APPROXIMATE_SIZE_FORMAT = new DecimalFormat("#.##"); private static final Logger logger = Logger.getLogger(DataSourceSummaryDetailsPanel.class.getName()); /** @@ -70,8 +67,7 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel { private void updateDetailsPanelData(DataSource selectedDataSource, Long unallocatedFilesSize, String osDetails, String usage) { clearTableValues(); if (selectedDataSource != null) { - unallocatedSizeValue.setText(getSizeString(unallocatedFilesSize)); - + unallocatedSizeValue.setText(SizeRepresentationUtil.getSizeString(unallocatedFilesSize)); timeZoneValue.setText(selectedDataSource.getTimeZone()); displayNameValue.setText(selectedDataSource.getName()); originalNameValue.setText(selectedDataSource.getName()); @@ -100,8 +96,8 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel { */ private void setFieldsForImage(Image selectedImage) { imageTypeValue.setText(selectedImage.getType().getName()); - sizeValue.setText(getSizeString(selectedImage.getSize())); - sectorSizeValue.setText(getSizeString(selectedImage.getSsize())); + sizeValue.setText(SizeRepresentationUtil.getSizeString(selectedImage.getSize())); + sectorSizeValue.setText(SizeRepresentationUtil.getSizeString(selectedImage.getSsize())); for (String path : selectedImage.getPaths()) { ((DefaultTableModel) filePathsTable.getModel()).addRow(new Object[]{path}); @@ -139,55 +135,6 @@ class DataSourceSummaryDetailsPanel extends BaseDataSourceSummaryPanel { } } - /** - * Get a long size in bytes as a string formated to be read by users. - * - * @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 - */ - @Messages({ - "DataSourceSummaryDetailsPanel.units.bytes= bytes", - "DataSourceSummaryDetailsPanel.units.kilobytes= kB", - "DataSourceSummaryDetailsPanel.units.megabytes= MB", - "DataSourceSummaryDetailsPanel.units.gigabytes= GB", - "DataSourceSummaryDetailsPanel.units.terabytes= TB", - "DataSourceSummaryDetailsPanel.units.petabytes= PB" - }) - private static String getSizeString(Long size) { - if (size == null) { - return ""; - } - double approximateSize = size; - if (approximateSize < SIZE_COVERSION_CONSTANT) { - return String.valueOf(size) + Bundle.DataSourceSummaryDetailsPanel_units_bytes(); - } - approximateSize /= SIZE_COVERSION_CONSTANT; - if (approximateSize < SIZE_COVERSION_CONSTANT) { - return APPROXIMATE_SIZE_FORMAT.format(approximateSize) + Bundle.DataSourceSummaryDetailsPanel_units_kilobytes() - + " (" + String.valueOf(size) + Bundle.DataSourceSummaryDetailsPanel_units_bytes() + ")"; - } - approximateSize /= SIZE_COVERSION_CONSTANT; - if (approximateSize < SIZE_COVERSION_CONSTANT) { - return APPROXIMATE_SIZE_FORMAT.format(approximateSize) + Bundle.DataSourceSummaryDetailsPanel_units_megabytes() - + " (" + String.valueOf(size) + Bundle.DataSourceSummaryDetailsPanel_units_bytes() + ")"; - } - approximateSize /= SIZE_COVERSION_CONSTANT; - if (approximateSize < SIZE_COVERSION_CONSTANT) { - return APPROXIMATE_SIZE_FORMAT.format(approximateSize) + Bundle.DataSourceSummaryDetailsPanel_units_gigabytes() - + " (" + String.valueOf(size) + Bundle.DataSourceSummaryDetailsPanel_units_bytes() + ")"; - } - approximateSize /= SIZE_COVERSION_CONSTANT; - if (approximateSize < SIZE_COVERSION_CONSTANT) { - return APPROXIMATE_SIZE_FORMAT.format(approximateSize) + Bundle.DataSourceSummaryDetailsPanel_units_terabytes() - + " (" + String.valueOf(size) + Bundle.DataSourceSummaryDetailsPanel_units_bytes() + ")"; - } - approximateSize /= SIZE_COVERSION_CONSTANT; - return APPROXIMATE_SIZE_FORMAT.format(approximateSize) + Bundle.DataSourceSummaryDetailsPanel_units_petabytes() - + " (" + String.valueOf(size) + Bundle.DataSourceSummaryDetailsPanel_units_bytes() + ")"; - } - /** * Update the visibility of all fields and their labels based on whether * they have contents. Empty fields have them and their contents hidden. diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java index 9696f4d4c4..6e6a4947e0 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java @@ -18,11 +18,11 @@ */ package org.sleuthkit.autopsy.datasourcesummary.ui; -import java.util.ArrayList; +import java.awt.Component; import java.util.Arrays; import java.util.List; +import java.util.function.Consumer; import javax.swing.JTabbedPane; -import org.apache.commons.lang3.tuple.Pair; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; import org.sleuthkit.datamodel.DataSource; @@ -33,7 +33,7 @@ import org.sleuthkit.datamodel.DataSource; * IngestJobInfoPanel. */ @Messages({ - "DataSourceSummaryTabbedPane_countsTab_title=Counts", + "DataSourceSummaryTabbedPane_typesTab_title=Types", "DataSourceSummaryTabbedPane_detailsTab_title=Container", "DataSourceSummaryTabbedPane_userActivityTab_title=User Activity", "DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History", @@ -42,18 +42,76 @@ import org.sleuthkit.datamodel.DataSource; }) public class DataSourceSummaryTabbedPane extends JTabbedPane { + /** + * Records of tab information (i.e. title, component, function to call on + * new data source). + */ + private static class DataSourceTab { + + private final String tabTitle; + private final Component component; + private final Consumer onDataSource; + + /** + * Main constructor. + * + * @param tabTitle The title of the tab. + * @param component The component to be displayed. + * @param onDataSource The function to be called on a new data source. + */ + DataSourceTab(String tabTitle, Component component, Consumer onDataSource) { + this.tabTitle = tabTitle; + this.component = component; + this.onDataSource = onDataSource; + } + + /** + * Main constructor. + * + * @param tabTitle The title of the tab. + * @param panel The component to be displayed in the tab. + */ + DataSourceTab(String tabTitle, BaseDataSourceSummaryPanel panel) { + this.tabTitle = tabTitle; + this.component = panel; + this.onDataSource = panel::setDataSource; + } + + /** + * @return The title for the tab. + */ + String getTabTitle() { + return tabTitle; + } + + /** + * @return The component to display in the tab. + */ + Component getComponent() { + return component; + } + + /** + * @return The function to be called on new data source. + */ + Consumer getOnDataSource() { + return onDataSource; + } + } + private static final long serialVersionUID = 1L; - // A pair of the tab name and the corresponding BaseDataSourceSummaryTabs to be displayed. - private final List> tabs = new ArrayList<>(Arrays.asList( - Pair.of(Bundle.DataSourceSummaryTabbedPane_countsTab_title(), new DataSourceSummaryCountsPanel()), - Pair.of(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), new DataSourceSummaryUserActivityPanel()), - Pair.of(Bundle.DataSourceSummaryTabbedPane_recentFileTab_title(), new RecentFilesPanel()), - Pair.of(Bundle.DataSourceSummaryTabbedPane_analysisTab_title(), new AnalysisPanel()) - )); - private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel(); + private final List tabs = Arrays.asList( + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_typesTab_title(), new TypesPanel()), + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), new DataSourceSummaryUserActivityPanel()), + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_recentFileTab_title(), new RecentFilesPanel()), + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_analysisTab_title(), new AnalysisPanel()), + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel, ingestHistoryPanel::setDataSource), + new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new DataSourceSummaryDetailsPanel()) + ); + private DataSource dataSource = null; /** @@ -64,18 +122,9 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { } private void initComponent() { - for (Pair tab : tabs) { - addTab(tab.getKey(), tab.getValue()); + for (DataSourceTab tab : tabs) { + addTab(tab.getTabTitle(), tab.getComponent()); } - - // IngestJobInfoPanel is not specifically a data source summary panel - // and is called separately for that reason. - addTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel); - - // The Container tab should be last. - Pair tab = Pair.of(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new DataSourceSummaryDetailsPanel()); - addTab(tab.getKey(), tab.getValue()); - tabs.add(tab); } /** @@ -95,12 +144,8 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; - for (Pair tab : tabs) { - tab.getValue().setDataSource(dataSource); + for (DataSourceTab tab : tabs) { + tab.getOnDataSource().accept(dataSource); } - - // IngestJobInfoPanel is not specifically a data source summary panel - // and is called separately for that reason. - ingestHistoryPanel.setDataSource(dataSource); } } diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/FileTypePieChart.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/FileTypePieChart.java deleted file mode 100644 index 691234cd48..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/FileTypePieChart.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2020 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; - -import java.awt.BorderLayout; -import java.awt.Font; -import javax.swing.JPanel; -import org.sleuthkit.datamodel.DataSource; - -import org.jfree.chart.ChartFactory; -import org.jfree.chart.ChartPanel; -import org.jfree.chart.JFreeChart; -import org.jfree.chart.labels.PieSectionLabelGenerator; -import org.jfree.chart.labels.StandardPieSectionLabelGenerator; -import org.jfree.chart.plot.PiePlot; -import org.jfree.data.general.DefaultPieDataset; - -import java.text.DecimalFormat; -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; -import javax.swing.JLabel; -import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.coreutils.FileTypeUtils; -import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceMimeTypeSummary; -import static org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory; - -/** - * A Pie Chart that shows file mime types in a data source. - */ -class FileTypePieChart extends JPanel { - - private static final long serialVersionUID = 1L; - - private static final Font DEFAULT_FONT = new JLabel().getFont(); - private static final Font DEFAULT_HEADER_FONT = new Font(DEFAULT_FONT.getName(), DEFAULT_FONT.getStyle(), (int) (DEFAULT_FONT.getSize() * 1.5)); - - private final DefaultPieDataset dataset = new DefaultPieDataset(); - private DataSource dataSource; - - // used for determining mime types that fall in the 'other' category - private static final Set ALL_CATEGORY_MIME_TYPES = Arrays.asList( - FileTypeCategory.IMAGE, - FileTypeCategory.VIDEO, - FileTypeCategory.AUDIO, - FileTypeCategory.DOCUMENTS, - FileTypeCategory.EXECUTABLE) - .stream() - .flatMap((cat) -> cat.getMediaTypes().stream()) - .collect(Collectors.toSet()); - - /** - * Default constructor for the pie chart. - */ - @Messages({ - "DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type", - "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.audio.row=Audio", - "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.documents.row=Documents", - "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.executables.row=Executables", - "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.images.row=Images", - "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.videos.row=Videos", - "DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_other_label=Other", - "DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_notAnalyzed_label=Not Analyzed" - }) - FileTypePieChart() { - // Create chart - JFreeChart chart = ChartFactory.createPieChart( - Bundle.DataSourceSummaryCountsPanel_byMimeTypeLabel_text(), - dataset, - true, - true, - false); - - chart.setBackgroundPaint(null); - chart.getLegend().setItemFont(DEFAULT_FONT); - chart.getTitle().setFont(DEFAULT_HEADER_FONT); - - PiePlot plot = ((PiePlot) chart.getPlot()); - - //Format Label - PieSectionLabelGenerator labelGenerator = new StandardPieSectionLabelGenerator( - "{0}: {1} ({2})", new DecimalFormat("0"), new DecimalFormat("0.0%")); - - plot.setLabelGenerator(labelGenerator); - plot.setLabelFont(DEFAULT_FONT); - - plot.setBackgroundPaint(null); - plot.setOutlinePaint(null); - - // Create Panel - ChartPanel panel = new ChartPanel(chart); - this.setLayout(new BorderLayout()); - this.add(panel, BorderLayout.CENTER); - } - - /** - * The datasource currently used as the model with this pie chart. - * - * @return The datasource currently being used as the model in this pie - * chart. - */ - DataSource getDataSource() { - return dataSource; - } - - /** - * Sets datasource to visualize in the pie chart. - * - * @param dataSource The datasource to use in this pie chart. - */ - void setDataSource(DataSource dataSource) { - this.dataSource = dataSource; - this.dataset.clear(); - - if (dataSource != null) { - addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_images_row(), - this.dataSource, FileTypeCategory.IMAGE); - addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_videos_row(), - this.dataSource, FileTypeCategory.VIDEO); - addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_audio_row(), - this.dataSource, FileTypeCategory.AUDIO); - addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_documents_row(), - this.dataSource, FileTypeCategory.DOCUMENTS); - addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_executables_row(), - this.dataSource, FileTypeCategory.EXECUTABLE); - addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_other_label(), - DataSourceMimeTypeSummary.getCountOfFilesNotInMimeTypes(this.dataSource, ALL_CATEGORY_MIME_TYPES)); - addIfPresent(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_notAnalyzed_label(), - DataSourceMimeTypeSummary.getCountOfFilesWithNoMimeType(this.dataSource)); - } - } - - /** - * Adds count for file type category if there is a value. Uses fields - * 'dataSource' and 'dataset'. - * - * @param label The label for this pie slice. - * @param dataSource The data source. - * @param category The category for the pie slice. - */ - private void addIfPresent(String label, DataSource dataSource, FileTypeUtils.FileTypeCategory category) { - if (dataSource == null) { - return; - } - - Long count = getCount(dataSource, category); - addIfPresent(label, count); - } - - /** - * Adds count for a a label if the count is non-null and greater than 0. - * - * @param label The label. - * @param count The count. - */ - private void addIfPresent(String label, Long count) { - if (count != null && count > 0) { - this.dataset.setValue(label, count); - } - } - - /** - * Retrieves the counts of files of a particular mime type for a particular - * DataSource. - * - * @param dataSource The DataSource. - * @param category The mime type category. - * - * @return The count. - */ - private static Long getCount(DataSource dataSource, FileTypeUtils.FileTypeCategory category) { - return DataSourceMimeTypeSummary.getCountOfFilesForMimeTypes(dataSource, category.getMediaTypes()); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/SizeRepresentationUtil.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/SizeRepresentationUtil.java new file mode 100644 index 0000000000..34c622563d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/SizeRepresentationUtil.java @@ -0,0 +1,103 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 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; + +import java.text.DecimalFormat; +import java.util.Arrays; +import java.util.List; +import org.openide.util.NbBundle; + +/** + * This class provides utilities for representing storage size in most relevant + * units (i.e. bytes, megabytes, etc.). + */ +public final class SizeRepresentationUtil { + + private static final int SIZE_CONVERSION_CONSTANT = 1000; + private static final DecimalFormat APPROXIMATE_SIZE_FORMAT = new DecimalFormat("#.##"); + + private static List 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() + ); + + /** + * Get a long size in bytes as a string formated to be read by users. + * + * @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. + */ + public static String getSizeString(Long size) { + return getSizeString(size, APPROXIMATE_SIZE_FORMAT, true); + } + + /** + * 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 showFullSize Optionally show the number of bytes in the + * datasource. + * + * @return Return a string formated with a user friendly version of the 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) { + 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; + } + } + + String fullSize = size + UNITS.get(0); + String closestUnitSize = format.format(approximateSize) + UNITS.get(unitsIndex); + + if (unitsIndex == 0) { + return fullSize; + } else if (showFullSize) { + return String.format("%s (%s)", closestUnitSize, fullSize); + } else { + return closestUnitSize; + } + } + + private SizeRepresentationUtil() { + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.form new file mode 100644 index 0000000000..2efe151eb5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.form @@ -0,0 +1,264 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java new file mode 100644 index 0000000000..46f475781c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/TypesPanel.java @@ -0,0 +1,377 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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; + +import java.awt.BorderLayout; +import java.text.DecimalFormat; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import javax.swing.JLabel; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceCountsSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceDetailsSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceMimeTypeSummary; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.AbstractLoadableComponent; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel.PieChartItem; + +import org.sleuthkit.datamodel.DataSource; + +/** + * Panel for displaying summary information on the known files present in the + * specified DataSource. + */ +@Messages({ + "TypesPanel_artifactsTypesPieChart_title=Artifact Types", + "TypesPanel_filesByCategoryTable_allocatedRow_title=Allocated Files", + "TypesPanel_filesByCategoryTable_unallocatedRow_title=Unallocated Files", + "TypesPanel_filesByCategoryTable_slackRow_title=Slack Files", + "TypesPanel_filesByCategoryTable_directoryRow_title=Directories", + "TypesPanel_fileMimeTypesChart_title=File Types", + "TypesPanel_fileMimeTypesChart_audio_title=Audio", + "TypesPanel_fileMimeTypesChart_documents_title=Documents", + "TypesPanel_fileMimeTypesChart_executables_title=Executables", + "TypesPanel_fileMimeTypesChart_images_title=Images", + "TypesPanel_fileMimeTypesChart_videos_title=Videos", + "TypesPanel_fileMimeTypesChart_other_title=Other", + "TypesPanel_fileMimeTypesChart_unknown_title=Unknown", + "TypesPanel_fileMimeTypesChart_notAnalyzed_title=Not Analyzed", + "TypesPanel_usageLabel_title=Usage", + "TypesPanel_osLabel_title=OS", + "TypesPanel_sizeLabel_title=Size"}) +class TypesPanel extends BaseDataSourceSummaryPanel { + + /** + * A label that allows for displaying loading messages and can be used with + * a DataFetchResult. Text displays as ":". + */ + private static class LoadableLabel extends AbstractLoadableComponent { + + private static final long serialVersionUID = 1L; + + private final JLabel label = new JLabel(); + private final String key; + + /** + * Main constructor for the label. + * + * @param key The key to be displayed. + */ + LoadableLabel(String key) { + this.key = key; + setLayout(new BorderLayout()); + add(label, BorderLayout.CENTER); + this.showResults(null); + } + + private void setValue(String value, boolean italicize) { + String formattedKey = StringUtils.isBlank(key) ? "" : key; + String formattedValue = StringUtils.isBlank(value) ? "" : value; + String htmlFormattedValue = (italicize) ? String.format("%s", formattedValue) : formattedValue; + label.setText(String.format("
%s: %s
", formattedKey, htmlFormattedValue)); + } + + @Override + protected void setMessage(boolean visible, String message) { + setValue(message, true); + } + + @Override + protected void setResults(String data) { + setValue(data, false); + } + } + + private static final long serialVersionUID = 1L; + private static final DecimalFormat INTEGER_SIZE_FORMAT = new DecimalFormat("#"); + private static final DecimalFormat COMMA_FORMATTER = new DecimalFormat("#,###"); + + // All file type categories. + private static final List>> FILE_MIME_TYPE_CATEGORIES = Arrays.asList( + Pair.of(Bundle.TypesPanel_fileMimeTypesChart_images_title(), FileTypeCategory.IMAGE.getMediaTypes()), + Pair.of(Bundle.TypesPanel_fileMimeTypesChart_videos_title(), FileTypeCategory.VIDEO.getMediaTypes()), + Pair.of(Bundle.TypesPanel_fileMimeTypesChart_audio_title(), FileTypeCategory.AUDIO.getMediaTypes()), + Pair.of(Bundle.TypesPanel_fileMimeTypesChart_documents_title(), FileTypeCategory.DOCUMENTS.getMediaTypes()), + Pair.of(Bundle.TypesPanel_fileMimeTypesChart_executables_title(), FileTypeCategory.EXECUTABLE.getMediaTypes()), + Pair.of(Bundle.TypesPanel_fileMimeTypesChart_unknown_title(), new HashSet<>(Arrays.asList("application/octet-stream"))) + ); + + 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()); + + private final PieChartPanel fileMimeTypesChart = new PieChartPanel(Bundle.TypesPanel_fileMimeTypesChart_title()); + + private final LoadableLabel allocatedLabel = new LoadableLabel(Bundle.TypesPanel_filesByCategoryTable_allocatedRow_title()); + private final LoadableLabel unallocatedLabel = new LoadableLabel(Bundle.TypesPanel_filesByCategoryTable_unallocatedRow_title()); + private final LoadableLabel slackLabel = new LoadableLabel(Bundle.TypesPanel_filesByCategoryTable_slackRow_title()); + private final LoadableLabel directoriesLabel = new LoadableLabel(Bundle.TypesPanel_filesByCategoryTable_directoryRow_title()); + + // all loadable components + private final List> loadables = Arrays.asList( + usageLabel, + osLabel, + sizeLabel, + fileMimeTypesChart, + allocatedLabel, + unallocatedLabel, + slackLabel, + directoriesLabel + ); + + // all of the means for obtaining data for the gui components. + private final List> dataFetchComponents = Arrays.asList( + // usage label worker + new DataFetchWorker.DataFetchComponents<>( + DataSourceDetailsSummary::getDataSourceType, + usageLabel::showDataFetchResult), + // os label worker + new DataFetchWorker.DataFetchComponents<>( + DataSourceDetailsSummary::getOperatingSystems, + osLabel::showDataFetchResult), + // 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<>( + this::getMimeTypeCategoriesModel, + fileMimeTypesChart::showDataFetchResult), + // allocated files worker + new DataFetchWorker.DataFetchComponents<>( + (dataSource) -> getStringOrZero(DataSourceCountsSummary.getCountOfAllocatedFiles(dataSource)), + allocatedLabel::showDataFetchResult), + // unallocated files worker + new DataFetchWorker.DataFetchComponents<>( + (dataSource) -> getStringOrZero(DataSourceCountsSummary.getCountOfUnallocatedFiles(dataSource)), + unallocatedLabel::showDataFetchResult), + // slack files worker + new DataFetchWorker.DataFetchComponents<>( + (dataSource) -> getStringOrZero(DataSourceCountsSummary.getCountOfSlackFiles(dataSource)), + slackLabel::showDataFetchResult), + // directories worker + new DataFetchWorker.DataFetchComponents<>( + (dataSource) -> getStringOrZero(DataSourceCountsSummary.getCountOfDirectories(dataSource)), + directoriesLabel::showDataFetchResult) + ); + + /** + * Main constructor. + */ + public TypesPanel() { + initComponents(); + } + + @Override + protected void onNewDataSource(DataSource dataSource) { + // if no data source is present or the case is not open, + // set results for tables to null. + if (dataSource == null || !Case.isCaseOpen()) { + this.dataFetchComponents.forEach((item) -> item.getResultHandler() + .accept(DataFetchResult.getSuccessResult(null))); + + } else { + // set tables to display loading screen + this.loadables.forEach((table) -> table.showDefaultLoadingMessage()); + + // create swing workers to run for each table + List> workers = dataFetchComponents + .stream() + .map((components) -> new DataFetchWorker<>(components, dataSource)) + .collect(Collectors.toList()); + + // submit swing workers to run + submit(workers); + } + } + + /** + * Gets all the data for the file type pie chart. + * + * @param dataSource The datasource. + * + * @return The pie chart items. + */ + private List getMimeTypeCategoriesModel(DataSource dataSource) { + if (dataSource == null) { + return null; + } + + // for each category of file types, get the counts of files + List> fileCategoryItems = FILE_MIME_TYPE_CATEGORIES + .stream() + .map((strCat) -> { + return Pair.of(strCat.getLeft(), + getLongOrZero(DataSourceMimeTypeSummary.getCountOfFilesForMimeTypes( + dataSource, strCat.getRight()))); + }) + .collect(Collectors.toList()); + + // get a count of all files with no mime type + Long noMimeTypeCount = getLongOrZero(DataSourceMimeTypeSummary.getCountOfFilesWithNoMimeType(dataSource)); + + // get the sum of all counts for the known categories + Long categoryTotalCount = getLongOrZero(fileCategoryItems.stream() + .collect(Collectors.summingLong((pair) -> pair.getValue()))); + + // get a count of all regular files + Long allRegularFiles = getLongOrZero(DataSourceMimeTypeSummary.getCountOfAllRegularFiles(dataSource)); + + // create entry for mime types in other category + fileCategoryItems.add(Pair.of(Bundle.TypesPanel_fileMimeTypesChart_other_title(), + allRegularFiles - (categoryTotalCount + noMimeTypeCount))); + + // create entry for not analyzed mime types category + fileCategoryItems.add(Pair.of(Bundle.TypesPanel_fileMimeTypesChart_notAnalyzed_title(), + noMimeTypeCount)); + + // create pie chart items to provide to pie chart + return fileCategoryItems.stream() + .filter(keyCount -> keyCount.getRight() != null && keyCount.getRight() > 0) + .map(keyCount -> new PieChartItem(keyCount.getLeft(), keyCount.getRight())) + .collect(Collectors.toList()); + } + + /** + * Returns the long value or zero if longVal is null. + * + * @param longVal The long value. + * + * @return The long value or 0 if provided value is null. + */ + private static long getLongOrZero(Long longVal) { + return longVal == null ? 0 : longVal; + } + + /** + * Returns string value of long with comma separators. If null returns a + * string of '0'. + * + * @param longVal The long value. + * + * @return The string value of the long. + */ + private static String getStringOrZero(Long longVal) { + return longVal == null ? "0" : COMMA_FORMATTER.format(longVal); + } + + /** + * 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 + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + javax.swing.JScrollPane scrollParent = new javax.swing.JScrollPane(); + javax.swing.JPanel contentParent = new javax.swing.JPanel(); + javax.swing.JPanel usagePanel = usageLabel; + javax.swing.JPanel osPanel = osLabel; + javax.swing.JPanel sizePanel = sizeLabel; + javax.swing.JPanel fileMimeTypesPanel = fileMimeTypesChart; + javax.swing.Box.Filler filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 5), new java.awt.Dimension(0, 5), new java.awt.Dimension(32767, 5)); + javax.swing.JPanel allocatedPanel = allocatedLabel; + javax.swing.JPanel unallocatedPanel = unallocatedLabel; + javax.swing.JPanel slackPanel = slackLabel; + javax.swing.JPanel directoriesPanel = directoriesLabel; + javax.swing.Box.Filler filler3 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767)); + + setLayout(new java.awt.BorderLayout()); + + contentParent.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10)); + contentParent.setMaximumSize(new java.awt.Dimension(32787, 32787)); + contentParent.setMinimumSize(new java.awt.Dimension(400, 490)); + contentParent.setLayout(new javax.swing.BoxLayout(contentParent, javax.swing.BoxLayout.PAGE_AXIS)); + + usagePanel.setAlignmentX(0.0F); + usagePanel.setMaximumSize(new java.awt.Dimension(32767, 20)); + usagePanel.setMinimumSize(new java.awt.Dimension(10, 20)); + usagePanel.setName(""); // NOI18N + usagePanel.setPreferredSize(new java.awt.Dimension(800, 20)); + contentParent.add(usagePanel); + + osPanel.setAlignmentX(0.0F); + osPanel.setMaximumSize(new java.awt.Dimension(32767, 20)); + osPanel.setMinimumSize(new java.awt.Dimension(10, 20)); + osPanel.setPreferredSize(new java.awt.Dimension(800, 20)); + contentParent.add(osPanel); + + sizePanel.setAlignmentX(0.0F); + sizePanel.setMaximumSize(new java.awt.Dimension(32767, 20)); + sizePanel.setMinimumSize(new java.awt.Dimension(10, 20)); + sizePanel.setPreferredSize(new java.awt.Dimension(800, 20)); + contentParent.add(sizePanel); + + fileMimeTypesPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(5, 5, 5, 5)); + fileMimeTypesPanel.setAlignmentX(0.0F); + fileMimeTypesPanel.setMaximumSize(new java.awt.Dimension(400, 300)); + fileMimeTypesPanel.setMinimumSize(new java.awt.Dimension(400, 300)); + fileMimeTypesPanel.setPreferredSize(new java.awt.Dimension(400, 300)); + contentParent.add(fileMimeTypesPanel); + contentParent.add(filler2); + + allocatedPanel.setAlignmentX(0.0F); + allocatedPanel.setMaximumSize(new java.awt.Dimension(32767, 16)); + allocatedPanel.setMinimumSize(new java.awt.Dimension(10, 16)); + allocatedPanel.setPreferredSize(new java.awt.Dimension(800, 16)); + contentParent.add(allocatedPanel); + + unallocatedPanel.setAlignmentX(0.0F); + unallocatedPanel.setMaximumSize(new java.awt.Dimension(32767, 16)); + unallocatedPanel.setMinimumSize(new java.awt.Dimension(10, 16)); + unallocatedPanel.setPreferredSize(new java.awt.Dimension(800, 16)); + contentParent.add(unallocatedPanel); + + slackPanel.setAlignmentX(0.0F); + slackPanel.setMaximumSize(new java.awt.Dimension(32767, 16)); + slackPanel.setMinimumSize(new java.awt.Dimension(10, 16)); + slackPanel.setPreferredSize(new java.awt.Dimension(800, 16)); + contentParent.add(slackPanel); + + directoriesPanel.setAlignmentX(0.0F); + directoriesPanel.setMaximumSize(new java.awt.Dimension(32767, 16)); + directoriesPanel.setMinimumSize(new java.awt.Dimension(10, 16)); + directoriesPanel.setPreferredSize(new java.awt.Dimension(800, 16)); + contentParent.add(directoriesPanel); + contentParent.add(filler3); + + scrollParent.setViewportView(contentParent); + + add(scrollParent, java.awt.BorderLayout.CENTER); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/AbstractLoadableComponent.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/AbstractLoadableComponent.java new file mode 100644 index 0000000000..74df7fd4d5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/AbstractLoadableComponent.java @@ -0,0 +1,170 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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; + +import java.util.Collection; +import java.util.logging.Level; +import javax.swing.JPanel; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.coreutils.Logger; + +/** + * Abstract class for common methods of a loadable component. + */ +@NbBundle.Messages({ + "AbstractLoadableComponent_loadingMessage_defaultText=Loading results...", + "AbstractLoadableComponent_errorMessage_defaultText=There was an error loading results.", + "AbstractLoadableComponent_noDataExists_defaultText=No data exists.",}) +public abstract class AbstractLoadableComponent extends JPanel implements LoadableComponent { + + private static final long serialVersionUID = 1L; + + /** + * The default loading message. + */ + public static final String DEFAULT_LOADING_MESSAGE = Bundle.AbstractLoadableComponent_loadingMessage_defaultText(); + + /** + * The default error message. + */ + public static final String DEFAULT_ERROR_MESSAGE = Bundle.AbstractLoadableComponent_errorMessage_defaultText(); + + /** + * The default 'no results' message. + */ + public static final String DEFAULT_NO_RESULTS_MESSAGE = Bundle.AbstractLoadableComponent_noDataExists_defaultText(); + + private static final Logger logger = Logger.getLogger(AbstractLoadableComponent.class.getName()); + + /** + * @return The default error message. + */ + public static String getDefaultErrorMessage() { + return DEFAULT_ERROR_MESSAGE; + } + + /** + * @return The default message for no results. + */ + public static String getDefaultNoResultsMessage() { + return DEFAULT_NO_RESULTS_MESSAGE; + } + + /** + * Clears the results from the underlying JTable and shows the provided + * message. + * + * @param message The message to be shown. + */ + public synchronized void showMessage(String message) { + setResults(null); + setMessage(true, message); + repaint(); + } + + /** + * Shows a default loading message on the table. This will clear any results + * in the table. + */ + public void showDefaultLoadingMessage() { + showMessage(DEFAULT_LOADING_MESSAGE); + } + + /** + * Shows the list as rows of data in the table. If overlay message will be + * cleared if present. + * + * @param data The data to be shown where each item represents a row of + * data. + */ + public synchronized void showResults(T data) { + setMessage(false, null); + setResults(data); + repaint(); + } + + /** + * Shows the data in a DataFetchResult. If there was an error during the + * operation, the errorMessage will be displayed. If the operation completed + * successfully and no data is present, noResultsMessage will be shown. + * Otherwise, the data will be shown as rows in the table. + * + * @param result The DataFetchResult. + * @param errorMessage The error message to be shown in the event of an + * error. + * @param noResultsMessage The message to be shown if there are no results + * but the operation completed successfully. + */ + public void showDataFetchResult(DataFetchResult result, String errorMessage, String noResultsMessage) { + if (result == null) { + logger.log(Level.SEVERE, "Null data fetch result received."); + return; + } + + switch (result.getResultType()) { + case SUCCESS: + T data = result.getData(); + if (data == null || (data instanceof Collection && ((Collection) data).isEmpty())) { + showMessage(noResultsMessage); + } else { + showResults(data); + } + break; + case ERROR: + // if there is an error, log accordingly, set result list to + // empty and display error message + logger.log(Level.WARNING, "An exception was caused while results were loaded.", result.getException()); + showMessage(errorMessage); + break; + default: + // an unknown loading state was specified. log accordingly. + logger.log(Level.SEVERE, "No known loading state was found in result."); + break; + } + } + + /** + * Shows the data in a DataFetchResult. If there was an error during the + * operation, the DEFAULT_ERROR_MESSAGE will be displayed. If the operation + * completed successfully and no data is present, DEFAULT_NO_RESULTS_MESSAGE + * will be shown. Otherwise, the data will be shown as rows in the table. + * + * @param result The DataFetchResult. + */ + public void showDataFetchResult(DataFetchResult result) { + showDataFetchResult(result, DEFAULT_ERROR_MESSAGE, DEFAULT_NO_RESULTS_MESSAGE); + } + + /** + * Sets the message and visibility of the message. Repaint does not need to + * be handled in this method. + * + * @param visible The visibility of the message. + * @param message The message to be displayed if visible. + */ + protected abstract void setMessage(boolean visible, String message); + + /** + * Sets the data to be shown in the JTable. Repaint does not need to be + * handled in this method. + * + * @param data The list of data objects to be shown. + */ + protected abstract void setResults(T data); +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BaseMessageOverlay.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BaseMessageOverlay.java new file mode 100644 index 0000000000..873fcb4d95 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/BaseMessageOverlay.java @@ -0,0 +1,87 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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; + +import java.awt.Graphics; +import javax.swing.JLabel; + +/** + * Base class for drawing a message overlay. Contains a paint method for + * painting a JLabel using a java.awt.Graphics object. + */ +public class BaseMessageOverlay { + + private final JLabel label; + private boolean visible = false; + + /** + * Main constructor for the Overlay. + */ + public BaseMessageOverlay() { + label = new JLabel(); + label.setHorizontalAlignment(JLabel.CENTER); + label.setVerticalAlignment(JLabel.CENTER); + label.setOpaque(false); + + } + + /** + * @return Whether or not this message overlay should be visible. + */ + public boolean isVisible() { + return visible; + } + + /** + * Sets this layer visible when painted. In order to be shown in UI, this + * component needs to be repainted. + * + * @param visible Whether or not it is visible. + */ + public void setVisible(boolean visible) { + this.visible = visible; + } + + /** + * Sets the message to be displayed in the child jlabel. + * + * @param message The message to be displayed. + */ + public void setMessage(String message) { + label.setText(String.format("
%s
", + message == null ? "" : message)); + } + + /** + * Paints the jlabel at full width and height with the graphics object. + * + * @param g The graphics object. + * @param width The width. + * @param height The height. + */ + public void paintOverlay(Graphics g, int width, int height) { + if (!visible) { + return; + } + + // paint the jlabel if visible. + label.setBounds(0, 0, width, height); + label.paint(g); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/Bundle.properties-MERGED index 332a884f05..491c3bfa56 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/Bundle.properties-MERGED @@ -1,3 +1,3 @@ -JTablePanel_errorMessage_defaultText=There was an error loading results. -JTablePanel_loadingMessage_defaultText=Loading results... -JTablePanel_noDataExists_defaultText=No data exists. +AbstractLoadableComponent_errorMessage_defaultText=There was an error loading results. +AbstractLoadableComponent_loadingMessage_defaultText=Loading results... +AbstractLoadableComponent_noDataExists_defaultText=No data exists. diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java index fe164db8df..f1c9ee92ff 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java @@ -23,31 +23,22 @@ import java.awt.Graphics; import java.util.Collections; import java.util.List; import java.util.function.Function; -import java.util.logging.Level; import java.util.stream.Collectors; import javax.swing.JComponent; -import javax.swing.JLabel; import javax.swing.JLayer; -import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.plaf.LayerUI; import javax.swing.table.DefaultTableColumnModel; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; -import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.CellModel; /** * A table that displays a list of items and also can display messages for * loading, load error, and not loaded. */ -@Messages({ - "JTablePanel_loadingMessage_defaultText=Loading results...", - "JTablePanel_errorMessage_defaultText=There was an error loading results.", - "JTablePanel_noDataExists_defaultText=No data exists.",}) -public class JTablePanel extends JPanel { +public class JTablePanel extends AbstractLoadableComponent> { /** * JTables don't allow displaying messages. So this LayerUI is used to @@ -58,27 +49,7 @@ public class JTablePanel extends JPanel { private static class Overlay extends LayerUI { private static final long serialVersionUID = 1L; - - private final JLabel label; - private boolean visible; - - /** - * Main constructor for the Overlay. - */ - Overlay() { - label = new JLabel(); - label.setHorizontalAlignment(JLabel.CENTER); - label.setVerticalAlignment(JLabel.CENTER); - label.setOpaque(false); - - } - - /** - * @return Whether or not this message overlay should be visible. - */ - boolean isVisible() { - return visible; - } + private final BaseMessageOverlay overlayDelegate = new BaseMessageOverlay(); /** * Sets this layer visible when painted. In order to be shown in UI, @@ -87,7 +58,7 @@ public class JTablePanel extends JPanel { * @param visible Whether or not it is visible. */ void setVisible(boolean visible) { - this.visible = visible; + overlayDelegate.setVisible(visible); } /** @@ -96,25 +67,13 @@ public class JTablePanel extends JPanel { * @param message The message to be displayed. */ void setMessage(String message) { - label.setText(String.format("
%s
", - message == null ? "" : message)); + overlayDelegate.setMessage(message); } @Override public void paint(Graphics g, JComponent c) { - // Paint the underlying view. super.paint(g, c); - - if (!visible) { - return; - } - - int w = c.getWidth(); - int h = c.getHeight(); - - // paint the jlabel if visible. - label.setBounds(0, 0, w, h); - label.paint(g); + overlayDelegate.paintOverlay(g, c.getWidth(), c.getHeight()); } } @@ -179,12 +138,6 @@ public class JTablePanel extends JPanel { private static final long serialVersionUID = 1L; - private static final Logger logger = Logger.getLogger(JTablePanel.class.getName()); - - private static final String DEFAULT_LOADING_MESSAGE = Bundle.JTablePanel_loadingMessage_defaultText(); - private static final String DEFAULT_ERROR_MESSAGE = Bundle.JTablePanel_errorMessage_defaultText(); - private static final String DEFAULT_NO_RESULTS_MESSAGE = Bundle.JTablePanel_noDataExists_defaultText(); - private static final CellModelTableCellRenderer DEFAULT_CELL_RENDERER = new CellModelTableCellRenderer(); /** @@ -231,7 +184,7 @@ public class JTablePanel extends JPanel { .map((colModel) -> colModel.getCellRenderer()) .collect(Collectors.toList()); - return new DefaultListTableModel<>(columnRenderers); + return new DefaultListTableModel(columnRenderers); } /** @@ -248,20 +201,6 @@ public class JTablePanel extends JPanel { return resultTable.setColumnModel(getTableColumnModel(columns)); } - /** - * @return The default error message. - */ - public static String getDefaultErrorMessage() { - return DEFAULT_ERROR_MESSAGE; - } - - /** - * @return The default message for no results. - */ - public static String getDefaultNoResultsMessage() { - return DEFAULT_NO_RESULTS_MESSAGE; - } - private JScrollPane tableScrollPane; private Overlay overlayLayer; private ListTableModel tableModel; @@ -285,7 +224,7 @@ public class JTablePanel extends JPanel { } /** - * Set the table model. This method must be called prior to calling + * Set the table model. This method must be called prior to calling * setResultList. * * @param tableModel @@ -318,18 +257,8 @@ public class JTablePanel extends JPanel { return this; } - /** - * Sets the data to be shown in the JTable. Repaint is not handled in this - * method and should be handled separately. - * - * @param data The list of data objects to be shown. - */ - private void setResultList(List data) { - - if(tableModel == null) { - throw new IllegalStateException("ListTableModel has not be initialized"); - } - + @Override + protected void setResults(List data) { // set the list of data to be shown as either the data or an empty list // on null. List dataToSet = (data == null) ? Collections.emptyList() : data; @@ -341,102 +270,12 @@ public class JTablePanel extends JPanel { this.tableModel.setDataRows(dataToSet); } - /** - * Sets the message and visibility of the overlay. Repaint is not handled in - * this method and should be handled separately. - * - * @param visible The visibility of the overlay. - * @param message The message in the overlay. - */ - private void setOverlay(boolean visible, String message) { + @Override + protected void setMessage(boolean visible, String message) { this.overlayLayer.setVisible(visible); this.overlayLayer.setMessage(message); } - /** - * Clears the results from the underlying JTable and shows the provided - * message. - * - * @param message The message to be shown. - */ - public synchronized void showMessage(String message) { - setResultList(null); - setOverlay(true, message); - repaint(); - } - - /** - * Shows a default loading message on the table. This will clear any results - * in the table. - */ - public void showDefaultLoadingMessage() { - showMessage(DEFAULT_LOADING_MESSAGE); - } - - /** - * Shows the list as rows of data in the table. If overlay message will be - * cleared if present. - * - * @param data The data to be shown where each item represents a row of - * data. - */ - public synchronized void showResults(List data) { - setOverlay(false, null); - setResultList(data); - repaint(); - } - - /** - * Shows the data in a DataFetchResult. If there was an error during the - * operation, the errorMessage will be displayed. If the operation completed - * successfully and no data is present, noResultsMessage will be shown. - * Otherwise, the data will be shown as rows in the table. - * - * @param result The DataFetchResult. - * @param errorMessage The error message to be shown in the event of an - * error. - * @param noResultsMessage The message to be shown if there are no results - * but the operation completed successfully. - */ - public void showDataFetchResult(DataFetchResult> result, String errorMessage, String noResultsMessage) { - if (result == null) { - logger.log(Level.SEVERE, "Null data processor result received."); - return; - } - - switch (result.getResultType()) { - case SUCCESS: - if (result.getData() == null || result.getData().isEmpty()) { - showMessage(noResultsMessage); - } else { - showResults(result.getData()); - } - break; - case ERROR: - // if there is an error, log accordingly, set result list to - // empty and display error message - logger.log(Level.WARNING, "An exception was caused while results were loaded.", result.getException()); - showMessage(errorMessage); - break; - default: - // an unknown loading state was specified. log accordingly. - logger.log(Level.SEVERE, "No known loading state was found in result."); - break; - } - } - - /** - * Shows the data in a DataFetchResult. If there was an error during the - * operation, the DEFAULT_ERROR_MESSAGE will be displayed. If the operation - * completed successfully and no data is present, DEFAULT_NO_RESULTS_MESSAGE - * will be shown. Otherwise, the data will be shown as rows in the table. - * - * @param result The DataFetchResult. - */ - public void showDataFetchResult(DataFetchResult> result) { - showDataFetchResult(result, DEFAULT_ERROR_MESSAGE, DEFAULT_NO_RESULTS_MESSAGE); - } - /** * Initialize the gui components. */ diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/LoadableComponent.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/LoadableComponent.java new file mode 100644 index 0000000000..652cf3bcd1 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/LoadableComponent.java @@ -0,0 +1,73 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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; + +/** + * Interface for a loadable component that can show messages, results, or a + * DataFetchResult. + */ +public interface LoadableComponent { + + /** + * Clears the results from the underlying JTable and shows the provided + * message. + * + * @param message The message to be shown. + */ + void showMessage(String message); + + /** + * Shows a default loading message on the table. This will clear any results + * in the table. + */ + void showDefaultLoadingMessage(); + + /** + * Shows the list as rows of data in the table. If overlay message will be + * cleared if present. + * + * @param data The data to be shown where each item represents a row of + * data. + */ + void showResults(T data); + + /** + * Shows the data in a DataFetchResult. If there was an error during the + * operation, the errorMessage will be displayed. If the operation completed + * successfully and no data is present, noResultsMessage will be shown. + * Otherwise, the data will be shown as rows in the table. + * + * @param result The DataFetchResult. + * @param errorMessage The error message to be shown in the event of an + * error. + * @param noResultsMessage The message to be shown if there are no results + * but the operation completed successfully. + */ + void showDataFetchResult(DataFetchResult result, String errorMessage, String noResultsMessage); + + /** + * Shows the data in a DataFetchResult. If there was an error during the + * operation, the DEFAULT_ERROR_MESSAGE will be displayed. If the operation + * completed successfully and no data is present, DEFAULT_NO_RESULTS_MESSAGE + * will be shown. Otherwise, the data will be shown as rows in the table. + * + * @param result The DataFetchResult. + */ + void showDataFetchResult(DataFetchResult result); +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartPanel.java new file mode 100644 index 0000000000..8d6eab4a9b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/PieChartPanel.java @@ -0,0 +1,201 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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; + +import java.awt.BorderLayout; +import java.awt.Font; +import java.awt.Graphics2D; +import java.text.DecimalFormat; +import java.util.List; +import javax.swing.JLabel; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.labels.PieSectionLabelGenerator; +import org.jfree.chart.labels.StandardPieSectionLabelGenerator; +import org.jfree.chart.panel.AbstractOverlay; +import org.jfree.chart.panel.Overlay; +import org.jfree.chart.plot.PiePlot; +import org.jfree.data.general.DefaultPieDataset; + +/** + * A pie chart panel. + */ +public class PieChartPanel extends AbstractLoadableComponent> { + + /** + * An individual pie chart slice in the pie chart. + */ + public static class PieChartItem { + + private final String label; + private final double value; + + /** + * Main constructor. + * + * @param label The label for this pie slice. + * @param value The value for this item. + */ + public PieChartItem(String label, double value) { + this.label = label; + this.value = value; + } + + /** + * @return The label for this item. + */ + public String getLabel() { + return label; + } + + /** + * @return The value for this item. + */ + public double getValue() { + return value; + } + } + + /** + * A JFreeChart message overlay that can show a message for the purposes of + * the LoadableComponent. + */ + private static class MessageOverlay extends AbstractOverlay implements Overlay { + + private static final long serialVersionUID = 1L; + private final BaseMessageOverlay overlay = new BaseMessageOverlay(); + + /** + * Sets this layer visible when painted. In order to be shown in UI, + * this component needs to be repainted. + * + * @param visible Whether or not it is visible. + */ + void setVisible(boolean visible) { + overlay.setVisible(visible); + } + + /** + * Sets the message to be displayed in the child jlabel. + * + * @param message The message to be displayed. + */ + void setMessage(String message) { + overlay.setMessage(message); + } + + @Override + public void paintOverlay(Graphics2D gd, ChartPanel cp) { + overlay.paintOverlay(gd, cp.getWidth(), cp.getHeight()); + } + + } + + private static final long serialVersionUID = 1L; + + private static final Font DEFAULT_FONT = new JLabel().getFont(); + private static final Font DEFAULT_HEADER_FONT = new Font(DEFAULT_FONT.getName(), DEFAULT_FONT.getStyle(), (int) (DEFAULT_FONT.getSize() * 1.5)); + private static final PieSectionLabelGenerator DEFAULT_LABEL_GENERATOR + = new StandardPieSectionLabelGenerator( + "{0}: {1} ({2})", new DecimalFormat("#,###"), new DecimalFormat("0.0%")); + + private final MessageOverlay overlay = new MessageOverlay(); + private final DefaultPieDataset dataset = new DefaultPieDataset(); + private final JFreeChart chart; + + /** + * Main constructor. + */ + public PieChartPanel() { + this(null); + } + + /** + * Main constructor for the pie chart. + * + * @param title The title for this pie chart. + */ + public PieChartPanel(String title) { + // Create chart + this.chart = ChartFactory.createPieChart( + title, + dataset, + true, + true, + false); + + chart.setBackgroundPaint(null); + chart.getLegend().setItemFont(DEFAULT_FONT); + chart.getTitle().setFont(DEFAULT_HEADER_FONT); + + // don't show a legend by default + chart.removeLegend(); + + PiePlot plot = ((PiePlot) chart.getPlot()); + + plot.setLabelGenerator(DEFAULT_LABEL_GENERATOR); + plot.setLabelFont(DEFAULT_FONT); + plot.setBackgroundPaint(null); + plot.setOutlinePaint(null); + + // Create Panel + ChartPanel panel = new ChartPanel(chart); + panel.addOverlay(overlay); + this.setLayout(new BorderLayout()); + this.add(panel, BorderLayout.CENTER); + } + + /** + * @return The title for this chart if one exists. + */ + public String getTitle() { + return (this.chart == null || this.chart.getTitle() == null) + ? null + : this.chart.getTitle().getText(); + } + + /** + * Sets the title for this pie chart. + * + * @param title The title. + * + * @return As a utility, returns this. + */ + public PieChartPanel setTitle(String title) { + this.chart.getTitle().setText(title); + return this; + } + + @Override + protected void setMessage(boolean visible, String message) { + this.overlay.setVisible(visible); + this.overlay.setMessage(message); + } + + @Override + protected void setResults(List data) { + this.dataset.clear(); + if (data != null) { + for (PieChartPanel.PieChartItem slice : data) { + this.dataset.setValue(slice.getLabel(), slice.getValue()); + } + } + } +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobLogger.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobLogger.java index 5bc3f46fac..4a7eb4d975 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobLogger.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestJobLogger.java @@ -70,7 +70,7 @@ final class AutoIngestJobLogger { */ INFO, /** - * Qualifies a log message about an unexpected event or condtion during + * Qualifies a log message about an unexpected event or condition during * automated ingest processing. */ WARNING, @@ -208,6 +208,18 @@ final class AutoIngestJobLogger { void logDataSourceProcessorSelected(String dsp) throws AutoIngestJobLoggerException, InterruptedException { log(MessageCategory.INFO, "Using data source processor: " + dsp); } + + /** + * Log that a data source is being skipped. + * + * @param dataSourceName The name of the data source + * + * @throws AutoIngestJobLogger.AutoIngestJobLoggerException + * @throws InterruptedException + */ + void logSkippingDataSource(String dataSourceName) throws AutoIngestJobLoggerException, InterruptedException { + log(MessageCategory.INFO, "File type can not currently be processed"); + } /** * Logs the failure of the selected data source processor. diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index 7c817e7739..a6ece45667 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -1040,7 +1040,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen } } - + /** * A task that submits an input directory scan task to the input directory * scan task executor. @@ -2440,6 +2440,15 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen currentJob.setProcessingStage(AutoIngestJob.Stage.COMPLETED, Date.from(Instant.now())); return; } + + if (SupportedDataSources.shouldSkipFile(dataSource.getPath().toString())) { + Manifest manifest = currentJob.getManifest(); + AutoIngestJobLogger jobLogger = new AutoIngestJobLogger(manifest.getFilePath(), manifest.getDataSourceFileName(), currentJob.getCaseDirectoryPath()); + jobLogger.logSkippingDataSource(dataSource.getPath().toString()); + sysLogger.log(Level.INFO, "Skipping data source that can not be processed ({0})", dataSource.getPath().toString()); + currentJob.setProcessingStage(AutoIngestJob.Stage.COMPLETED, Date.from(Instant.now())); + return; + } if (currentJob.isCanceled() || jobProcessingTaskFuture.isCancelled()) { return; diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/SupportedDataSources.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/SupportedDataSources.java new file mode 100644 index 0000000000..9df1587215 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/SupportedDataSources.java @@ -0,0 +1,53 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 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.experimental.autoingest; + +import java.util.Arrays; +import java.util.List; +import org.apache.commons.io.FilenameUtils; + +/** + * Utility class for checking whether a data source/file should be processed + * in an automated setting. The goal is to not spend time analyzing large + * files that Autopsy can not handle yet. + */ +public final class SupportedDataSources { + + private static final List UNSUPPORTED_EXTENSIONS = Arrays.asList("xry", "dar"); + + /** + * Check whether a file should be added to a case, either as a data source or part of a + * logical file set. + * + * @param fileName The name of the file. + * + * @return true if the file is currently unsupported and should be skipped, false otherwise. + */ + public static boolean shouldSkipFile(String fileName) { + String ext = FilenameUtils.getExtension(fileName); + if (ext == null) { + return false; + } + return UNSUPPORTED_EXTENSIONS.contains(ext.toLowerCase()); + } + + private SupportedDataSources() { + // Static class + } +}